chill created this revision. chill added reviewers: john.brawn, olista01, eli.friedman, rengolin. Herald added a reviewer: javed.absar. Herald added subscribers: chrib, kristof.beyls.
The "Procedure Call Procedure Call Standard for the ARMĀ® Architecture" (https://static.docs.arm.com/ihi0042/f/IHI0042F_aapcs.pdf), specifies that composite types are passed according to their natural alignment: > 5.5 Parameter Passing > ... > B.5 If the argument is an alignment adjusted type its value is passed as a > copy of the actual value. The > copy will have an alignment defined as follows. > > - For a Fundamental Data Type, the alignment is the natural alignment of that > type, after any promotions > - For a Composite Type, the alignment of the copy will have 4-byte alignment > if its natural alignment is <= 4 and 8-byte alignment if its natural > alignment is >= 8 The "natural alignment" is defined as the maximum of the alignment of the top-level components: > 4.3 Composite Types > ... > > - The natural alignment of a composite type is the maximum of each of the > member alignments of the 'top-level' members of the composite type i.e. > before any alignment adjustment of the entire composite is applied `clang`, however, uses the actual alignment of the composite, instead of the natural alignment. This patch fixes passing of composite types to use the natural alignment. With this patch `clang` conforms to AAPCS and is compatible with GCC. Repository: rC Clang https://reviews.llvm.org/D46013 Files: include/clang/AST/ASTContext.h include/clang/AST/RecordLayout.h lib/AST/ASTContext.cpp lib/AST/RecordLayout.cpp lib/AST/RecordLayoutBuilder.cpp lib/CodeGen/TargetInfo.cpp test/CodeGen/aapcs-align.cc test/CodeGen/arm-arguments.c
Index: test/CodeGen/arm-arguments.c =================================================================== --- test/CodeGen/arm-arguments.c +++ test/CodeGen/arm-arguments.c @@ -211,10 +211,13 @@ // APCS-GNU: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align {{[0-9]+}} %[[b]], i8* align {{[0-9]+}} %[[c]] // APCS-GNU: %[[d:.*]] = bitcast %struct.s35* %[[a]] to <4 x float>* // APCS-GNU: load <4 x float>, <4 x float>* %[[d]], align 16 -// AAPCS-LABEL: define arm_aapcscc <4 x float> @f35(i32 %i, %struct.s35* byval align 8, %struct.s35* byval align 8) -// AAPCS: %[[a:.*]] = alloca %struct.s35, align 16 -// AAPCS: %[[b:.*]] = bitcast %struct.s35* %[[a]] to i8* -// AAPCS: %[[c:.*]] = bitcast %struct.s35* %0 to i8* -// AAPCS: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %[[b]], i8* align 8 %[[c]] -// AAPCS: %[[d:.*]] = bitcast %struct.s35* %[[a]] to <4 x float>* -// AAPCS: load <4 x float>, <4 x float>* %[[d]], align 16 + +// AAPCS-LABEL: define arm_aapcscc <4 x float> @f35(i32 %i, %struct.s35* byval align 4 %s1, %struct.s35* byval align 4 %s2) +// AAPCS: %[[a_addr:.*]] = alloca <4 x float>, align 16 +// AAPCS: %[[b_addr:.*]] = alloca <4 x float>, align 16 +// AAPCS: %[[p1:.*]] = bitcast %struct.s35* %s1 to <4 x float>* +// AAPCS: %[[a:.*]] = load <4 x float>, <4 x float>* %[[p1]], align 4 +// AAPCS: %[[p2:.*]] = bitcast %struct.s35* %s2 to <4 x float>* +// AAPCS: %[[b:.*]] = load <4 x float>, <4 x float>* %[[p2]], align 4 +// AAPCS: store <4 x float> %[[a]], <4 x float>* %[[a_addr]], align 16 +// AAPCS: store <4 x float> %[[b]], <4 x float>* %[[b_addr]], align 16 Index: test/CodeGen/aapcs-align.cc =================================================================== --- /dev/null +++ test/CodeGen/aapcs-align.cc @@ -0,0 +1,102 @@ +// REQUIRES: arm-registered-target +// RUN: %clang_cc1 -triple arm-none-none-eabi \ +// RUN: -O2 \ +// RUN: -target-cpu cortex-a8 \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +extern "C" { + +struct S { + int x, y; +}; + +// Base case, nothing interesting. +void f0(int, S); +void f0m(int, int, int, int, int, S); +void g0() { + S s = {6, 7}; + f0(1, s); + f0m(1, 2, 3, 4, 5, s); +} +// CHECK: define void @g0 +// CHECK: call void @f0{{.*}}[2 x i32] [i32 6, i32 7] +// CHECK: call void @f0m{{.*}}[2 x i32] [i32 6, i32 7] +// CHECK: declare void @f0(i32, [2 x i32]) +// CHECK: declare void @f0m(i32, i32, i32, i32, i32, [2 x i32]) + +// Aligned struct, passed according to its natural alignment. +struct __attribute__((aligned(8))) S8 { + int x, y; +} s8; + +void f1(int, S8); +void f1m(int, int, int, int, int, S8 s); +void g1() { + S8 s = {6, 7}; + f1(1, s); + f1m(1, 2, 3, 4, 5, s); +} +// CHECK: define void @g1 +// CHECK: call void @f1{{.*}}[2 x i32] [i32 6, i32 7] +// CHECK: call void @f1m{{.*}}[2 x i32] [i32 6, i32 7] +// CHECK: declare void @f1(i32, [2 x i32]) +// CHECK: declare void @f1m(i32, i32, i32, i32, i32, [2 x i32]) + +// Aligned struct, passed according to its natural alignment. +struct alignas(16) S16 { + int x, y; +}; + +extern "C" void f2(int, S16); +extern "C" void f2m(int, int, int, int, int, S16); + +void g2() { + S16 s = {6, 7}; + f2(1, s); + f2m(1, 2, 3, 4, 5, s); +} +// CHECK: define void @g2 +// CHECK: call void @f2{{.*}}[4 x i32] [i32 6, i32 7, +// CHECK: call void @f2m{{.*}}[4 x i32] [i32 6, i32 7, +// CHECK: declare void @f2(i32, [4 x i32]) +// CHECK: declare void @f2m(i32, i32, i32, i32, i32, [4 x i32]) + +// Increased natural alignment. +struct SF8 { + int x __attribute__((aligned(8))); + int y; +}; + +void f3(int, SF8); +void f3m(int, int, int, int, int, SF8); +void g3() { + SF8 s = {6, 7}; + f3(1, s); + f3m(1, 2, 3, 4, 5, s); +} +// CHECK: define void @g3 +// CHECK: call void @f3{{.*}}[1 x i64] [i64 30064771078] +// CHECK: call void @f3m{{.*}}[1 x i64] [i64 30064771078] +// CHECK: declare void @f3(i32, [1 x i64]) +// CHECK: declare void @f3m(i32, i32, i32, i32, i32, [1 x i64]) + +// Increased natural alignment, capped to 8 though. +struct SF16 { + int x; + int y alignas(16); + int z, a, b, c, d, e, f, g, h, i, j, k; +}; + +void f4(int, SF16); +void f4m(int, int, int, int, int, SF16); +void g4() { + SF16 s = {6, 7}; + f4(1, s); + f4m(1, 2, 3, 4, 5, s); +} +// CHECK: define void @g4 +// CHECK: call void @f4{{.*}}i32 1, %struct.SF16* byval nonnull align 8 +// CHECK: call void @f4m{{.*}}%struct.SF16* byval nonnull align 8 +// CHECK: declare void @f4(i32, %struct.SF16* byval align 8) +// CHECK: declare void @f4m(i32, i32, i32, i32, i32, %struct.SF16* byval align 8) +} Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -5777,15 +5777,18 @@ // The ABI alignment for APCS is 4-byte and for AAPCS at least 4-byte and at // most 8-byte. We realign the indirect argument if type alignment is bigger // than ABI alignment. - uint64_t ABIAlign = 4; - uint64_t TyAlign = getContext().getTypeAlign(Ty) / 8; - if (getABIKind() == ARMABIInfo::AAPCS_VFP || - getABIKind() == ARMABIInfo::AAPCS) - ABIAlign = std::min(std::max(TyAlign, (uint64_t)4), (uint64_t)8); - + uint64_t ABIAlign = 32; + uint64_t TyAlign; + if (getABIKind() == ARMABIInfo::AAPCS_VFP || + getABIKind() == ARMABIInfo::AAPCS) { + TyAlign = getContext().getTypeNaturalAlign(Ty); + ABIAlign = std::min(std::max(TyAlign, (uint64_t)32), (uint64_t)64); + } else { + TyAlign = getContext().getTypeAlign(Ty) ; + } if (getContext().getTypeSizeInChars(Ty) > CharUnits::fromQuantity(64)) { assert(getABIKind() != ARMABIInfo::AAPCS16_VFP && "unexpected byval"); - return ABIArgInfo::getIndirect(CharUnits::fromQuantity(ABIAlign), + return ABIArgInfo::getIndirect(CharUnits::fromQuantity(ABIAlign / 8), /*ByVal=*/true, /*Realign=*/TyAlign > ABIAlign); } @@ -5801,7 +5804,7 @@ unsigned SizeRegs; // FIXME: Try to match the types of the arguments more accurately where // we can. - if (getContext().getTypeAlign(Ty) <= 32) { + if (TyAlign <= 32) { ElemTy = llvm::Type::getInt32Ty(getVMContext()); SizeRegs = (getContext().getTypeSize(Ty) + 31) / 32; } else { Index: lib/AST/RecordLayoutBuilder.cpp =================================================================== --- lib/AST/RecordLayoutBuilder.cpp +++ lib/AST/RecordLayoutBuilder.cpp @@ -582,6 +582,9 @@ /// \brief The alignment if attribute packed is not used. CharUnits UnpackedAlignment; + /// \brief The maximum of the alignments of top-level members. + CharUnits NaturalAlignment; + SmallVector<uint64_t, 16> FieldOffsets; /// \brief Whether the external AST source has provided a layout for this @@ -662,6 +665,7 @@ EmptySubobjectMap *EmptySubobjects) : Context(Context), EmptySubobjects(EmptySubobjects), Size(0), Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()), + NaturalAlignment(CharUnits::One()), UseExternalLayout(false), InferAlignment(false), Packed(false), IsUnion(false), IsMac68kAlign(false), IsMsStruct(false), UnfilledBitsInLastUnit(0), LastBitfieldTypeSize(0), @@ -1700,7 +1704,9 @@ setSize(std::max(getSizeInBits(), getDataSizeInBits())); // Remember max struct/class alignment. - UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign), + NaturalAlignment = + std::max(NaturalAlignment, Context.toCharUnitsFromBits(FieldAlign)); + UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign), Context.toCharUnitsFromBits(UnpackedFieldAlign)); } @@ -1855,6 +1861,7 @@ setSize(std::max(getSizeInBits(), getDataSizeInBits())); // Remember max struct/class alignment. + NaturalAlignment = std::max(NaturalAlignment, FieldAlign); UpdateAlignment(FieldAlign, UnpackedFieldAlign); } @@ -2973,7 +2980,8 @@ if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { Builder.cxxLayout(RD); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment, + *this, Builder.Size, Builder.Alignment, Builder.Alignment, + Builder.RequiredAlignment, Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase, Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets, Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(), @@ -2983,7 +2991,8 @@ } else { Builder.layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment, + *this, Builder.Size, Builder.Alignment, Builder.Alignment, + Builder.RequiredAlignment, Builder.Size, Builder.FieldOffsets); } } else { @@ -3004,7 +3013,7 @@ CharUnits NonVirtualSize = skipTailPadding ? DataSize : Builder.NonVirtualSize; NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, + *this, Builder.getSize(), Builder.Alignment, Builder.NaturalAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(), CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets, @@ -3017,7 +3026,7 @@ Builder.Layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, + *this, Builder.getSize(), Builder.Alignment, Builder.NaturalAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.getSize(), Builder.FieldOffsets); } @@ -3171,6 +3180,7 @@ const ASTRecordLayout *NewEntry = new (*this) ASTRecordLayout(*this, Builder.getSize(), Builder.Alignment, + Builder.NaturalAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.getDataSize(), Index: lib/AST/RecordLayout.cpp =================================================================== --- lib/AST/RecordLayout.cpp +++ lib/AST/RecordLayout.cpp @@ -30,17 +30,20 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits naturalAlignment, CharUnits requiredAlignment, CharUnits datasize, ArrayRef<uint64_t> fieldoffsets) : Size(size), DataSize(datasize), Alignment(alignment), + NaturalAlignment(naturalAlignment), RequiredAlignment(requiredAlignment) { FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end()); } // Constructor for C++ records. ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits naturalAlignment, CharUnits requiredAlignment, bool hasOwnVFPtr, bool hasExtendableVFPtr, CharUnits vbptroffset, @@ -57,6 +60,7 @@ const BaseOffsetsMapTy& BaseOffsets, const VBaseOffsetsMapTy& VBaseOffsets) : Size(size), DataSize(datasize), Alignment(alignment), + NaturalAlignment(naturalAlignment), RequiredAlignment(requiredAlignment), CXXInfo(new (Ctx) CXXRecordLayoutInfo) { FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end()); Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -1663,6 +1663,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { uint64_t Width = 0; unsigned Align = 8; + unsigned NaturalAlign = 0; bool AlignIsRequired = false; unsigned AS = 0; switch (T->getTypeClass()) { @@ -1873,6 +1874,7 @@ const ASTRecordLayout &Layout = getASTObjCInterfaceLayout(ObjCI->getDecl()); Width = toBits(Layout.getSize()); Align = toBits(Layout.getAlignment()); + NaturalAlign = toBits(Layout.getNaturalAlignment()); break; } case Type::Record: @@ -1891,6 +1893,7 @@ getTypeInfo(ED->getIntegerType()->getUnqualifiedDesugaredType()); if (unsigned AttrAlign = ED->getMaxAlignment()) { Info.Align = AttrAlign; + Info.NaturalAlign = Info.Align; Info.AlignIsRequired = true; } return Info; @@ -1901,6 +1904,7 @@ const ASTRecordLayout &Layout = getASTRecordLayout(RD); Width = toBits(Layout.getSize()); Align = toBits(Layout.getAlignment()); + NaturalAlign = toBits(Layout.getNaturalAlignment()); AlignIsRequired = RD->hasAttr<AlignedAttr>(); break; } @@ -1937,6 +1941,7 @@ AlignIsRequired = Info.AlignIsRequired; } Width = Info.Width; + NaturalAlign = Info.NaturalAlign; break; } @@ -1974,7 +1979,9 @@ } assert(llvm::isPowerOf2_32(Align) && "Alignment must be power of 2"); - return TypeInfo(Width, Align, AlignIsRequired); + if (NaturalAlign == 0) + NaturalAlign = Align; + return TypeInfo(Width, Align, NaturalAlign, AlignIsRequired); } unsigned ASTContext::getOpenMPDefaultSimdAlign(QualType T) const { Index: include/clang/AST/RecordLayout.h =================================================================== --- include/clang/AST/RecordLayout.h +++ include/clang/AST/RecordLayout.h @@ -71,6 +71,10 @@ // Alignment - Alignment of record in characters. CharUnits Alignment; + // NaturalAlignment - Maximum of the alignments of the record members in + // characters. + CharUnits NaturalAlignment; + /// RequiredAlignment - The required alignment of the object. In the MS-ABI /// the __declspec(align()) trumps #pramga pack and must always be obeyed. CharUnits RequiredAlignment; @@ -136,14 +140,16 @@ CXXRecordLayoutInfo *CXXInfo = nullptr; ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits naturalAlignment, CharUnits requiredAlignment, CharUnits datasize, ArrayRef<uint64_t> fieldoffsets); using BaseOffsetsMapTy = CXXRecordLayoutInfo::BaseOffsetsMapTy; // Constructor for C++ records. ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits naturalAlignment, CharUnits requiredAlignment, bool hasOwnVFPtr, bool hasExtendableVFPtr, CharUnits vbptroffset, @@ -170,6 +176,9 @@ /// getAlignment - Get the record alignment in characters. CharUnits getAlignment() const { return Alignment; } + /// getNaturalAlignment - Get the natural record alignment in characters. + CharUnits getNaturalAlignment() const { return NaturalAlignment; } + /// getSize - Get the record size in characters. CharUnits getSize() const { return Size; } Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -137,11 +137,14 @@ struct TypeInfo { uint64_t Width = 0; unsigned Align = 0; + unsigned NaturalAlign = 0; bool AlignIsRequired : 1; TypeInfo() : AlignIsRequired(false) {} - TypeInfo(uint64_t Width, unsigned Align, bool AlignIsRequired) - : Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {} + TypeInfo(uint64_t Width, unsigned Align, unsigned NaturalAlign, + bool AlignIsRequired) + : Width(Width), Align(Align), NaturalAlign(NaturalAlign), + AlignIsRequired(AlignIsRequired) {} }; /// \brief Holds long-lived AST nodes (such as types and decls) that can be @@ -2043,6 +2046,15 @@ unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; } unsigned getTypeAlign(const Type *T) const { return getTypeInfo(T).Align; } + /// \brief Return the ABI-specified natural alignment of a (complete) type \p + /// T, before alignment adjustments, in bits. + unsigned getTypeNaturalAlign(QualType T) const { + return getTypeInfo(T).NaturalAlign; + } + unsigned getTypeNaturalAlign(const Type *T) const { + return getTypeInfo(T).NaturalAlign; + } + /// \brief Return the ABI-specified alignment of a type, in bits, or 0 if /// the type is incomplete and we cannot determine the alignment (for /// example, from alignment attributes).
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits