]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
aix: ABI struct alignment (PR99557)
authorDavid Edelsohn <dje.gcc@gmail.com>
Sun, 14 Mar 2021 19:09:21 +0000 (15:09 -0400)
committerDavid Edelsohn <dje.gcc@gmail.com>
Fri, 26 Mar 2021 23:56:12 +0000 (19:56 -0400)
The AIX power alignment rules apply the natural alignment of the
"first member" if it is of a floating-point data type (or is an aggregate
whose recursively "first" member or element is such a type). The alignment
associated with these types for subsequent members use an alignment value
where the floating-point data type is considered to have 4-byte alignment.

GCC had been stripping array type but had not recursively looked
within structs and unions.  This also applies to classes and
subclasses and, therefore, becomes more prominent with C++.

For example,

struct A {
  double x[2];
  int y;
};
struct B {
  int i;
  struct A a;
};

struct A has double-word alignment for the bare type, but
word alignment and offset within struct B despite the alignment of
struct A.  If struct A were the first member of struct B, struct B
would have double-word alignment.  One must search for the innermost
first member to increase the alignment if double and then search for
the innermost first member to reduce the alignment if the TYPE had
double-word alignment solely because the innermost first member was
double.

This patch recursively looks through the first member to apply the
double-word alignment to the struct / union as a whole and to apply
the word alignment to the struct or union as a member within a struct
or union.

This is an ABI change for GCC on AIX, but GCC on AIX had not correctly
implemented the AIX ABI and had not been compatible with the IBM XL
compiler.

Bootstrapped on powerpc-ibm-aix7.2.3.0.

gcc/ChangeLog:

* config/rs6000/aix.h (ADJUST_FIELD_ALIGN): Call function.
* config/rs6000/rs6000-protos.h (rs6000_special_adjust_field_align):
Declare.
* config/rs6000/rs6000.c (rs6000_special_adjust_field_align): New.
(rs6000_special_round_type_align): Recursively check innermost first
field.

gcc/testsuite/ChangeLog:

* gcc.target/powerpc/pr99557.c: New.

gcc/config/rs6000/aix.h
gcc/config/rs6000/rs6000-protos.h
gcc/config/rs6000/rs6000.c
gcc/testsuite/gcc.target/powerpc/pr99557.c [new file with mode: 0644]

index 2db50c8007f112ef89b6a0e43124b0947cdd94f6..7fccb31307bbd4098d00f62f66d304800598863e 100644 (file)
 /* This now supports a natural alignment mode.  */
 /* AIX word-aligns FP doubles but doubleword-aligns 64-bit ints.  */
 #define ADJUST_FIELD_ALIGN(FIELD, TYPE, COMPUTED) \
