diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h --- a/clang/include/clang/AST/RecordLayout.h +++ b/clang/include/clang/AST/RecordLayout.h @@ -78,6 +78,10 @@ /// the __declspec(align()) trumps #pramga pack and must always be obeyed. CharUnits RequiredAlignment; + /// AIXOffsetAlignment - The special AIX alignment for the object that + /// contains floating-point member or sub-member. This is for AIX-ABI only. + CharUnits AIXOffsetAlignment; + /// FieldOffsets - Array of field offsets in bits. ASTVector FieldOffsets; @@ -139,30 +143,25 @@ CXXRecordLayoutInfo *CXXInfo = nullptr; ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, - CharUnits unadjustedAlignment, - CharUnits requiredAlignment, CharUnits datasize, + CharUnits unadjustedAlignment, CharUnits requiredAlignment, + CharUnits aixOffsetAlignment, CharUnits datasize, ArrayRef fieldoffsets); using BaseOffsetsMapTy = CXXRecordLayoutInfo::BaseOffsetsMapTy; // Constructor for C++ records. - ASTRecordLayout(const ASTContext &Ctx, - CharUnits size, CharUnits alignment, - CharUnits unadjustedAlignment, - CharUnits requiredAlignment, - bool hasOwnVFPtr, bool hasExtendableVFPtr, - CharUnits vbptroffset, - CharUnits datasize, - ArrayRef fieldoffsets, + ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits unadjustedAlignment, CharUnits requiredAlignment, + CharUnits aixOffsetAlignment, bool hasOwnVFPtr, + bool hasExtendableVFPtr, CharUnits vbptroffset, + CharUnits datasize, ArrayRef fieldoffsets, CharUnits nonvirtualsize, CharUnits nonvirtualalignment, CharUnits SizeOfLargestEmptySubobject, - const CXXRecordDecl *PrimaryBase, - bool IsPrimaryBaseVirtual, + const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const CXXRecordDecl *BaseSharingVBPtr, - bool EndsWithZeroSizedObject, - bool LeadsWithZeroSizedBase, - const BaseOffsetsMapTy& BaseOffsets, - const VBaseOffsetsMapTy& VBaseOffsets); + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase, + const BaseOffsetsMapTy &BaseOffsets, + const VBaseOffsetsMapTy &VBaseOffsets); ~ASTRecordLayout() = default; @@ -175,6 +174,10 @@ /// getAlignment - Get the record alignment in characters. CharUnits getAlignment() const { return Alignment; } + /// getAIXOffsetAlignment - Get the record of aix offset alignment in + /// characters. + CharUnits getAIXOffsetAlignment() const { return AIXOffsetAlignment; } + /// getUnadjustedAlignment - Get the record alignment in characters, before /// alignment adjustement. CharUnits getUnadjustedAlignment() const { return UnadjustedAlignment; } @@ -193,9 +196,7 @@ /// getDataSize() - Get the record data size, which is the record size /// without tail padding, in characters. - CharUnits getDataSize() const { - return DataSize; - } + CharUnits getDataSize() const { return DataSize; } /// getNonVirtualSize - Get the non-virtual size (in chars) of an object, /// which is the size of the object without virtual bases. @@ -287,9 +288,7 @@ return !CXXInfo->VBPtrOffset.isNegative(); } - CharUnits getRequiredAlignment() const { - return RequiredAlignment; - } + CharUnits getRequiredAlignment() const { return RequiredAlignment; } bool endsWithZeroSizedObject() const { return CXXInfo && CXXInfo->EndsWithZeroSizedObject; diff --git a/clang/lib/AST/RecordLayout.cpp b/clang/lib/AST/RecordLayout.cpp --- a/clang/lib/AST/RecordLayout.cpp +++ b/clang/lib/AST/RecordLayout.cpp @@ -31,37 +31,32 @@ CharUnits alignment, CharUnits unadjustedAlignment, CharUnits requiredAlignment, + CharUnits aixOffsetAlignment, CharUnits datasize, ArrayRef fieldoffsets) : Size(size), DataSize(datasize), Alignment(alignment), UnadjustedAlignment(unadjustedAlignment), - RequiredAlignment(requiredAlignment) { + RequiredAlignment(requiredAlignment), + AIXOffsetAlignment(aixOffsetAlignment) { FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end()); } // Constructor for C++ records. -ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, - CharUnits size, CharUnits alignment, - CharUnits unadjustedAlignment, - CharUnits requiredAlignment, - bool hasOwnVFPtr, bool hasExtendableVFPtr, - CharUnits vbptroffset, - CharUnits datasize, - ArrayRef fieldoffsets, - CharUnits nonvirtualsize, - CharUnits nonvirtualalignment, - CharUnits SizeOfLargestEmptySubobject, - const CXXRecordDecl *PrimaryBase, - bool IsPrimaryBaseVirtual, - const CXXRecordDecl *BaseSharingVBPtr, - bool EndsWithZeroSizedObject, - bool LeadsWithZeroSizedBase, - const BaseOffsetsMapTy& BaseOffsets, - const VBaseOffsetsMapTy& VBaseOffsets) - : Size(size), DataSize(datasize), Alignment(alignment), - UnadjustedAlignment(unadjustedAlignment), - RequiredAlignment(requiredAlignment), CXXInfo(new (Ctx) CXXRecordLayoutInfo) -{ +ASTRecordLayout::ASTRecordLayout( + const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits unadjustedAlignment, CharUnits requiredAlignment, + CharUnits aixOffsetAlignment, bool hasOwnVFPtr, bool hasExtendableVFPtr, + CharUnits vbptroffset, CharUnits datasize, ArrayRef fieldoffsets, + CharUnits nonvirtualsize, CharUnits nonvirtualalignment, + CharUnits SizeOfLargestEmptySubobject, const CXXRecordDecl *PrimaryBase, + bool IsPrimaryBaseVirtual, const CXXRecordDecl *BaseSharingVBPtr, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase, + const BaseOffsetsMapTy &BaseOffsets, const VBaseOffsetsMapTy &VBaseOffsets) + : Size(size), DataSize(datasize), Alignment(alignment), + UnadjustedAlignment(unadjustedAlignment), + RequiredAlignment(requiredAlignment), + AIXOffsetAlignment(aixOffsetAlignment), + CXXInfo(new (Ctx) CXXRecordLayoutInfo) { FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end()); CXXInfo->PrimaryBase.setPointer(PrimaryBase); diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/RecordLayout.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/Attr.h" @@ -15,6 +14,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/Format.h" @@ -594,6 +594,10 @@ /// \brief The maximum of the alignments of top-level members. CharUnits UnadjustedAlignment; + /// \breif The AIX special alignment for object contains floating-point + /// member or sub-members + CharUnits AIXOffsetAlignment; + SmallVector FieldOffsets; /// Whether the external AST source has provided a layout for this @@ -679,15 +683,15 @@ : Context(Context), EmptySubobjects(EmptySubobjects), Size(0), Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()), UnadjustedAlignment(CharUnits::One()), - UseExternalLayout(false), InferAlignment(false), Packed(false), - IsUnion(false), IsMac68kAlign(false), IsMsStruct(false), - UnfilledBitsInLastUnit(0), LastBitfieldTypeSize(0), - MaxFieldAlignment(CharUnits::Zero()), DataSize(0), - NonVirtualSize(CharUnits::Zero()), + AIXOffsetAlignment(CharUnits::One()), UseExternalLayout(false), + InferAlignment(false), Packed(false), IsUnion(false), + IsMac68kAlign(false), IsMsStruct(false), UnfilledBitsInLastUnit(0), + LastBitfieldTypeSize(0), MaxFieldAlignment(CharUnits::Zero()), + DataSize(0), NonVirtualSize(CharUnits::Zero()), NonVirtualAlignment(CharUnits::One()), PaddedFieldSize(CharUnits::Zero()), PrimaryBase(nullptr), - PrimaryBaseIsVirtual(false), HasOwnVFPtr(false), - HasPackedField(false), FirstNearlyEmptyVBase(nullptr) {} + PrimaryBaseIsVirtual(false), HasOwnVFPtr(false), HasPackedField(false), + FirstNearlyEmptyVBase(nullptr) {} void Layout(const RecordDecl *D); void Layout(const CXXRecordDecl *D); @@ -762,9 +766,13 @@ /// alignment. void FinishLayout(const NamedDecl *D); - void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment); + void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment, + CharUnits AIXOffsetNewAlignment); + void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment) { + UpdateAlignment(NewAlignment, UnpackedNewAlignment, UnpackedNewAlignment); + } void UpdateAlignment(CharUnits NewAlignment) { - UpdateAlignment(NewAlignment, NewAlignment); + UpdateAlignment(NewAlignment, NewAlignment, NewAlignment); } /// Retrieve the externally-supplied field offset for the given @@ -806,6 +814,10 @@ }; } // end anonymous namespace +static bool isAIXLayout(const ASTContext &Context) { + return Context.getTargetInfo().getTriple().getOS() == llvm::Triple::AIX; +} + void ItaniumRecordLayoutBuilder::SelectPrimaryVBase(const CXXRecordDecl *RD) { for (const auto &I : RD->bases()) { assert(!I.getType()->isDependentType() && @@ -997,7 +1009,9 @@ setSize(getSize().alignTo(BaseAlign)); // Update the alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, + /*AIXOffsetAlignment : used by AIX-ABI*/ + BaseAlign); } void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases( @@ -1196,6 +1210,7 @@ // Clang <= 6 incorrectly applied the 'packed' attribute to base classes. // Per GCC's documentation, it only applies to non-static data members. CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment(); + CharUnits AIXOffsetBaseAlign = Layout.getAIXOffsetAlignment(); CharUnits BaseAlign = (Packed && ((Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver6) || @@ -1208,11 +1223,17 @@ (!HasExternalLayout || Offset == CharUnits::Zero()) && EmptySubobjects->CanPlaceBaseAtOffset(Base, CharUnits::Zero())) { setSize(std::max(getSize(), Layout.getSize())); - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, AIXOffsetBaseAlign); return CharUnits::Zero(); } + // Use AIX special alignment if current base is not the first member + // and the struct is not a union + if (isAIXLayout(Context) && !getDataSize().isZero() && !IsUnion) { + BaseAlign = AIXOffsetBaseAlign; + } + // The maximum field alignment overrides base align. if (!MaxFieldAlignment.isZero()) { BaseAlign = std::min(BaseAlign, MaxFieldAlignment); @@ -1248,7 +1269,7 @@ setSize(std::max(getSize(), Offset + Layout.getSize())); // Remember max struct/class alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, AIXOffsetBaseAlign); return Offset; } @@ -1829,22 +1850,54 @@ } } + // AIX ABI has this special rule that in aggregates, the first member of + // floating point data type(or aggregate type contains floating point data + // type) is aligned according to its natural alignment value, subsequent + // of such data type members of the aggregate are aligned on 4-byte + // boundaries. + CharUnits AIXOffsetFieldAlign = FieldAlign; + if (isAIXLayout(Context)) { + if (const BuiltinType *BTy = + Context.getBaseElementType(D->getType())->getAs()) { + if (BTy->isFloatingPoint()) { + AIXOffsetFieldAlign = CharUnits::fromQuantity(4); + } + } else if (const RecordType *RT = D->getType() + ->getBaseElementTypeUnsafe() + ->getAs()) { + if (const RecordDecl *RD = RT->getDecl()) { + const ASTRecordLayout &FieldRecord = Context.getASTRecordLayout(RD); + AIXOffsetFieldAlign = FieldRecord.getAIXOffsetAlignment(); + } + } + // Field offset could still be 0 if we have 0 size bitfield as first member + // So if field index is not 0 and the struct is not a union, we should use + // AIX special alignment as well. + if (FieldOffset != CharUnits::Zero() || + (D->getFieldIndex() != 0 && !IsUnion)) + FieldAlign = AIXOffsetFieldAlign; + } + // The align if the field is not packed. This is to check if the attribute // was unnecessary (-Wpacked). CharUnits UnpackedFieldAlign = FieldAlign; CharUnits UnpackedFieldOffset = FieldOffset; - if (FieldPacked) + if (FieldPacked) { FieldAlign = CharUnits::One(); + AIXOffsetFieldAlign = CharUnits::One(); + } CharUnits MaxAlignmentInChars = Context.toCharUnitsFromBits(D->getMaxAlignment()); FieldAlign = std::max(FieldAlign, MaxAlignmentInChars); UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars); + AIXOffsetFieldAlign = std::max(AIXOffsetFieldAlign, MaxAlignmentInChars); // The maximum field alignment overrides the aligned attribute. if (!MaxFieldAlignment.isZero()) { FieldAlign = std::min(FieldAlign, MaxFieldAlignment); UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment); + AIXOffsetFieldAlign = std::min(AIXOffsetFieldAlign, MaxFieldAlignment); } // Round up the current record size to the field's alignment boundary. @@ -1910,7 +1963,7 @@ // Remember max struct/class alignment. UnadjustedAlignment = std::max(UnadjustedAlignment, FieldAlign); - UpdateAlignment(FieldAlign, UnpackedFieldAlign); + UpdateAlignment(FieldAlign, UnpackedFieldAlign, AIXOffsetFieldAlign); } void ItaniumRecordLayoutBuilder::FinishLayout(const NamedDecl *D) { @@ -1981,7 +2034,8 @@ } void ItaniumRecordLayoutBuilder::UpdateAlignment( - CharUnits NewAlignment, CharUnits UnpackedNewAlignment) { + CharUnits NewAlignment, CharUnits UnpackedNewAlignment, + CharUnits AIXOffsetNewAlignment) { // The alignment is not modified when using 'mac68k' alignment or when // we have an externally-supplied layout that also provides overall alignment. if (IsMac68kAlign || (UseExternalLayout && !InferAlignment)) @@ -1998,6 +2052,12 @@ "Alignment not a power of 2"); UnpackedAlignment = UnpackedNewAlignment; } + + if (AIXOffsetNewAlignment > AIXOffsetAlignment) { + assert(llvm::isPowerOf2_64(AIXOffsetNewAlignment.getQuantity()) && + "Alignment not a power of 2"); + AIXOffsetAlignment = AIXOffsetNewAlignment; + } } uint64_t @@ -3049,6 +3109,7 @@ NewEntry = new (*this) ASTRecordLayout( *this, Builder.Size, Builder.Alignment, Builder.Alignment, Builder.RequiredAlignment, + /*AIXOffsetAlignment : used by AIX-ABI*/ Builder.Alignment, Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase, Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets, Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(), @@ -3060,6 +3121,7 @@ NewEntry = new (*this) ASTRecordLayout( *this, Builder.Size, Builder.Alignment, Builder.Alignment, Builder.RequiredAlignment, + /*AIXOffsetAlignment : used by AIX-ABI*/ Builder.Alignment, Builder.Size, Builder.FieldOffsets); } } else { @@ -3080,9 +3142,12 @@ CharUnits NonVirtualSize = skipTailPadding ? DataSize : Builder.NonVirtualSize; NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, - /*RequiredAlignment : used by MS-ABI)*/ - Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(), + *this, Builder.getSize(), Builder.Alignment, + Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI*/ + Builder.Alignment, + /*AIXOffsetAlignment : used by AIX-ABI*/ + Builder.AIXOffsetAlignment, Builder.HasOwnVFPtr, RD->isDynamicClass(), CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets, NonVirtualSize, Builder.NonVirtualAlignment, EmptySubobjects.SizeOfLargestEmptySubobject, Builder.PrimaryBase, @@ -3093,9 +3158,12 @@ Builder.Layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, - /*RequiredAlignment : used by MS-ABI)*/ - Builder.Alignment, Builder.getSize(), Builder.FieldOffsets); + *this, Builder.getSize(), Builder.Alignment, + Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI*/ + Builder.Alignment, + /*AIXOffsetAlignment : used by AIX-ABI*/ + Builder.AIXOffsetAlignment, Builder.getSize(), Builder.FieldOffsets); } } @@ -3245,14 +3313,12 @@ ItaniumRecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/nullptr); Builder.Layout(D); - const ASTRecordLayout *NewEntry = - new (*this) ASTRecordLayout(*this, Builder.getSize(), - Builder.Alignment, - Builder.UnadjustedAlignment, - /*RequiredAlignment : used by MS-ABI)*/ - Builder.Alignment, - Builder.getDataSize(), - Builder.FieldOffsets); + const ASTRecordLayout *NewEntry = new (*this) ASTRecordLayout( + *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI*/ + Builder.Alignment, + /*AIXOffsetAlignment : used by AIX-ABI*/ + Builder.Alignment, Builder.getDataSize(), Builder.FieldOffsets); ObjCLayouts[Key] = NewEntry; diff --git a/clang/test/Layout/aix-double-struct-member.cpp b/clang/test/Layout/aix-double-struct-member.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-double-struct-member.cpp @@ -0,0 +1,282 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | FileCheck %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | FileCheck %s + +namespace test1 { +// Test double alignment when it is/is not the first struct member. +struct D { + double d1; + int i1; +}; + +struct DoubleFirst { + struct D d2; + int i2; +}; + +struct IntFirst { + int i3; + struct D d3; +}; + +int a = sizeof(DoubleFirst); +int b = sizeof(IntFirst); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::D +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: 8 | int i1 +// CHECK-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK-NEXT: | nvsize=16, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::DoubleFirst +// CHECK-NEXT: 0 | struct test1::D d2 +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: 8 | int i1 +// CHECK-NEXT: 16 | int i2 +// CHECK-NEXT: | [sizeof=24, dsize=24, align=8, +// CHECK-NEXT: | nvsize=24, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::IntFirst +// CHECK-NEXT: 0 | int i3 +// CHECK-NEXT: 4 | struct test1::D d3 +// CHECK-NEXT: 4 | double d1 +// CHECK-NEXT: 12 | int i1 +// CHECK-NEXT: | [sizeof=20, dsize=20, align=4, +// CHECK-NEXT: | nvsize=20, nvalign=4] +}; // namespace test1 + +namespace test2 { +// Test AIX layout for zero sized bitfield followed by double. +struct Double { + int : 0; + double d; +}; + +int a = sizeof(Double); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::Double +// CHECK-NEXT: 0:- | int +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, +// CHECK-NEXT: | nvsize=8, nvalign=4] +}; // namespace test2 + +namespace test3 { +// Test the alignment of a double member in union. +union A { + int *b; + double d; +}; + +struct UnionStruct { + union A a; + int i; +}; + +int a = sizeof(UnionStruct); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | union test3::A +// CHECK-NEXT: 0 | int * b +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK-NEXT: | nvsize=8, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::UnionStruct +// CHECK-NEXT: 0 | union test3::A a +// CHECK-NEXT: 0 | int * b +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | int i +// CHECK-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK-NEXT: | nvsize=16, nvalign=8] +}; // namespace test3 + +namespace test4 { +// Test the AIX Alignment rule when layout base class. +struct A { + int a; +}; + +struct B { + double d; +}; + +class S : A, B { +}; + +int a = sizeof(S); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::A +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: | [sizeof=4, dsize=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::B +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK-NEXT: | nvsize=8, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | class test4::S +// CHECK-NEXT: 0 | struct test4::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test4::B (base) +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, +// CHECK-NEXT: | nvsize=12, nvalign=4] +}; // namespace test4 + +namespace test5 { +// Test the AIX alignment rule with basic inheritance. +struct Empty { +}; + +struct EmptyDer : Empty { + double d; +}; + +struct NonEmpty { + int i; +}; + +struct NonEmptyDer : NonEmpty { + double d; +}; + +int a = sizeof(EmptyDer); +int b = sizeof(NonEmptyDer); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test5::Empty (empty) +// CHECK-NEXT: | [sizeof=1, dsize=1, align=1, +// CHECK-NEXT: | nvsize=1, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test5::EmptyDer +// CHECK-NEXT: 0 | struct test5::Empty (base) (empty) +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK-NEXT: | nvsize=8, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test5::NonEmpty +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: | [sizeof=4, dsize=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test5::NonEmptyDer +// CHECK-NEXT: 0 | struct test5::NonEmpty (base) +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, +// CHECK-NEXT: | nvsize=12, nvalign=4] +}; // namespace test5 + +namespace test6 { +// Test how align attribute interacts with AIX alignment. +struct T { + char a; + double __attribute__((aligned(16))) d; + int i; +}; + +struct S { + double __attribute__((aligned(16))) d1; + char a; + double d2; +}; + +struct H { + double d1; + char a; + double d2; +}; + +int a = sizeof(T); +int b = sizeof(S); +int c = sizeof(H); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::T +// CHECK-NEXT: 0 | char a +// CHECK-NEXT: 16 | double d +// CHECK-NEXT: 24 | int i +// CHECK-NEXT: | [sizeof=32, dsize=32, align=16, +// CHECK-NEXT: | nvsize=32, nvalign=16] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::S +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: 8 | char a +// CHECK-NEXT: 12 | double d2 +// CHECK-NEXT: | [sizeof=32, dsize=32, align=16, +// CHECK-NEXT: | nvsize=32, nvalign=16] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::H +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: 8 | char a +// CHECK-NEXT: 12 | double d2 +// CHECK-NEXT: | [sizeof=24, dsize=24, align=8, +// CHECK-NEXT: | nvsize=24, nvalign=8] +}; //namespace test6 + +namespace test7 { +// Test how pack align interacts with AIX alignment. +struct S { + int i; + short j; + double k; +}; + +#pragma pack(2) +struct T { + int i; + short j; + double k; +}; + +struct H { + double d; + short j; + int i; +}; + +int a = sizeof(S); +int b = sizeof(T); +int c = sizeof(H); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test7::S +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | short j +// CHECK-NEXT: 8 | double k +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, +// CHECK-NEXT: | nvsize=16, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test7::T +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | short j +// CHECK-NEXT: 6 | double k +// CHECK-NEXT: | [sizeof=14, dsize=14, align=2, +// CHECK-NEXT: | nvsize=14, nvalign=2] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test7::H +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short j +// CHECK-NEXT: 10 | int i +// CHECK-NEXT: | [sizeof=14, dsize=14, align=2, +// CHECK-NEXT: | nvsize=14, nvalign=2] +}; // namespace test7 diff --git a/clang/test/Layout/aix-virtual-function-alignment.cpp b/clang/test/Layout/aix-virtual-function-alignment.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-virtual-function-alignment.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | FileCheck \ +// RUN: --check-prefix=CHECK32 %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | FileCheck \ +// RUN: --check-prefix=CHECK64 %s + +struct A { + virtual void boo() {} +}; + +struct B { + bool b; + A a; +}; + +int i = sizeof(B); + +// CHECK32: *** Dumping AST Record Layout +// CHECK32-NEXT: 0 | struct A +// CHECK32-NEXT: 0 | (A vtable pointer) +// CHECK32-NEXT: | [sizeof=4, dsize=4, align=4, +// CHECK32-NEXT: | nvsize=4, nvalign=4] + +// CHECK32: *** Dumping AST Record Layout +// CHECK32-NEXT: 0 | struct B +// CHECK32-NEXT: 0 | _Bool b +// CHECK32-NEXT: 4 | struct A a +// CHECK32-NEXT: 4 | (A vtable pointer) +// CHECK32-NEXT: | [sizeof=8, dsize=8, align=4, +// CHECK32-NEXT: | nvsize=8, nvalign=4] + +// CHECK64: *** Dumping AST Record Layout +// CHECK64-NEXT: 0 | struct A +// CHECK64-NEXT: 0 | (A vtable pointer) +// CHECK64-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK64-NEXT: | nvsize=8, nvalign=8] + +// CHECK64: *** Dumping AST Record Layout +// CHECK64-NEXT: 0 | struct B +// CHECK64-NEXT: 0 | _Bool b +// CHECK64-NEXT: 8 | struct A a +// CHECK64-NEXT: 8 | (A vtable pointer) +// CHECK64-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK64-NEXT: | nvsize=16, nvalign=8]