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.
This commit is contained in:
David Edelsohn 2021-03-14 15:09:21 -04:00
parent 1cdfc98a99
commit 42a21b4cb5
4 changed files with 130 additions and 19 deletions

View File

@ -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

View 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,

View 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)

View File

@ -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;
}