diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2111,6 +2111,14 @@ bool isAlignmentRequired(const Type *T) const; bool isAlignmentRequired(QualType T) const; + bool shouldCheckRecordFirstMember(CharUnits Alignment, + CharUnits MaxFieldAligment) const; + + /// hasFloatingPointAsFirstMember - Check if a RecordDecl has a double or a + /// long double as its first field. + bool hasFloatingPointAsFirstMember(const RecordDecl *RD, + const CharUnits MaxFieldAlignment) const; + /// Return the "preferred" alignment of the specified type \p T for /// the current target, in bits. /// 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 @@ -70,6 +70,9 @@ // Alignment - Alignment of record in characters. CharUnits Alignment; + /// The maximum allowed field alignment. This is set by #pragma pack. + CharUnits MaxFieldAlignment; + // UnadjustedAlignment - Maximum of the alignments of the record members in // characters. CharUnits UnadjustedAlignment; @@ -139,30 +142,25 @@ CXXRecordLayoutInfo *CXXInfo = nullptr; ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, - CharUnits unadjustedAlignment, + CharUnits maxFieldAlignment, CharUnits unadjustedAlignment, CharUnits requiredAlignment, 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 maxFieldAlignment, 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 *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 +173,10 @@ /// getAlignment - Get the record alignment in characters. CharUnits getAlignment() const { return Alignment; } + /// getMaxFieldAlignment - Get the maximum allowed field alignment. This is + /// set by #pragma pack. + CharUnits getMaxFieldAlignment() const { return MaxFieldAlignment; } + /// getUnadjustedAlignment - Get the record alignment in characters, before /// alignment adjustement. CharUnits getUnadjustedAlignment() const { return UnadjustedAlignment; } @@ -193,9 +195,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 +287,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/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2368,6 +2368,127 @@ return toCharUnitsFromBits(getTypeUnadjustedAlign(T)); } +bool ASTContext::shouldCheckRecordFirstMember( + CharUnits Alignment, CharUnits MaxFieldAligment) const { + if (Alignment != CharUnits::fromQuantity(4)) + // On AIX, the object which does not have 4-byte alignment is guaranteed to + // be aligned to the preferred alignement value. + return false; + + if (!MaxFieldAligment.isZero() && + MaxFieldAligment <= CharUnits::fromQuantity(4)) + // #pragma pack(n) will not affect the preferred alignment on AIX if n is + // no more than 4. + return false; + + return true; +} + +/// hasFloatingPointAsFirstMember - For Itanium ABI, check if a RecordDecl has a +/// double or a long double as its first field. +bool ASTContext::hasFloatingPointAsFirstMember( + const RecordDecl *RD, const CharUnits MaxFieldAlignment) const { + auto CXXRD = dyn_cast(RD); + + if (CXXRD) { + // Check if the RecordDecl has a Vtable pointer as the first member. + if (CXXRD->isDynamicClass()) { + return false; + } + + // Get the first non-empty non-virtual base class if any. + CXXRecordDecl *Base = nullptr; + for (const CXXBaseSpecifier &B : CXXRD->bases()) { + CXXRecordDecl *Tmp = B.getType()->getAsCXXRecordDecl(); + if (!Tmp->isEmpty() && !B.isVirtual()) { + Base = Tmp; + break; + } + } + + if (Base != nullptr) { + const ASTRecordLayout &Layout = getASTRecordLayout(Base); + return hasFloatingPointAsFirstMember(Base, Layout.getMaxFieldAlignment()); + } + } + + if (RD->isUnion()) { + // On AIX, double/long double should be always naturally aligned to 8 in a + // Union. + bool flag = false; + for (RecordDecl::field_iterator FI = RD->field_begin(), E = RD->field_end(); + FI != E; ++FI) { + const FieldDecl *Field = *FI; + if (const BuiltinType *BTy = + getBaseElementType(Field->getType())->getAs()) { + if (BTy->isSpecificBuiltinType(BuiltinType::LongDouble) || + BTy->isSpecificBuiltinType(BuiltinType::Double)) + flag = flag || true; + } else if (auto RT = Field->getType()->getAs()) { + // Recursively check fields of a record type. + const RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &Layout = getASTRecordLayout(RD); + flag = flag || + hasFloatingPointAsFirstMember(RD, Layout.getMaxFieldAlignment()); + } + + if (flag) { + // We verified that the first member is a double/long double. + if ((MaxFieldAlignment.isZero() || + MaxFieldAlignment >= CharUnits::fromQuantity(8)) && + toCharUnitsFromBits(Field->getMaxAlignment()) <= + CharUnits::fromQuantity(4)) + return true; + + return false; + } + } + return false; + } + + // Check the real first field. + const FieldDecl *Field = nullptr; + for (RecordDecl::field_iterator FI = RD->field_begin(), E = RD->field_end(); + FI != E; ++FI) { + const FieldDecl *D = *FI; + auto *FieldClass = D->getType()->getAsCXXRecordDecl(); + bool PotentiallyOverlapping = + D->hasAttr() && FieldClass; + if (PotentiallyOverlapping && FieldClass->isEmpty()) { + // An empty CXXRecordDecl with [[no_unique_address]] attribute will not + // affect the double/long double alignment. + continue; + } + Field = D; + break; + } + + if (Field == nullptr) + return false; + + if (const BuiltinType *BTy = + getBaseElementType(Field->getType())->getAs()) { + if (BTy->isSpecificBuiltinType(BuiltinType::LongDouble) || + BTy->isSpecificBuiltinType(BuiltinType::Double)) + if ((MaxFieldAlignment.isZero() || + MaxFieldAlignment >= CharUnits::fromQuantity(8)) && + toCharUnitsFromBits(Field->getMaxAlignment()) <= + CharUnits::fromQuantity(4)) + return true; + + return false; + } + + // Recursively check fields of record type. + if (auto RT = Field->getType()->getAs()) { + const RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &Layout = getASTRecordLayout(RD); + return hasFloatingPointAsFirstMember(RD, Layout.getMaxFieldAlignment()); + } + + return false; +} + /// getPreferredTypeAlign - Return the "preferred" alignment of the specified /// type for the current target in bits. This can be different than the ABI /// alignment in cases where it is beneficial for performance to overalign @@ -2392,12 +2513,25 @@ T = ET->getDecl()->getIntegerType().getTypePtr(); if (T->isSpecificBuiltinType(BuiltinType::Double) || T->isSpecificBuiltinType(BuiltinType::LongLong) || - T->isSpecificBuiltinType(BuiltinType::ULongLong)) + T->isSpecificBuiltinType(BuiltinType::ULongLong) || + (T->isSpecificBuiltinType(BuiltinType::LongDouble) && + getTargetInfo().getTriple().isOSAIX())) // Don't increase the alignment if an alignment attribute was specified on a // typedef declaration. if (!TI.AlignIsRequired) return std::max(ABIAlign, (unsigned)getTypeSize(T)); + if (const auto *RT = T->getAs()) + // On AIX, upgrade the preferred alignment value if thie Record has a double + // as its first member. + if (getTargetInfo().getTriple().isOSAIX()) { + const RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &Layout = getASTRecordLayout(RD); + CharUnits MaxFieldAlignment = Layout.MaxFieldAlignment; + if (shouldCheckRecordFirstMember(Layout.Alignment, MaxFieldAlignment) && + hasFloatingPointAsFirstMember(RD, MaxFieldAlignment)) + return std::max(ABIAlign, (unsigned)toBits(CharUnits::fromQuantity(8))); + } return ABIAlign; } 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 @@ -29,39 +29,34 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits maxFieldAlignment, CharUnits unadjustedAlignment, CharUnits requiredAlignment, CharUnits datasize, ArrayRef fieldoffsets) : Size(size), DataSize(datasize), Alignment(alignment), + MaxFieldAlignment(maxFieldAlignment), UnadjustedAlignment(unadjustedAlignment), RequiredAlignment(requiredAlignment) { 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 maxFieldAlignment, 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), + MaxFieldAlignment(maxFieldAlignment), + UnadjustedAlignment(unadjustedAlignment), + RequiredAlignment(requiredAlignment), + 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" @@ -806,6 +806,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() && @@ -1936,8 +1940,23 @@ uint64_t UnpaddedSize = getSizeInBits() - UnfilledBitsInLastUnit; uint64_t UnpackedSizeInBits = llvm::alignTo(getSizeInBits(), Context.toBits(UnpackedAlignment)); - uint64_t RoundedSize = - llvm::alignTo(getSizeInBits(), Context.toBits(Alignment)); + + uint64_t AlignValue = Context.toBits(Alignment); + if (isAIXLayout(Context)) + if (const RecordDecl *RD = dyn_cast(D)) { + if (Context.shouldCheckRecordFirstMember(Alignment, MaxFieldAlignment)) + // 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. + AlignValue = + Context.hasFloatingPointAsFirstMember(RD, MaxFieldAlignment) + ? Context.toBits(CharUnits::fromQuantity(8)) + : AlignValue; + } + + uint64_t RoundedSize = llvm::alignTo(getSizeInBits(), AlignValue); if (UseExternalLayout) { // If we're inferring alignment, and the external size is smaller than @@ -3047,20 +3066,19 @@ if (const auto *RD = dyn_cast(D)) { Builder.cxxLayout(RD); NewEntry = new (*this) ASTRecordLayout( - *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(), - Builder.PrimaryBase, false, Builder.SharedVBPtrBase, - Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase, - Builder.Bases, Builder.VBases); + *this, Builder.Size, Builder.Alignment, Builder.MaxFieldAlignment, + Builder.Alignment, Builder.RequiredAlignment, Builder.HasOwnVFPtr, + Builder.HasOwnVFPtr || Builder.PrimaryBase, Builder.VBPtrOffset, + Builder.DataSize, Builder.FieldOffsets, Builder.NonVirtualSize, + Builder.Alignment, CharUnits::Zero(), Builder.PrimaryBase, false, + Builder.SharedVBPtrBase, Builder.EndsWithZeroSizedObject, + Builder.LeadsWithZeroSizedBase, Builder.Bases, Builder.VBases); } else { Builder.layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.Size, Builder.Alignment, Builder.Alignment, - Builder.RequiredAlignment, - Builder.Size, Builder.FieldOffsets); + *this, Builder.Size, Builder.Alignment, Builder.MaxFieldAlignment, + Builder.Alignment, Builder.RequiredAlignment, Builder.Size, + Builder.FieldOffsets); } } else { if (const auto *RD = dyn_cast(D)) { @@ -3079,8 +3097,9 @@ skipTailPadding ? Builder.getSize() : Builder.getDataSize(); CharUnits NonVirtualSize = skipTailPadding ? DataSize : Builder.NonVirtualSize; - NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, + NewEntry = new (*this) ASTRecordLayout( + *this, Builder.getSize(), Builder.Alignment, + Builder.MaxFieldAlignment, Builder.UnadjustedAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(), CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets, @@ -3093,7 +3112,8 @@ Builder.Layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, + *this, Builder.getSize(), Builder.Alignment, + Builder.MaxFieldAlignment, Builder.UnadjustedAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.getSize(), Builder.FieldOffsets); } @@ -3245,14 +3265,11 @@ 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.MaxFieldAlignment, + Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI)*/ + Builder.Alignment, Builder.getDataSize(), Builder.FieldOffsets); ObjCLayouts[Key] = NewEntry; @@ -3414,23 +3431,33 @@ OS << "[sizeof=" << Layout.getSize().getQuantity(); if (CXXRD && !isMsLayout(C)) OS << ", dsize=" << Layout.getDataSize().getQuantity(); - OS << ", align=" << Layout.getAlignment().getQuantity(); + OS << ", align=" + << (!isAIXLayout(C) ? Layout.getAlignment().getQuantity() + : C.toCharUnitsFromBits( + C.getPreferredTypeAlign(RD->getTypeForDecl())) + .getQuantity()); if (CXXRD) { OS << ",\n"; PrintIndentNoOffset(OS, IndentLevel - 1); OS << " nvsize=" << Layout.getNonVirtualSize().getQuantity(); - OS << ", nvalign=" << Layout.getNonVirtualAlignment().getQuantity(); + + CharUnits nvalign = Layout.getNonVirtualAlignment(); + CharUnits MaxFieldAlignment = Layout.getMaxFieldAlignment(); + if (isAIXLayout(C) && + C.shouldCheckRecordFirstMember(nvalign, MaxFieldAlignment)) + if (C.hasFloatingPointAsFirstMember(RD, MaxFieldAlignment)) + nvalign = CharUnits::fromQuantity(8); + OS << ", nvalign=" << nvalign.getQuantity(); } OS << "]\n"; } -void ASTContext::DumpRecordLayout(const RecordDecl *RD, - raw_ostream &OS, +void ASTContext::DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS, bool Simple) const { if (!Simple) { ::DumpRecordLayout(OS, RD, *this, CharUnits(), 0, nullptr, - /*PrintSizeInfo*/true, + /*PrintSizeInfo*/ true, /*IncludeVirtualBases=*/true); return; } @@ -3449,10 +3476,14 @@ OS << " Size:" << toBits(Info.getSize()) << "\n"; if (!isMsLayout(*this)) OS << " DataSize:" << toBits(Info.getDataSize()) << "\n"; - OS << " Alignment:" << toBits(Info.getAlignment()) << "\n"; + OS << " Alignment:" + << (!isAIXLayout(*this) ? toBits(Info.getAlignment()) + : getPreferredTypeAlign(RD->getTypeForDecl())) + << "\n"; OS << " FieldOffsets: ["; for (unsigned i = 0, e = Info.getFieldCount(); i != e; ++i) { - if (i) OS << ", "; + if (i) + OS << ", "; OS << Info.getFieldOffset(i); } OS << "]>\n"; diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h --- a/clang/lib/Basic/Targets/PPC.h +++ b/clang/lib/Basic/Targets/PPC.h @@ -361,11 +361,17 @@ } if (Triple.isOSFreeBSD() || Triple.isOSNetBSD() || Triple.isOSOpenBSD() || - Triple.getOS() == llvm::Triple::AIX || Triple.isMusl()) { + Triple.isMusl()) { LongDoubleWidth = LongDoubleAlign = 64; LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } + if (Triple.isOSAIX()) { + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); + } + // PPC32 supports atomics up to 4 bytes. MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32; } @@ -397,12 +403,17 @@ if (Triple.getOS() == llvm::Triple::AIX) SuitableAlign = 64; - if (Triple.isOSFreeBSD() || Triple.getOS() == llvm::Triple::AIX || - Triple.isMusl()) { + if (Triple.isOSFreeBSD() || Triple.isMusl()) { LongDoubleWidth = LongDoubleAlign = 64; LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } + if (Triple.isOSAIX()) { + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); + } + // PPC64 supports atomics up to 8 bytes. MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; } 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,295 @@ +// 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 #pragma pack and align attribute interacts with AIX alignment. +struct A { + char a; + double __attribute__((aligned(16))) d; + int i; +}; + +struct B { + double __attribute__((aligned(4))) d1; + char a; + double d2; +}; + +#pragma pack(2) +struct C { + int i; + short j; + double k; +}; + +#pragma pack(2) +struct D { + double d; + short j; + int i; +}; + +#pragma pack(8) +struct E { + double __attribute__((aligned(4))) d; + short s; +}; + +#pragma pack(4) +struct F : public D { + double d; +}; + +#pragma pack(2) +struct G : public E { + int i; +}; + +int a = sizeof(A); +int b = sizeof(B); +int c = sizeof(C); +int d = sizeof(D); +int e = sizeof(E); +int f = sizeof(F); +int g = sizeof(G); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::A +// 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::B +// 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] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::C +// 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 test6::D +// 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] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::E +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short s +// CHECK-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK-NEXT: | nvsize=16, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::F +// CHECK-NEXT: 0 | struct test6::D (base) +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short j +// CHECK-NEXT: 10 | int i +// CHECK-NEXT: 16 | double d +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, +// CHECK-NEXT: | nvsize=24, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::G +// CHECK-NEXT: 0 | struct test6::E (base) +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short s +// CHECK-NEXT: 16 | int i +// CHECK-NEXT: | [sizeof=20, dsize=20, align=2, +// CHECK-NEXT: | nvsize=20, nvalign=2] + +}; // namespace test6 diff --git a/clang/test/Layout/aix-no-unique-address-with-double.cpp b/clang/test/Layout/aix-no-unique-address-with-double.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-no-unique-address-with-double.cpp @@ -0,0 +1,127 @@ +// 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 + +struct Empty {}; + +struct A { + double d; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct 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 A +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK-NEXT: | nvsize=8, nvalign=8] + +struct B { + ~B(); + + Empty emp; + A a; + char c; +}; + +struct B1 { + [[no_unique_address]] B b; + char ext[7]; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct B +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 4 | struct A a +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: 12 | char c +// CHECK-NEXT: | [sizeof=16, dsize=13, align=4, +// CHECK-NEXT: | nvsize=13, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct B1 +// CHECK-NEXT: 0 | struct B b +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 4 | struct A a +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: 12 | char c +// CHECK-NEXT: 13 | char [7] ext +// CHECK-NEXT: | [sizeof=20, dsize=20, align=4, +// CHECK-NEXT: | nvsize=20, nvalign=4] + +struct C { + ~C(); + + [[no_unique_address]] Empty emp; + A a; + char c; +}; + +struct C1 { + [[no_unique_address]] C c; + char ext[7]; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct C +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 0 | struct A a +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | char c +// CHECK-NEXT: | [sizeof=16, dsize=9, align=8, +// CHECK-NEXT: | nvsize=9, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct C1 +// CHECK-NEXT: 0 | struct C c +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 0 | struct A a +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | char c +// CHECK-NEXT: 9 | char [7] ext +// CHECK-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK-NEXT: | nvsize=16, nvalign=8] + +struct D { + ~D(); + + [[no_unique_address]] char notEmp; + A a; + char c; +}; + +struct D1 { + [[no_unique_address]] D d; + char ext[7]; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct D +// CHECK-NEXT: 0 | char notEmp +// CHECK-NEXT: 4 | struct A a +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: 12 | char c +// CHECK-NEXT: | [sizeof=16, dsize=13, align=4, +// CHECK-NEXT: | nvsize=13, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct D1 +// CHECK-NEXT: 0 | struct D d +// CHECK-NEXT: 0 | char notEmp +// CHECK-NEXT: 4 | struct A a +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: 12 | char c +// CHECK-NEXT: 13 | char [7] ext +// CHECK-NEXT: | [sizeof=20, dsize=20, align=4, +// CHECK-NEXT: | nvsize=20, nvalign=4] + +int a = sizeof(Empty); +int b = sizeof(A); +int c = sizeof(B1); +int d = sizeof(C1); +int e = sizeof(D1); diff --git a/clang/test/Layout/aix-virtual-function-and-base-with-double.cpp b/clang/test/Layout/aix-virtual-function-and-base-with-double.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-virtual-function-and-base-with-double.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | FileCheck \ +// RUN: --check-prefixes=CHECK,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-prefixes=CHECK,CHECK64 %s + +namespace test1 { +struct A { + double d1; + virtual void boo() {} +}; + +struct B { + double d2; + A a; +}; + +struct C : public A { + double d3; +}; + +int i = sizeof(B); +int j = sizeof(C); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::A +// CHECK-NEXT: 0 | (A vtable pointer) +// CHECK32-NEXT: 4 | double d1 +// CHECK32-NEXT: | [sizeof=12, dsize=12, align=4, +// CHECK32-NEXT: | nvsize=12, nvalign=4] +// CHECK64-NEXT: 8 | double d1 +// CHECK64-NEXT: | [sizeof=16, dsize=16, align=8, +// CHECK64-NEXT: | nvsize=16, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::B +// CHECK-NEXT: 0 | double d2 +// CHECK-NEXT: 8 | struct test1::A a +// CHECK-NEXT: 8 | (A vtable pointer) +// CHECK32-NEXT: 12 | double d1 +// CHECK32-NEXT: | [sizeof=24, dsize=20, align=8, +// CHECK32-NEXT: | nvsize=20, nvalign=8] +// CHECK64-NEXT: 16 | double d1 +// CHECK64-NEXT: | [sizeof=24, dsize=24, align=8, +// CHECK64-NEXT: | nvsize=24, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::C +// CHECK-NEXT: 0 | struct test1::A (primary base) +// CHECK-NEXT: 0 | (A vtable pointer) +// CHECK32-NEXT: 4 | double d1 +// CHECK32-NEXT: 12 | double d3 +// CHECK32-NEXT: | [sizeof=20, dsize=20, align=4, +// CHECK32-NEXT: | nvsize=20, nvalign=4] +// CHECK64-NEXT: 8 | double d1 +// CHECK64-NEXT: 16 | double d3 +// CHECK64-NEXT: | [sizeof=24, dsize=24, align=8, +// CHECK64-NEXT: | nvsize=24, nvalign=8] + +}; // namespace test1 + +namespace test2 { +struct A { + long long l1; +}; + +struct B : public virtual A { + double d2; +}; + +#pragma pack(2) +struct C : public virtual A { + double __attribute__((aligned(4))) d3; +}; + +int i = sizeof(B); +int j = sizeof(C); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::A +// CHECK-NEXT: 0 | long long l1 +// CHECK-NEXT: | [sizeof=8, dsize=8, align=8, +// CHECK-NEXT: | nvsize=8, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::B +// CHECK-NEXT: 0 | (B vtable pointer) +// CHECK32-NEXT: 4 | double d2 +// CHECK64-NEXT: 8 | double d2 +// CHECK-NEXT: 16 | struct test2::A (virtual base) +// CHECK-NEXT: 16 | long long l1 +// CHECK-NEXT: | [sizeof=24, dsize=24, align=8, +// CHECK32-NEXT: | nvsize=12, nvalign=4] +// CHECK64-NEXT: | nvsize=16, nvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::C +// CHECK-NEXT: 0 | (C vtable pointer) +// CHECK32-NEXT: 4 | double d3 +// CHECK32-NEXT: 12 | struct test2::A (virtual base) +// CHECK32-NEXT: 12 | long long l1 +// CHECK32-NEXT: | [sizeof=20, dsize=20, align=2, +// CHECK32-NEXT: | nvsize=12, nvalign=2] +// CHECK64-NEXT: 8 | double d3 +// CHECK64-NEXT: 16 | struct test2::A (virtual base) +// CHECK64-NEXT: 16 | long long l1 +// CHECK64-NEXT: | [sizeof=24, dsize=24, align=2, +// CHECK64-NEXT: | nvsize=16, nvalign=2] + +}; // namespace test2