__attribute__((warn_if_not_aligned(N))) issues a warning if the field in a struct or union is not aligned to N:
typedef unsigned long long __u64 __attribute__((aligned(4),warn_if_not_aligned(8))); struct foo { int i1; int i2; __u64 x; }; __u64 is aligned to 4 bytes. But inside struct foo, __u64 should be aligned at 8 bytes. It is used to define struct foo in such a way that struct foo has the same layout and x has the same alignment when __u64 is aligned at either 4 or 8 bytes. Since struct foo is normally aligned to 4 bytes, a warning will be issued: warning: alignment 4 of ‘struct foo’ is less than 8 Align struct foo to 8 bytes: struct foo { int i1; int i2; __u64 x; } __attribute__((aligned(8))); silences the warning. It also warns the field with misaligned offset: struct foo { int i1; int i2; int i3; __u64 x; } __attribute__((aligned(8))); warning: ‘x’ offset 12 in ‘struct foo’ isn't aligned to 8 The same warning is also issued without the warn_if_not_aligned attribute for the field with explicitly specified alignment in a packed struct or union: struct __attribute__ ((aligned (8))) S8 { char a[8]; }; struct __attribute__ ((packed)) S { struct S8 s8; }; warning: alignment 1 of ‘struct S’ is less than 8 gcc/ PR c/53037 * c-decl.c (finish_enum): Also copy TYPE_WARN_IF_NOT_ALIGN. * print-tree.c (print_node): Support TYPE_WARN_IF_NOT_ALIGN. * stor-layout.c (handle_warn_if_not_align): New. (place_union_field): Call handle_warn_if_not_align. (place_field): Call handle_warn_if_not_align. Copy TYPE_WARN_IF_NOT_ALIGN. (finish_builtin_struct): Copy TYPE_WARN_IF_NOT_ALIGN. (layout_type): Likewise. * tree.c (build_range_type_1): Likewise. * tree-core.h (tree_type_common): Add warn_if_not_align. Set spare to 18. * tree.h (TYPE_WARN_IF_NOT_ALIGN): New. (SET_TYPE_WARN_IF_NOT_ALIGN): Likewise. * c-family/c-attribs.c (handle_warn_if_not_aligned_attribute): New. (c_common_attribute_table): Add warn_if_not_aligned. (handle_aligned_attribute): Renamed to ... (common_handle_aligned_attribute): Remove argument, name, and add argument, warn_if_not_aligned. Handle warn_if_not_aligned. (handle_aligned_attribute): New. * c/c-decl.c (finish_enum): Copy TYPE_WARN_IF_NOT_ALIGN. gcc/testsuite/ PR c/53037 * gcc.dg/pr53037-1.c: New test. --- gcc/c-family/c-attribs.c | 55 +++++++++++++++++++++++++++++++++---- gcc/c/c-decl.c | 2 ++ gcc/print-tree.c | 6 ++-- gcc/stor-layout.c | 45 ++++++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/pr53037-1.c | 59 ++++++++++++++++++++++++++++++++++++++++ gcc/tree-core.h | 3 +- gcc/tree.c | 1 + gcc/tree.h | 10 +++++++ 8 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr53037-1.c diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index 695c58c..a76f9f7 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -85,6 +85,8 @@ static tree handle_destructor_attribute (tree *, tree, tree, int, bool *); static tree handle_mode_attribute (tree *, tree, tree, int, bool *); static tree handle_section_attribute (tree *, tree, tree, int, bool *); static tree handle_aligned_attribute (tree *, tree, tree, int, bool *); +static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, + int, bool *); static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); @@ -208,6 +210,9 @@ const struct attribute_spec c_common_attribute_table[] = handle_section_attribute, false }, { "aligned", 0, 1, false, false, false, handle_aligned_attribute, false }, + { "warn_if_not_aligned", 0, 1, false, false, false, + handle_warn_if_not_aligned_attribute, + false }, { "weak", 0, 0, true, false, false, handle_weak_attribute, false }, { "noplt", 0, 0, true, false, false, @@ -1558,12 +1563,13 @@ check_cxx_fundamental_alignment_constraints (tree node, return !alignment_too_large_p; } -/* Handle a "aligned" attribute; arguments as in - struct attribute_spec.handler. */ +/* Common codes shared by handle_warn_if_not_aligned_attribute and + handle_aligned_attribute. */ static tree -handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args, - int flags, bool *no_add_attrs) +common_handle_aligned_attribute (tree *node, tree args, int flags, + bool *no_add_attrs, + bool warn_if_not_aligned) { tree decl = NULL_TREE; tree *type = NULL; @@ -1612,8 +1618,16 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args, else *type = build_variant_type_copy (*type); - SET_TYPE_ALIGN (*type, (1U << i) * BITS_PER_UNIT); - TYPE_USER_ALIGN (*type) = 1; + if (warn_if_not_aligned) + { + SET_TYPE_WARN_IF_NOT_ALIGN (*type, (1U << i) * BITS_PER_UNIT); + warn_if_not_aligned = false; + } + else + { + SET_TYPE_ALIGN (*type, (1U << i) * BITS_PER_UNIT); + TYPE_USER_ALIGN (*type) = 1; + } } else if (! VAR_OR_FUNCTION_DECL_P (decl) && TREE_CODE (decl) != FIELD_DECL) @@ -1650,9 +1664,38 @@ handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args, DECL_USER_ALIGN (decl) = 1; } + if (warn_if_not_aligned) + { + error ("warn_if_not_aligned may not be specified for %q+D", decl); + *no_add_attrs = true; + } + return NULL_TREE; } +/* Handle a "aligned" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args, + int flags, bool *no_add_attrs) +{ + return common_handle_aligned_attribute (node, args, flags, + no_add_attrs, false); +} + +/* Handle a "warn_if_not_aligned" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_warn_if_not_aligned_attribute (tree *node, tree ARG_UNUSED (name), + tree args, int flags, + bool *no_add_attrs) +{ + return common_handle_aligned_attribute (node, args, flags, + no_add_attrs, true); +} + /* Handle a "weak" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index f2b8096..f27356b 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -8363,6 +8363,8 @@ finish_enum (tree enumtype, tree values, tree attributes) TYPE_PRECISION (tem) = TYPE_PRECISION (enumtype); SET_TYPE_ALIGN (tem, TYPE_ALIGN (enumtype)); TYPE_USER_ALIGN (tem) = TYPE_USER_ALIGN (enumtype); + SET_TYPE_WARN_IF_NOT_ALIGN (tem, + TYPE_WARN_IF_NOT_ALIGN (enumtype)); TYPE_UNSIGNED (tem) = TYPE_UNSIGNED (enumtype); TYPE_LANG_SPECIFIC (tem) = TYPE_LANG_SPECIFIC (enumtype); } diff --git a/gcc/print-tree.c b/gcc/print-tree.c index ea26a0b..7b53c49 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -603,8 +603,10 @@ print_node (FILE *file, const char *prefix, tree node, int indent, if (TYPE_USER_ALIGN (node)) fprintf (file, " user"); - fprintf (file, " align %d symtab %d alias set " HOST_WIDE_INT_PRINT_DEC, - TYPE_ALIGN (node), TYPE_SYMTAB_ADDRESS (node), + fprintf (file, " align %d warn_if_not_align %d symtab %d alias set " + HOST_WIDE_INT_PRINT_DEC, + TYPE_ALIGN (node), TYPE_WARN_IF_NOT_ALIGN (node), + TYPE_SYMTAB_ADDRESS (node), (HOST_WIDE_INT) TYPE_ALIAS_SET (node)); if (TYPE_STRUCTURAL_EQUALITY_P (node)) diff --git a/gcc/stor-layout.c b/gcc/stor-layout.c index 1574e43..28249bc 100644 --- a/gcc/stor-layout.c +++ b/gcc/stor-layout.c @@ -1074,6 +1074,38 @@ update_alignment_for_field (record_layout_info rli, tree field, return desired_align; } +static void +handle_warn_if_not_align (tree field, unsigned int record_align) +{ + tree type = TREE_TYPE (field); + + if (type == error_mark_node) + return; + + unsigned int warn_if_not_align = TYPE_WARN_IF_NOT_ALIGN (type); + + if (!warn_if_not_align && TYPE_USER_ALIGN (type)) + warn_if_not_align = TYPE_ALIGN (type); + + if (!warn_if_not_align) + return; + + tree context = DECL_CONTEXT (field); + + warn_if_not_align /= BITS_PER_UNIT; + record_align /= BITS_PER_UNIT; + if ((record_align % warn_if_not_align) != 0) + warning (0, "alignment %d of %qT is less than %d", + record_align, context, warn_if_not_align); + + unsigned int off + = (tree_to_uhwi (DECL_FIELD_OFFSET (field)) + + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) / BITS_PER_UNIT); + if ((off % warn_if_not_align) != 0) + warning (0, "%q+D offset %d in %qT isn't aligned to %d", + field, off, context, warn_if_not_align); +} + /* Called from place_field to handle unions. */ static void @@ -1084,6 +1116,7 @@ place_union_field (record_layout_info rli, tree field) DECL_FIELD_OFFSET (field) = size_zero_node; DECL_FIELD_BIT_OFFSET (field) = bitsize_zero_node; SET_DECL_OFFSET_ALIGN (field, BIGGEST_ALIGNMENT); + handle_warn_if_not_align (field, rli->record_align); /* If this is an ERROR_MARK return *after* having set the field at the start of the union. This helps when parsing @@ -1169,6 +1202,7 @@ place_field (record_layout_info rli, tree field) DECL_FIELD_OFFSET (field) = rli->offset; DECL_FIELD_BIT_OFFSET (field) = rli->bitpos; SET_DECL_OFFSET_ALIGN (field, rli->offset_align); + handle_warn_if_not_align (field, rli->record_align); return; } @@ -1290,6 +1324,9 @@ place_field (record_layout_info rli, tree field) if (! DECL_PACKED (field)) TYPE_USER_ALIGN (rli->t) |= TYPE_USER_ALIGN (type); + + SET_TYPE_WARN_IF_NOT_ALIGN (rli->t, + TYPE_WARN_IF_NOT_ALIGN (type)); } #ifdef BITFIELD_NBYTES_LIMITED @@ -1328,6 +1365,8 @@ place_field (record_layout_info rli, tree field) rli->bitpos = round_up (rli->bitpos, type_align); TYPE_USER_ALIGN (rli->t) |= TYPE_USER_ALIGN (type); + SET_TYPE_WARN_IF_NOT_ALIGN (rli->t, + TYPE_WARN_IF_NOT_ALIGN (type)); } #endif @@ -1478,6 +1517,7 @@ place_field (record_layout_info rli, tree field) DECL_FIELD_OFFSET (field) = rli->offset; DECL_FIELD_BIT_OFFSET (field) = rli->bitpos; SET_DECL_OFFSET_ALIGN (field, rli->offset_align); + handle_warn_if_not_align (field, rli->record_align); /* Evaluate nonconstant offsets only once, either now or as soon as safe. */ if (TREE_CODE (DECL_FIELD_OFFSET (field)) != INTEGER_CST) @@ -2088,6 +2128,8 @@ finish_builtin_struct (tree type, const char *name, tree fields, { SET_TYPE_ALIGN (type, TYPE_ALIGN (align_type)); TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (align_type); + SET_TYPE_WARN_IF_NOT_ALIGN (type, + TYPE_WARN_IF_NOT_ALIGN (align_type)); } layout_type (type); @@ -2324,6 +2366,9 @@ layout_type (tree type) align = MAX (align, TYPE_ALIGN (type)); else TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (element); + if (!TYPE_WARN_IF_NOT_ALIGN (type)) + SET_TYPE_WARN_IF_NOT_ALIGN (type, + TYPE_WARN_IF_NOT_ALIGN (element)); #ifdef ROUND_TYPE_ALIGN align = ROUND_TYPE_ALIGN (type, align, BITS_PER_UNIT); #else diff --git a/gcc/testsuite/gcc.dg/pr53037-1.c b/gcc/testsuite/gcc.dg/pr53037-1.c new file mode 100644 index 0000000..1ab1f86 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr53037-1.c @@ -0,0 +1,59 @@ +/* PR c/53037. */ +/* { dg-do compile } */ +/* { dg-options "-O0" } */ + +typedef unsigned long long __u64 + __attribute__((aligned(4),warn_if_not_aligned(8))); + +struct foo1 +{ + int i1; + int i2; + int i3; + __u64 x; /* { dg-warning "'x' offset 12 in 'struct foo1' isn't aligned to 8" } */ +}; /* { dg-warning "alignment 4 of 'struct foo1' is less than 8" } */ + +struct foo2 +{ + int i1; + int i2; + int i3; + __u64 x; /* { dg-warning "'x' offset 12 in 'struct foo2' isn't aligned to 8" } */ +} __attribute__((aligned(8))); + +struct foo3 +{ + int i1; + int i3; + __u64 x; +}; /* { dg-warning "alignment 4 of 'struct foo3' is less than 8" } */ + +struct foo4 +{ + int i1; + int i2; + __u64 x; +} __attribute__((aligned(8))); + +union bar1 +{ + int i1; + int i3; + __u64 x; +}; /* { dg-warning "alignment 4 of 'union bar1' is less than 8" } */ + +union bar2 +{ + int i1; + int i3; + __u64 x; +} __attribute__((aligned(8))); + +struct __attribute__ ((aligned (8))) S8 { char a[8]; }; +struct __attribute__ ((packed)) S0 { + struct S8 s8; +}; /* { dg-warning "alignment 1 of 'struct S0' is less than 8" } */ + +struct __attribute__ ((packed)) S1 { + long long ll; +}; diff --git a/gcc/tree-core.h b/gcc/tree-core.h index ea73477..0882bdc 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1516,8 +1516,9 @@ struct GTY(()) tree_type_common { so we need to store the value 32 (not 31, as we need the zero as well), hence six bits. */ unsigned align : 6; + unsigned warn_if_not_align : 6; unsigned typeless_storage : 1; - unsigned spare : 24; + unsigned spare : 18; alias_set_type alias_set; tree pointer_to; diff --git a/gcc/tree.c b/gcc/tree.c index a58f9aa..66287ae 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -8303,6 +8303,7 @@ build_range_type_1 (tree type, tree lowval, tree highval, bool shared) TYPE_SIZE_UNIT (itype) = TYPE_SIZE_UNIT (type); SET_TYPE_ALIGN (itype, TYPE_ALIGN (type)); TYPE_USER_ALIGN (itype) = TYPE_USER_ALIGN (type); + SET_TYPE_WARN_IF_NOT_ALIGN (itype, TYPE_WARN_IF_NOT_ALIGN (type)); if (!shared) return itype; diff --git a/gcc/tree.h b/gcc/tree.h index c6e883c..7d9572e 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1922,6 +1922,16 @@ extern machine_mode element_mode (const_tree t); /* The alignment for NODE, in bytes. */ #define TYPE_ALIGN_UNIT(NODE) (TYPE_ALIGN (NODE) / BITS_PER_UNIT) +/* The minimum alignment necessary for objects of this type without + warning. The value is an int, measured in bits. */ +#define TYPE_WARN_IF_NOT_ALIGN(NODE) \ + (TYPE_CHECK (NODE)->type_common.warn_if_not_align \ + ? ((unsigned)1) << ((NODE)->type_common.warn_if_not_align - 1) : 0) + +/* Specify that TYPE_WARN_IF_NOT_ALIGN(NODE) is X. */ +#define SET_TYPE_WARN_IF_NOT_ALIGN(NODE, X) \ + (TYPE_CHECK (NODE)->type_common.warn_if_not_align = ffs_hwi (X)) + /* If your language allows you to declare types, and you want debug info for them, then you need to generate corresponding TYPE_DECL nodes. These "stub" TYPE_DECL nodes have no name, and simply point at the -- 2.9.4