-  ((TARGET_ALIGN_NATURAL == 0                                          \
-    && (TYPE_MODE (strip_array_types (TYPE)) == DFmode                 \
-       || TYPE_MODE (strip_array_types (TYPE)) == DCmode))             \
-   ? MIN ((COMPUTED), 32)                                              \
+  (TARGET_ALIGN_NATURAL == 0                                           \
+   ? rs6000_special_adjust_field_align (TYPE, COMPUTED)                        \
    : (COMPUTED))
 
 /* AIX increases natural record alignment to doubleword if the first
index 203660b0a789713c9d0e9a5e7e91bc2a06263627..c44fd3d02638be6844f8f760984c6ea0876e931e 100644 (file)
@@ -227,6 +227,7 @@ address_is_prefixed (rtx addr,
 #ifdef TREE_CODE
 extern unsigned int rs6000_data_alignment (tree, unsigned int, enum data_align);
 extern bool rs6000_special_adjust_field_align_p (tree, unsigned int);
+extern unsigned int rs6000_special_adjust_field_align (tree, unsigned int);
 extern unsigned int rs6000_special_round_type_align (tree, unsigned int,
                                                     unsigned int);
 extern unsigned int darwin_rs6000_special_round_type_align (tree, unsigned int,
index 34c4edae20ea0dd94c1f08c60aa677ea8e8a5000..fd2b0b5280c8206efc36bce1e6521321e93954bb 100644 (file)
@@ -7853,32 +7853,91 @@ rs6000_special_adjust_field_align_p (tree type, unsigned int computed)
   return false;
 }
 
-/* AIX increases natural record alignment to doubleword if the first
-   field is an FP double while the FP fields remain word aligned.  */
+/* AIX word-aligns FP doubles but doubleword-aligns 64-bit ints.  */
+
+unsigned int
+rs6000_special_adjust_field_align (tree type, unsigned int computed)
+{
+  if (computed <= 32)
+    return computed;
+
+  /* Strip initial arrays.  */
+  while (TREE_CODE (type) == ARRAY_TYPE)
+    type = TREE_TYPE (type);
+
+  /* If RECORD or UNION, recursively find the first field. */
+  while (AGGREGATE_TYPE_P (type))
+    {
+      tree field = TYPE_FIELDS (type);
+
+      /* Skip all non field decls */
+      while (field != NULL
+            && (TREE_CODE (field) != FIELD_DECL
+                || DECL_FIELD_ABI_IGNORED (field)))
+       field = DECL_CHAIN (field);
+
+      if (! field)
+       break;
+
+      /* A packed field does not contribute any extra alignment.  */
+      if (DECL_PACKED (field))
+       return computed;
+
+      type = TREE_TYPE (field);
+
+      /* Strip arrays.  */
+      while (TREE_CODE (type) == ARRAY_TYPE)
+       type = TREE_TYPE (type);
+    }
+
+  if (! AGGREGATE_TYPE_P (type) && type != error_mark_node
+      && (TYPE_MODE (type) == DFmode || TYPE_MODE (type) == DCmode))
+    computed = MIN (computed, 32);
+
+  return computed;
+}
+
+/* AIX increases natural record alignment to doubleword if the innermost first
+   field is an FP double while the FP fields remain word aligned.
+   Only called if TYPE initially is a RECORD or UNION.  */
 
 unsigned int
 rs6000_special_round_type_align (tree type, unsigned int computed,
                                 unsigned int specified)
 {
   unsigned int align = MAX (computed, specified);
-  tree field = TYPE_FIELDS (type);
 
-  /* Skip all non field decls */
-  while (field != NULL
-        && (TREE_CODE (field) != FIELD_DECL
-            || DECL_FIELD_ABI_IGNORED (field)))
-    field = DECL_CHAIN (field);
+  if (TYPE_PACKED (type) || align >= 64)
+    return align;
 
-  if (field != NULL && field != type)
+  /* If RECORD or UNION, recursively find the first field. */
+  do
     {
+      tree field = TYPE_FIELDS (type);
+
+      /* Skip all non field decls */
+      while (field != NULL
+            && (TREE_CODE (field) != FIELD_DECL
+                || DECL_FIELD_ABI_IGNORED (field)))
+       field = DECL_CHAIN (field);
+
+      if (! field)
+       break;
+
+      /* A packed field does not contribute any extra alignment.  */
+      if (DECL_PACKED (field))
+       return align;
+
       type = TREE_TYPE (field);
+
+      /* Strip arrays.  */
       while (TREE_CODE (type) == ARRAY_TYPE)
        type = TREE_TYPE (type);
+    } while (AGGREGATE_TYPE_P (type));
 
-      if (type != error_mark_node
-         && (TYPE_MODE (type) == DFmode || TYPE_MODE (type) == DCmode))
-       align = MAX (align, 64);
-    }
+  if (! AGGREGATE_TYPE_P (type) && type != error_mark_node
+      && (TYPE_MODE (type) == DFmode || TYPE_MODE (type) == DCmode))
+    align = MAX (align, 64);
 
   return align;
 }
@@ -10576,7 +10635,7 @@ rs6000_emit_move (rtx dest, rtx source, machine_mode mode)
     case E_OOmode:
     case E_XOmode:
       if (CONST_INT_P (operands[1]) && INTVAL (operands[1]) != 0)
-       error ("%qs is an opaque type, and you can't set it to other values.",
+       error ("%qs is an opaque type, and you cannot set it to other values",
               (mode == OOmode) ? "__vector_pair" : "__vector_quad");
       break;
 
@@ -20049,7 +20108,7 @@ rs6000_handle_altivec_attribute (tree *node,
   else if (TREE_CODE (type) == COMPLEX_TYPE)
     error ("use of %<complex%> in AltiVec types is invalid");
   else if (DECIMAL_FLOAT_MODE_P (mode))
-    error ("use of decimal floating point types in AltiVec types is invalid");
+    error ("use of decimal floating-point types in AltiVec types is invalid");
   else if (!TARGET_VSX)
     {
       if (type == long_unsigned_type_node || type == long_integer_type_node)
diff --git a/gcc/testsuite/gcc.target/powerpc/pr99557.c b/gcc/testsuite/gcc.target/powerpc/pr99557.c
new file mode 100644 (file)
index 0000000..e0f8b24
--- /dev/null
@@ -0,0 +1,53 @@
+/* { dg-do run { target { powerpc*-ibm-aix* } } } */
+/* { dg-options "" } */
+
+void abort (void);
+
+struct A {
+  double x[2];
+  int y;
+};
+
+struct B {
+  int i;
+  struct A a;
+};
+
+struct N {
+  double d[2];
+};
+
+struct S {
+  struct N n;
+  float f;
+};
+
+struct T {
+  char c;
+  struct S s;
+};
+
+int main() {   
+  if (__alignof(struct A) != 8)
+    abort();
+
+  if (__alignof(struct B) != 4)
+    abort();
+
+  if (__builtin_offsetof(struct B, a) != 4)
+    abort();
+
+  if (__alignof(struct N) != 8)
+    abort();
+
+  if (__alignof(struct S) != 8)
+    abort();
+
+  if (__alignof(struct T) != 4)
+    abort();
+
+  if (__builtin_offsetof(struct T, s) != 4)
+    abort();
+
+  return 0;
+}