Reputation: 4527
For the following union (without AAA or BBB):
union AAA bpf_attr {
struct { /* Used by BPF_MAP_CREATE */
uint32_t map_type;
uint32_t key_size; /* size of key in bytes */
uint32_t value_size; /* size of value in bytes */
uint32_t max_entries; /* maximum number of entries
in a map */
};
struct { /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY commands */
uint32_t map_fd;
uint64_t alignas(8) key;
union {
uint64_t alignas(8) value;
uint64_t alignas(8) next_key;
};
uint64_t flags;
};
struct { /* Used by BPF_PROG_LOAD */
uint32_t prog_type;
uint32_t insn_cnt;
uint64_t alignas(8) insns; /* 'const struct bpf_insn *' */
uint64_t alignas(8) license; /* 'const char *' */
uint32_t log_level; /* verbosity level of verifier */
uint32_t log_size; /* size of user buffer */
uint64_t alignas(8) log_buf; /* user supplied 'char *'
buffer */
uint32_t kern_version;
/* checked when prog_type=kprobe
(since Linux 4.1) */
};
} BBB;
Is there any difference if I put _Alignas(8)
at AAA
or BBB
(or any other position if valid)? The standard doesn't seem to specify. Is there any difference between any of the former or doing the same with [[gnu::aligned(8)]]
? GCC does specify some difference, but I don't understand the text:
Where attribute specifiers follow the closing brace, they are considered to relate to the structure, union or enumerated type defined, not to any enclosing declaration the type specifier appears in, and the type defined is not complete until after the attribute specifiers.
EDIT:
After some tests, it seems that (but am not sure at all):
union alignas(N) u {
...
} v;
The above is not legal.
union u {
...
} alignas(N) v;
The above is only legal because there's a variable declaration. It should be equivalent to:
union u {
...
};
union u alignas(N) v;
union [[gnu::aligned(8)]] u {
...
};
The above seems to be legal, without the need of a variable declaration.
union u {
...
} [[gnu::aligned(8)]] v;
The above seems to be ignored (with a warning), but I don't understand why; the text says that it's allowed, even if it's not the preferred version.
Yet another weird thing:
The last form doesn't trigger a warning if instead of C2x syntax you use __attribute__((aligned(8)))
, which by the way is the form that Linux uses in its source code.
I could test more combinations, but it's an exponential thing, and a theoretical answer seems more appropriate.
Upvotes: 0
Views: 699
Reputation: 4527
I don't know what the theoretical answer is according to the standard and the GNU documentation, as they're not very detailed, but here's an extensive experimental answer.
For my experiment I wrote the following file:
align.c
:
#include <stdalign.h>
#include <stdio.h>
struct s {
int a;
long b;
char c;
} s_, s__;
__attribute__((aligned(16))) struct t {
int a;
long b;
char c;
} t_, t__;
alignas(16) struct tt {
int a;
long b;
char c;
} tt_, tt__;
[[gnu::aligned(16)]] struct ttt {
int a;
long b;
char c;
} ttt_, ttt__;
struct __attribute__((aligned(16))) u {
int a;
long b;
char c;
} u_, u__;
/* error: expected ‘{’ before ‘_Alignas’
struct alignas(16) uu {
int a;
long b;
char c;
} uu_, uu__;
*/
struct [[gnu::aligned(16)]] uuu {
int a;
long b;
char c;
} uuu_, uuu__;
/* error: expected identifier or ‘(’ before ‘{’ token
struct v __attribute__((aligned(16))) {
int a;
long b;
char c;
} v_, v_;
*/
/* error: expected identifier or ‘(’ before ‘{’ token
struct vv alignas(16) {
int a;
long b;
char c;
} vv_, vv__;
*/
// warning: ignoring attributes applied to ‘struct vvv’ after definition [-Wattributes]
/* error: expected identifier or ‘(’ before ‘{’ token
struct vvv [[gnu::aligned(16)]] {
int a;
long b;
char c;
} vvv_, vvv__;
*/
struct w {
int a;
long b;
char c;
} __attribute__((aligned(16))) w_, w__;
struct ww {
int a;
long b;
char c;
} alignas(16) ww_, ww__;
// warning: ignoring attributes applied to ‘struct www’ after definition [-Wattributes]
struct www {
int a;
long b;
char c;
} [[gnu::aligned(16)]] www_, www__;
struct x {
int a;
long b;
char c;
} x_ __attribute__((aligned(16))), x__;
/* error: expected ‘;’ before ‘_Alignas’
struct xx {
int a;
long b;
char c;
} xx_ alignas(16), xx__;
*/
struct xxx {
int a;
long b;
char c;
} xxx_ [[gnu::aligned(16)]], xxx__;
struct y {
int a;
long b;
char c;
} y_, __attribute__((aligned(16))) y__;
/* error: expected identifier or ‘(’ before ‘_Alignas’
struct yy {
int a;
long b;
char c;
} yy_, alignas(16) yy__;
*/
/* error: expected identifier or ‘(’ before ‘[’ token
struct yyy {
int a;
long b;
char c;
} yyy_, [[gnu::aligned(16)]] yyy__;
*/
struct z {
int a;
long b;
char c;
} z_, z__ __attribute__((aligned(16)));
/* error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘_Alignas’
struct zz {
int a;
long b;
char c;
} zz_, zz__ alignas(16);
*/
struct zzz {
int a;
long b;
char c;
} zzz_, zzz__ [[gnu::aligned(16)]];
int main(void)
{
printf("s: %2zu; s_: %2zu; s__: %2zu\n\n", alignof(struct s), alignof(s_), alignof(s__));
printf("t: %2zu; t_: %2zu; t__: %2zu\n", alignof(struct t), alignof(t_), alignof(t__));
printf("tt: %2zu; tt_: %2zu; tt__: %2zu\n", alignof(struct tt), alignof(tt_), alignof(tt__));
printf("ttt: %2zu; ttt_: %2zu; ttt__: %2zu\n\n", alignof(struct ttt), alignof(ttt_), alignof(ttt__));
printf("u: %2zu; u_: %2zu; u__: %2zu\n", alignof(struct u), alignof(u_), alignof(u__));
puts("uu: -");// printf("uu: %2zu; uu_: %2zu; uu__: %2zu\n", alignof(struct uu), alignof(uu_), alignof(uu__));
printf("uuu: %2zu; uuu_: %2zu; uuu__: %2zu\n\n", alignof(struct uuu), alignof(uuu_), alignof(uuu__));
puts("v: -");// printf("v: %2zu; v_: %2zu; v__: %2zu\n", alignof(struct v), alignof(v_), alignof(v__));
puts("vv: -");// printf("vv: %2zu; vv_: %2zu; vv__: %2zu\n", alignof(struct vv), alignof(vv_), alignof(vv__));
puts("vvv: -\n");// printf("vvv: %2zu; vvv_: %2zu; vvv__: %2zu\n", alignof(struct vvv), alignof(vvv_), alignof(vvv__));
printf("w: %2zu; w_: %2zu; w__: %2zu\n", alignof(struct w), alignof(w_), alignof(w__));
printf("ww: %2zu; ww_: %2zu; ww__: %2zu\n", alignof(struct ww), alignof(ww_), alignof(ww__));
printf("www: %2zu; www_: %2zu; www__: %2zu\n\n", alignof(struct www), alignof(www_), alignof(www__));
printf("x: %2zu; x_: %2zu; x__: %2zu\n", alignof(struct x), alignof(x_), alignof(x__));
puts("xx: -");// printf("xx: %2zu; xx_: %2zu; xx__: %2zu\n", alignof(struct xx), alignof(xx_), alignof(xx__));
printf("xxx: %2zu; xxx_: %2zu; xxx__: %2zu\n\n", alignof(struct xxx), alignof(xxx_), alignof(xxx__));
printf("y: %2zu; y_: %2zu; y__: %2zu\n", alignof(struct y), alignof(y_), alignof(y__));
puts("yy: -");// printf("yy: %2zu; yy_: %2zu; yy__: %2zu\n", alignof(struct yy), alignof(yy_), alignof(yy__));
puts("yyy: -\n");// printf("yyy: %2zu; yyy_: %2zu; yyy__: %2zu\n\n", alignof(struct yyy), alignof(yyy_), alignof(yyy__));
printf("z: %2zu; z_: %2zu; z__: %2zu\n", alignof(struct z), alignof(z_), alignof(z__));
puts("zz: -");// printf("zz: %2zu; zz_: %2zu; zz__: %2zu\n", alignof(struct zz), alignof(zz_), alignof(zz__));
printf("zzz: %2zu; zzz_: %2zu; zzz__: %2zu\n", alignof(struct zzz), alignof(zzz_), alignof(zzz__));
return 0;
}
cc -Wall -Wextra align.c -o align
gives the warnings and errors I commented above. I had to comment out some code to make it compile.
$ ./align
s: 8; s_: 8; s__: 8
t: 8; t_: 16; t__: 16
tt: 8; tt_: 16; tt__: 16
ttt: 8; ttt_: 16; ttt__: 16
u: 16; u_: 16; u__: 16
uu: -
uuu: 16; uuu_: 16; uuu__: 16
v: -
vv: -
vvv: -
w: 16; w_: 16; w__: 16
ww: 8; ww_: 16; ww__: 16
www: 8; www_: 16; www__: 16
x: 8; x_: 16; x__: 8
xx: -
xxx: 8; xxx_: 16; xxx__: 8
y: 8; y_: 8; y__: 16
yy: -
yyy: -
z: 8; z_: 8; z__: 16
zz: -
zzz: 8; zzz_: 8; zzz__: 16
Conclusion:
If the alignment attribute goes before the struct
keyword, it applies to any variables declared, but not to the type itself.
If the attribute goes between the struct
keyword and the struct
tag, it applies to both the type and any variables declared. This is invalid for C11 alignas()
, which doesn't compile.
The attribute can't go just before {
.
If the attribute goes just after the }
, if works as if if were before the struct
keyword (applies only to any variables declared), EXCEPT for old GNU syntax, which applies to the type itself too.
If the attribute goes after a declared variable, it applies to it only. This is invalid for C11 alignas()
, which doesn't compile.
If the attribute goes before one declared variable that is not the first one (there is at least a comma between }
, and the attribute), it applies to the variable next to it only. This is only valid for old GNU syntax; others don't compile.
Upvotes: 1