From 42a21b4cb540be93548a6ff6d4cb4a73ab1665be Mon Sep 17 00:00:00 2001 From: David Edelsohn Date: Sun, 14 Mar 2021 15:09:21 -0400 Subject: [PATCH] aix: ABI struct alignment (PR99557) 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 | 6 +- gcc/config/rs6000/rs6000-protos.h | 1 + gcc/config/rs6000/rs6000.c | 89 +++++++++++++++++++++++++----- gcc/testsuite/gcc.target/powerpc/pr99557.c | 53 ++++++++++++++++++ 4 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99557.c diff --git a/gcc/config/rs6000/aix.h b/gcc/config/rs6000/aix.h index 2db50c8..7fccb313 100644 --- a/gcc/config/rs6000/aix.h +++ b/gcc/config/rs6000/aix.h @@ -223,10 +223,8 @@ /* 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 diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index 203660b..c44fd3d 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -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, diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 34c4eda..fd2b0b5 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -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 % 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 index 0000000..e0f8b24 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr99557.c @@ -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; +} -- 2.7.4