__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

Reply via email to