diff options
author | David Edelsohn <dje.gcc@gmail.com> | 2021-03-14 15:09:21 -0400 |
---|---|---|
committer | David Edelsohn <dje.gcc@gmail.com> | 2021-03-26 19:56:12 -0400 |
commit | 42a21b4cb540be93548a6ff6d4cb4a73ab1665be (patch) | |
tree | c2bc50b613ad33db76a0db5a0dd76f4897212640 | |
parent | 1cdfc98a9981768c475fabf069ba4d3e460deb2a (diff) | |
download | gcc-42a21b4cb540be93548a6ff6d4cb4a73ab1665be.zip gcc-42a21b4cb540be93548a6ff6d4cb4a73ab1665be.tar.gz gcc-42a21b4cb540be93548a6ff6d4cb4a73ab1665be.tar.bz2 |
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.
-rw-r--r-- | gcc/config/rs6000/aix.h | 6 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000-protos.h | 1 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.c | 89 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/powerpc/pr99557.c | 53 |
4 files changed, 130 insertions, 19 deletions
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 %<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 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; +} |