Using Compound Literal + type-punning to initialize static const array

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

Consider the following code:

struct reg_layout { unsigned DF:4, :4, DI:8; };
union div_ctrl { unsigned val; struct reg_layout bits; };
#define FILL_LAYOUT(I,F) (union div_ctrl) {.bits = { .DF=F, .DI=I }}
#define GET_REG_VAL(I,F) (FILL_LAYOUT(I,F)).val

The union provides a low-level view (the raw "val") and a somewhat
higher-level view (the "bits" bit-fields) where we can focus on the
interesting parts, namely I and F, without cluttering the expression
with masks, shifts, etc.

(Please note: this is in arch-specific code, portability is not required.)

I'd like to initialize a static const array using .bits, and later only
refer to .val

static const unsigned arr[] = {
  GET_REG_VAL(1,8),
  GET_REG_VAL(2,0),
};

I was hoping the GET_REG_VAL function-macro would be equivalent to
#define GET_REG_VAL_equiv(I,F) ((I<<8) | F)

But GCC (version 4.8.2) refuses my code :-(

$ gcc -std=gnu99 -O3 -S test.c
test.c:3:33: error: initializer element is not constant
 #define FILL_LAYOUT(I,F) (union div_ctrl) {.bits = { .DF=F, .DI=I }}
                                 ^
test.c:4:27: note: in expansion of macro ‘FILL_LAYOUT’
 #define GET_REG_VAL(I,F) (FILL_LAYOUT(I,F)).val
                           ^
test.c:6:3: note: in expansion of macro ‘GET_REG_VAL’
   GET_REG_VAL(1,8),
   ^
test.c:3:33: error: (near initialization for ‘arr[0]’)
 #define FILL_LAYOUT(I,F) (union div_ctrl) {.bits = { .DF=F, .DI=I }}
                                 ^
test.c:4:27: note: in expansion of macro ‘FILL_LAYOUT’
 #define GET_REG_VAL(I,F) (FILL_LAYOUT(I,F)).val
                           ^
test.c:6:3: note: in expansion of macro ‘GET_REG_VAL’
   GET_REG_VAL(1,8),
   ^

According to the documentation,
https://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html

As a GNU extension, GCC allows initialization of objects with static
storage duration by compound literals (which is not possible in ISO
C99, because the initializer is not a constant). It is handled as if
the object is initialized only with the bracket enclosed list if the
types of the compound literal and the object match. The initializer
list of the compound literal must be constant. If the object being
initialized has array type of unknown size, the size is determined by
compound literal size.

I was under the impression that I should be able to use my syntax
in GNU89 or GNU99 mode. But I don't think I understand everything
in the above paragraph.

Do you see a way to do what I want? Can you explain why it fails?



Although the wording in the documentation seems fairly old, I did
run across a very recent discussion that looks relevant:

Enable initializing statics with COMPOUND_LITERAL_EXPR in C99 (PR c/63567)
https://gcc.gnu.org/ml/gcc-patches/2014-10/msg01790.html
[CCing the participants of this discussion]

Omitting the array to make a simpler test case(*) produces the same
diagnostic.
(*) static const unsigned u = GET_REG_VAL(1,8);



For the record, while playing around with this problem, my colleague
came up with this (monstrous) code snippet which ICEs gcc-4.8.2

union foo { struct { unsigned baz: 3, bar: 1; } bits; int value; };
int * const adds[] = {&(((union foo){.bits={ .bar = 0x1 }}).value)};
int main() { return *(adds[0]); }

$ gcc -std=gnu99 -O3 -S test.c
test.c: In function ‘main’:
test.c:3:5: internal compiler error: in expand_expr_real_1, at expr.c:10540
 int main() { return *(adds[0]); }
     ^
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-4.8/README.Bugs> for instructions.
Preprocessed source stored into /tmp/cc2RJY0y.out file, please attach this to your bugreport.

// /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -imultiarch x86_64-linux-gnu test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -O3 -std=gnu99 -o - -fstack-protector -Wformat -Wformat-security -frandom-seed=0
# 1 "test.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
union foo { struct { unsigned baz: 3, bar: 1; } bits; int value; };
int * const adds[] = {&(((union foo){.bits={ .bar = 0x1 }}).value)};
int main() { return *(adds[0]); }


I can't believe you've read this far!

Regards.




[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux