Index: clang/include/clang/AST/RecordLayout.h =================================================================== --- clang/include/clang/AST/RecordLayout.h +++ clang/include/clang/AST/RecordLayout.h @@ -70,6 +70,11 @@ // Alignment - Alignment of record in characters. CharUnits Alignment; + // PreferredAlignment - Preferred alignment of record in characters. This + // can be different than Alignment in cases where it is beneficial for + // performance or backwards compatibility perserving (e.g. AIX-ABI). + CharUnits PreferredAlignment; + // UnadjustedAlignment - Maximum of the alignments of the record members in // characters. CharUnits UnadjustedAlignment; @@ -91,6 +96,11 @@ /// which is the alignment of the object without virtual bases. CharUnits NonVirtualAlignment; + /// PreferredNVAlignment - The preferred non-virtual alignment (in chars) of + /// an object, which is the preferred alignment of the object without + /// virtual bases. + CharUnits PreferredNVAlignment; + /// SizeOfLargestEmptySubobject - The size of the largest empty subobject /// (either a base or a member). Will be zero if the class doesn't contain /// any empty subobjects. @@ -139,30 +149,26 @@ CXXRecordLayoutInfo *CXXInfo = nullptr; ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, - CharUnits unadjustedAlignment, + CharUnits preferredAlignment, 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 preferredAlignment, CharUnits unadjustedAlignment, + CharUnits requiredAlignment, bool hasOwnVFPtr, + bool hasExtendableVFPtr, CharUnits vbptroffset, + CharUnits datasize, ArrayRef fieldoffsets, CharUnits nonvirtualsize, CharUnits nonvirtualalignment, + CharUnits preferrednvalignment, 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 +181,10 @@ /// getAlignment - Get the record alignment in characters. CharUnits getAlignment() const { return Alignment; } + /// getPreferredFieldAlignment - Get the record preferred alignment in + /// characters. + CharUnits getPreferredAlignment() const { return PreferredAlignment; } + /// getUnadjustedAlignment - Get the record alignment in characters, before /// alignment adjustement. CharUnits getUnadjustedAlignment() const { return UnadjustedAlignment; } @@ -193,9 +203,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. @@ -205,14 +213,23 @@ return CXXInfo->NonVirtualSize; } - /// getNonVirtualSize - Get the non-virtual alignment (in chars) of an object, - /// which is the alignment of the object without virtual bases. + /// getNonVirtualAlignment - Get the non-virtual alignment (in chars) of an + /// object, which is the alignment of the object without virtual bases. CharUnits getNonVirtualAlignment() const { assert(CXXInfo && "Record layout does not have C++ specific info!"); return CXXInfo->NonVirtualAlignment; } + /// getPreferredNVAlignment - Get the preferred non-virtual alignment (in + /// chars) of an object, which is the preferred alignment of the object + /// without virtual bases. + CharUnits getPreferredNVAlignment() const { + assert(CXXInfo && "Record layout does not have C++ specific info!"); + + return CXXInfo->PreferredNVAlignment; + } + /// getPrimaryBase - Get the primary base for this record. const CXXRecordDecl *getPrimaryBase() const { assert(CXXInfo && "Record layout does not have C++ specific info!"); @@ -287,9 +304,7 @@ return !CXXInfo->VBPtrOffset.isNegative(); } - CharUnits getRequiredAlignment() const { - return RequiredAlignment; - } + CharUnits getRequiredAlignment() const { return RequiredAlignment; } bool endsWithZeroSizedObject() const { return CXXInfo && CXXInfo->EndsWithZeroSizedObject; Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ clang/include/clang/Basic/TargetInfo.h @@ -1397,6 +1397,9 @@ /// Whether target allows to overalign ABI-specified preferred alignment virtual bool allowsLargerPreferedTypeAlignment() const { return true; } + /// Whether target supports the special `power` alignment rules of AIX. + virtual bool supportsAIXPowerAlignment() const { return false; } + /// Set supported OpenCL extensions and optional core features. virtual void setSupportedOpenCLOpts() {} Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -2392,8 +2392,8 @@ /// 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 -/// a data type. +/// alignment in cases where it is beneficial for performance or backwards +/// compatibility perserving to overalign a data type. unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { TypeInfo TI = getTypeInfo(T); unsigned ABIAlign = TI.Align; @@ -2404,17 +2404,26 @@ if (T->isMemberPointerType()) return getPreferredTypeAlign(getPointerDiffType().getTypePtr()); + if (const auto *RT = T->getAs()) { + const RecordDecl *RD = RT->getDecl(); + return std::max(ABIAlign, static_cast(toBits( + getASTRecordLayout(RD).PreferredAlignment))); + } + if (!Target->allowsLargerPreferedTypeAlignment()) return ABIAlign; - // Double and long long should be naturally aligned if possible. + // Double, long double (only when the target supports AIX power alignment) and + // long long should be naturally aligned if possible. if (const auto *CT = T->getAs()) T = CT->getElementType().getTypePtr(); if (const auto *ET = T->getAs()) 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) && + Target->supportsAIXPowerAlignment())) // Don't increase the alignment if an alignment attribute was specified on a // typedef declaration. if (!TI.AlignIsRequired) Index: clang/lib/AST/RecordLayout.cpp =================================================================== --- clang/lib/AST/RecordLayout.cpp +++ clang/lib/AST/RecordLayout.cpp @@ -29,45 +29,42 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, + CharUnits preferredAlignment, CharUnits unadjustedAlignment, CharUnits requiredAlignment, CharUnits datasize, ArrayRef fieldoffsets) : Size(size), DataSize(datasize), Alignment(alignment), + PreferredAlignment(preferredAlignment), 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 preferredAlignment, CharUnits unadjustedAlignment, + CharUnits requiredAlignment, bool hasOwnVFPtr, bool hasExtendableVFPtr, + CharUnits vbptroffset, CharUnits datasize, ArrayRef fieldoffsets, + CharUnits nonvirtualsize, CharUnits nonvirtualalignment, + CharUnits preferrednvalignment, 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), + PreferredAlignment(preferredAlignment), + UnadjustedAlignment(unadjustedAlignment), + RequiredAlignment(requiredAlignment), + CXXInfo(new (Ctx) CXXRecordLayoutInfo) { FieldOffsets.append(Ctx, fieldoffsets.begin(), fieldoffsets.end()); CXXInfo->PrimaryBase.setPointer(PrimaryBase); CXXInfo->PrimaryBase.setInt(IsPrimaryBaseVirtual); CXXInfo->NonVirtualSize = nonvirtualsize; CXXInfo->NonVirtualAlignment = nonvirtualalignment; + CXXInfo->PreferredNVAlignment = preferrednvalignment; CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject; CXXInfo->BaseOffsets = BaseOffsets; CXXInfo->VBaseOffsets = VBaseOffsets; Index: clang/lib/AST/RecordLayoutBuilder.cpp =================================================================== --- clang/lib/AST/RecordLayoutBuilder.cpp +++ 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" @@ -588,6 +588,9 @@ /// Alignment - The current alignment of the record layout. CharUnits Alignment; + /// PreferredAlignment - The preferred alignment of the record layout. + CharUnits PreferredAlignment; + /// The alignment if attribute packed is not used. CharUnits UnpackedAlignment; @@ -631,6 +634,7 @@ CharUnits NonVirtualSize; CharUnits NonVirtualAlignment; + CharUnits PreferredNVAlignment; /// If we've laid out a field but not included its tail padding in Size yet, /// this is the size up to the end of that field. @@ -651,6 +655,12 @@ /// the flag of field offset changing due to packed attribute. bool HasPackedField; + /// HandledFirstNonOverlappingEmptyField - An auxiliary field used for AIX. + /// When there are OverlappingEmptyFields existing in the aggregate, the + /// flag shows if the following first non-overlappingEmptyField has been + /// handled, if any. + bool HandledFirstNonOverlappingEmptyField; + typedef llvm::DenseMap BaseOffsetsMapTy; /// Bases - base classes and their offsets in the record. @@ -677,17 +687,19 @@ ItaniumRecordLayoutBuilder(const ASTContext &Context, EmptySubobjectMap *EmptySubobjects) : 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()), + Alignment(CharUnits::One()), PreferredAlignment(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()), NonVirtualAlignment(CharUnits::One()), + PreferredNVAlignment(CharUnits::One()), PaddedFieldSize(CharUnits::Zero()), PrimaryBase(nullptr), - PrimaryBaseIsVirtual(false), HasOwnVFPtr(false), - HasPackedField(false), FirstNearlyEmptyVBase(nullptr) {} + PrimaryBaseIsVirtual(false), HasOwnVFPtr(false), HasPackedField(false), + HandledFirstNonOverlappingEmptyField(false), + FirstNearlyEmptyVBase(nullptr) {} void Layout(const RecordDecl *D); void Layout(const CXXRecordDecl *D); @@ -762,9 +774,13 @@ /// alignment. void FinishLayout(const NamedDecl *D); - void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment); + void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment, + CharUnits PreferredAlignment); + void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment) { + UpdateAlignment(NewAlignment, UnpackedNewAlignment, NewAlignment); + } void UpdateAlignment(CharUnits NewAlignment) { - UpdateAlignment(NewAlignment, NewAlignment); + UpdateAlignment(NewAlignment, NewAlignment, NewAlignment); } /// Retrieve the externally-supplied field offset for the given @@ -997,7 +1013,7 @@ setSize(getSize().alignTo(BaseAlign)); // Update the alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, BaseAlign); } void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases( @@ -1179,8 +1195,6 @@ CharUnits ItaniumRecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class); - - CharUnits Offset; // Query the external layout to see if it provides an offset. @@ -1193,29 +1207,42 @@ HasExternalLayout = External.getExternalVBaseOffset(Base->Class, Offset); } + auto getBaseOrPreferredAlign = [&](CharUnits UnpackedAlign) { + return (Packed && ((Context.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver6) || + Context.getTargetInfo().getTriple().isPS4())) + ? CharUnits::One() + : UnpackedAlign; + }; + // 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 BaseAlign = - (Packed && ((Context.getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver6) || - Context.getTargetInfo().getTriple().isPS4())) - ? CharUnits::One() - : UnpackedBaseAlign; + CharUnits UnpackedPreferredAlign = Layout.getPreferredNVAlignment(); + CharUnits BaseAlign = getBaseOrPreferredAlign(UnpackedBaseAlign); + CharUnits PreferredAlign = getBaseOrPreferredAlign(UnpackedPreferredAlign); // If we have an empty base class, try to place it at offset 0. if (Base->Class->isEmpty() && (!HasExternalLayout || Offset == CharUnits::Zero()) && EmptySubobjects->CanPlaceBaseAtOffset(Base, CharUnits::Zero())) { setSize(std::max(getSize(), Layout.getSize())); - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, PreferredAlign); return CharUnits::Zero(); } + // Do not use AIX special alignment if current base is not the first member or + // the struct is not a union. + if (Context.getTargetInfo().supportsAIXPowerAlignment() && + !(getDataSize().isZero() || IsUnion)) { + PreferredAlign = BaseAlign; + } + // The maximum field alignment overrides base align. if (!MaxFieldAlignment.isZero()) { BaseAlign = std::min(BaseAlign, MaxFieldAlignment); + PreferredAlign = std::min(PreferredAlign, MaxFieldAlignment); UnpackedBaseAlign = std::min(UnpackedBaseAlign, MaxFieldAlignment); } @@ -1248,7 +1275,7 @@ setSize(std::max(getSize(), Offset + Layout.getSize())); // Remember max struct/class alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, PreferredAlign); return Offset; } @@ -1321,6 +1348,7 @@ NonVirtualSize = Context.toCharUnitsFromBits( llvm::alignTo(getSizeInBits(), Context.getTargetInfo().getCharAlign())); NonVirtualAlignment = Alignment; + PreferredNVAlignment = PreferredAlignment; // Lay out the virtual bases and add the primary virtual base offsets. LayoutVirtualBases(RD, RD); @@ -1733,25 +1761,36 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, bool InsertExtraPadding) { + auto *FieldClass = D->getType()->getAsCXXRecordDecl(); + bool PotentiallyOverlapping = D->hasAttr() && FieldClass; + bool IsOverlappingEmptyField = + PotentiallyOverlapping && FieldClass->isEmpty(); + + bool FoundNonOverlappingEmptyField = false; + bool SupportsAIXPowerAlignment = + Context.getTargetInfo().supportsAIXPowerAlignment(); + if (SupportsAIXPowerAlignment && !HandledFirstNonOverlappingEmptyField && + !IsOverlappingEmptyField) + FoundNonOverlappingEmptyField = true; + if (D->isBitField()) { + if (FoundNonOverlappingEmptyField) + HandledFirstNonOverlappingEmptyField = true; + LayoutBitField(D); return; } uint64_t UnpaddedFieldOffset = getDataSizeInBits() - UnfilledBitsInLastUnit; - // Reset the unfilled bits. UnfilledBitsInLastUnit = 0; LastBitfieldTypeSize = 0; - auto *FieldClass = D->getType()->getAsCXXRecordDecl(); - bool PotentiallyOverlapping = D->hasAttr() && FieldClass; - bool IsOverlappingEmptyField = PotentiallyOverlapping && FieldClass->isEmpty(); bool FieldPacked = Packed || D->hasAttr(); - CharUnits FieldOffset = (IsUnion || IsOverlappingEmptyField) - ? CharUnits::Zero() - : getDataSize(); + CharUnits FieldOffset = + (IsUnion || IsOverlappingEmptyField) ? CharUnits::Zero() : getDataSize(); + CharUnits FieldSize; CharUnits FieldAlign; // The amount of this class's dsize occupied by the field. @@ -1829,21 +1868,61 @@ } } + // 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 PreferredAlign = FieldAlign; + if (SupportsAIXPowerAlignment && FieldOffset == CharUnits::Zero() && + (IsUnion || FoundNonOverlappingEmptyField)) { + HandledFirstNonOverlappingEmptyField = true; + + if (const ComplexType *CTy = + Context.getBaseElementType(D->getType())->getAs()) { + if (const BuiltinType *BTy = + Context.getBaseElementType(CTy->getElementType()) + ->getAs()) + if (BTy->getKind() == BuiltinType::Double || + BTy->getKind() == BuiltinType::LongDouble) { + PreferredAlign = CharUnits::fromQuantity(8); + } + } else if (const BuiltinType *BTy = + Context.getBaseElementType(D->getType()) + ->getAs()) { + if (BTy->getKind() == BuiltinType::Double || + BTy->getKind() == BuiltinType::LongDouble) { + PreferredAlign = CharUnits::fromQuantity(8); + } + } else if (const RecordType *RT = D->getType() + ->getBaseElementTypeUnsafe() + ->getAs()) { + if (const RecordDecl *RD = RT->getDecl()) { + const ASTRecordLayout &FieldRecord = Context.getASTRecordLayout(RD); + PreferredAlign = FieldRecord.getPreferredAlignment(); + } + } + } + // 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(); + PreferredAlign = CharUnits::One(); + } CharUnits MaxAlignmentInChars = Context.toCharUnitsFromBits(D->getMaxAlignment()); FieldAlign = std::max(FieldAlign, MaxAlignmentInChars); + PreferredAlign = std::max(PreferredAlign, MaxAlignmentInChars); UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars); // The maximum field alignment overrides the aligned attribute. if (!MaxFieldAlignment.isZero()) { FieldAlign = std::min(FieldAlign, MaxFieldAlignment); + PreferredAlign = std::min(PreferredAlign, MaxFieldAlignment); UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment); } @@ -1910,7 +1989,7 @@ // Remember max struct/class alignment. UnadjustedAlignment = std::max(UnadjustedAlignment, FieldAlign); - UpdateAlignment(FieldAlign, UnpackedFieldAlign); + UpdateAlignment(FieldAlign, UnpackedFieldAlign, PreferredAlign); } void ItaniumRecordLayoutBuilder::FinishLayout(const NamedDecl *D) { @@ -1936,8 +2015,10 @@ 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 RoundedSize = llvm::alignTo( + getSizeInBits(), + Context.toBits(!Context.getTargetInfo().supportsAIXPowerAlignment() ? Alignment : PreferredAlignment)); if (UseExternalLayout) { // If we're inferring alignment, and the external size is smaller than @@ -1981,7 +2062,8 @@ } void ItaniumRecordLayoutBuilder::UpdateAlignment( - CharUnits NewAlignment, CharUnits UnpackedNewAlignment) { + CharUnits NewAlignment, CharUnits UnpackedNewAlignment, + CharUnits PreferredNewAlignment) { // 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 +2080,12 @@ "Alignment not a power of 2"); UnpackedAlignment = UnpackedNewAlignment; } + + if (PreferredNewAlignment > PreferredAlignment) { + assert(llvm::isPowerOf2_64(PreferredNewAlignment.getQuantity()) && + "Alignment not a power of 2"); + PreferredAlignment = PreferredNewAlignment; + } } uint64_t @@ -3048,10 +3136,10 @@ 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.Alignment, Builder.RequiredAlignment, Builder.HasOwnVFPtr, + Builder.HasOwnVFPtr || Builder.PrimaryBase, Builder.VBPtrOffset, + Builder.DataSize, Builder.FieldOffsets, Builder.NonVirtualSize, + Builder.Alignment, Builder.Alignment, CharUnits::Zero(), Builder.PrimaryBase, false, Builder.SharedVBPtrBase, Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase, Builder.Bases, Builder.VBases); @@ -3059,8 +3147,8 @@ Builder.layout(D); NewEntry = new (*this) ASTRecordLayout( *this, Builder.Size, Builder.Alignment, Builder.Alignment, - Builder.RequiredAlignment, - Builder.Size, Builder.FieldOffsets); + Builder.Alignment, Builder.RequiredAlignment, Builder.Size, + Builder.FieldOffsets); } } else { if (const auto *RD = dyn_cast(D)) { @@ -3080,11 +3168,13 @@ CharUnits NonVirtualSize = skipTailPadding ? DataSize : Builder.NonVirtualSize; NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, + *this, Builder.getSize(), Builder.Alignment, + Builder.PreferredAlignment, Builder.UnadjustedAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(), CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets, NonVirtualSize, Builder.NonVirtualAlignment, + Builder.PreferredNVAlignment, EmptySubobjects.SizeOfLargestEmptySubobject, Builder.PrimaryBase, Builder.PrimaryBaseIsVirtual, nullptr, false, false, Builder.Bases, Builder.VBases); @@ -3093,7 +3183,8 @@ Builder.Layout(D); NewEntry = new (*this) ASTRecordLayout( - *this, Builder.getSize(), Builder.Alignment, Builder.UnadjustedAlignment, + *this, Builder.getSize(), Builder.Alignment, + Builder.PreferredAlignment, Builder.UnadjustedAlignment, /*RequiredAlignment : used by MS-ABI)*/ Builder.Alignment, Builder.getSize(), Builder.FieldOffsets); } @@ -3245,14 +3336,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.PreferredAlignment, + Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI)*/ + Builder.Alignment, Builder.getDataSize(), Builder.FieldOffsets); ObjCLayouts[Key] = NewEntry; @@ -3415,22 +3503,26 @@ if (CXXRD && !isMsLayout(C)) OS << ", dsize=" << Layout.getDataSize().getQuantity(); OS << ", align=" << Layout.getAlignment().getQuantity(); + if (C.getTargetInfo().supportsAIXPowerAlignment()) + OS << ", preferredalign=" << Layout.getPreferredAlignment().getQuantity(); if (CXXRD) { OS << ",\n"; PrintIndentNoOffset(OS, IndentLevel - 1); OS << " nvsize=" << Layout.getNonVirtualSize().getQuantity(); OS << ", nvalign=" << Layout.getNonVirtualAlignment().getQuantity(); + if (C.getTargetInfo().supportsAIXPowerAlignment()) + OS << ", preferrednvalign=" + << Layout.getPreferredNVAlignment().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; } @@ -3450,9 +3542,13 @@ if (!isMsLayout(*this)) OS << " DataSize:" << toBits(Info.getDataSize()) << "\n"; OS << " Alignment:" << toBits(Info.getAlignment()) << "\n"; + if (Target->supportsAIXPowerAlignment()) + OS << " PreferredAlignment:" << toBits(Info.getPreferredAlignment()) + << "\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"; Index: clang/lib/Basic/Targets/OSTargets.h =================================================================== --- clang/lib/Basic/Targets/OSTargets.h +++ clang/lib/Basic/Targets/OSTargets.h @@ -719,6 +719,8 @@ // AIX sets FLT_EVAL_METHOD to be 1. unsigned getFloatEvalMethod() const override { return 1; } bool hasInt128Type() const override { return false; } + + bool supportsAIXPowerAlignment() const override { return true; } }; void addWindowsDefines(const llvm::Triple &Triple, const LangOptions &Opts, Index: clang/lib/Basic/Targets/PPC.h =================================================================== --- clang/lib/Basic/Targets/PPC.h +++ clang/lib/Basic/Targets/PPC.h @@ -370,9 +370,13 @@ } if (Triple.isOSFreeBSD() || Triple.isOSNetBSD() || Triple.isOSOpenBSD() || - Triple.getOS() == llvm::Triple::AIX || Triple.isMusl()) { + Triple.isMusl()) { LongDoubleWidth = LongDoubleAlign = 64; LongDoubleFormat = &llvm::APFloat::IEEEdouble(); + } else if (Triple.isOSAIX()) { + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } // PPC32 supports atomics up to 4 bytes. @@ -403,13 +407,14 @@ ABI = "elfv1"; } - 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(); + } else if (Triple.isOSAIX()) { + SuitableAlign = 64; + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } // PPC64 supports atomics up to 8 bytes. Index: clang/test/Layout/aix-double-struct-member.cpp =================================================================== --- /dev/null +++ clang/test/Layout/aix-double-struct-member.cpp @@ -0,0 +1,347 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | \ +// RUN: 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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=20, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=4, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::B +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=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, preferredalign=1, +// CHECK-NEXT: | nvsize=1, nvalign=1, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=4, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] +} // namespace test5 + +namespace test6 { +// Test AIX power alignment rule with double array as the frist member. +struct A { + struct B { + double d[3]; + } b; +}; + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::A::B +// CHECK-NEXT: 0 | double [3] d +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::A +// CHECK-NEXT: 0 | struct test6::A::B b +// CHECK-NEXT: 0 | double [3] d +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=8] + +} // namespace test6 + +namespace test7 { +struct A { + struct B { + long double _Complex d[3]; + } b; +}; + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test7::A::B +// CHECK-NEXT: 0 | _Complex long double [3] d +// CHECK-NEXT: | [sizeof=48, dsize=48, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=48, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test7::A +// CHECK-NEXT: 0 | struct test7::A::B b +// CHECK-NEXT: 0 | _Complex long double [3] d +// CHECK-NEXT: | [sizeof=48, dsize=48, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=48, nvalign=4, preferrednvalign=8] + +} // namespace test7 + +namespace tes8 { +// 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 tes8::A +// CHECK-NEXT: 0 | char a +// CHECK-NEXT: 16 | double d +// CHECK-NEXT: 24 | int i +// CHECK-NEXT: | [sizeof=32, dsize=32, align=16, preferredalign=16, +// CHECK-NEXT: | nvsize=32, nvalign=16, preferrednvalign=16] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::B +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: 8 | char a +// CHECK-NEXT: 12 | double d2 +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::C +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | short j +// CHECK-NEXT: 6 | double k +// CHECK-NEXT: | [sizeof=14, dsize=14, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=14, nvalign=2, preferrednvalign=2] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::D +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short j +// CHECK-NEXT: 10 | int i +// CHECK-NEXT: | [sizeof=14, dsize=14, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=14, nvalign=2, preferrednvalign=2] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::E +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | short s +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::F +// CHECK-NEXT: 0 | struct tes8::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, preferredalign=4, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct tes8::G +// CHECK-NEXT: 0 | struct tes8::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, preferredalign=2, +// CHECK-NEXT: | nvsize=20, nvalign=2, preferrednvalign=2] + +} // namespace tes8 Index: clang/test/Layout/aix-no-unique-address-with-double.cpp =================================================================== --- /dev/null +++ clang/test/Layout/aix-no-unique-address-with-double.cpp @@ -0,0 +1,144 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s 2>/dev/null | \ +// RUN: 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, preferredalign=1, +// CHECK-NEXT: | nvsize=1, nvalign=1, preferrednvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct A +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=13, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=20, nvalign=4, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=9, nvalign=4, preferrednvalign=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=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=13, nvalign=4, preferrednvalign=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, preferredalign=4, +// CHECK-NEXT: | nvsize=20, nvalign=4, preferrednvalign=4] + +struct E { + [[no_unique_address]] Empty emp; + int : 0; + double d; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct E +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 0:- | int +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=4] + +int a = sizeof(Empty); +int b = sizeof(A); +int c = sizeof(B1); +int d = sizeof(C1); +int e = sizeof(D1); +int f = sizeof(E); Index: clang/test/Layout/aix-virtual-function-and-base-with-double.cpp =================================================================== --- /dev/null +++ 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 | \ +// RUN: FileCheck --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 | \ +// RUN: FileCheck --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, preferredalign=4, +// CHECK32-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] +// CHECK64-NEXT: 8 | double d1 +// CHECK64-NEXT: | [sizeof=16, dsize=16, align=8, preferredalign=8, +// CHECK64-NEXT: | nvsize=16, nvalign=8, preferrednvalign=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=4, preferredalign=8, +// CHECK32-NEXT: | nvsize=20, nvalign=4, preferrednvalign=8] +// CHECK64-NEXT: 16 | double d1 +// CHECK64-NEXT: | [sizeof=24, dsize=24, align=8, preferredalign=8, +// CHECK64-NEXT: | nvsize=24, nvalign=8, preferrednvalign=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, preferredalign=4, +// CHECK32-NEXT: | nvsize=20, nvalign=4, preferrednvalign=4] +// CHECK64-NEXT: 8 | double d1 +// CHECK64-NEXT: 16 | double d3 +// CHECK64-NEXT: | [sizeof=24, dsize=24, align=8, preferredalign=8, +// CHECK64-NEXT: | nvsize=24, nvalign=8, preferrednvalign=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, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=8, preferrednvalign=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, preferredalign=8, +// CHECK32-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] +// CHECK64-NEXT: | nvsize=16, nvalign=8, preferrednvalign=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, preferredalign=2, +// CHECK32-NEXT: | nvsize=12, nvalign=2, preferrednvalign=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, preferredalign=2, +// CHECK64-NEXT: | nvsize=16, nvalign=2, preferrednvalign=2] + +} // namespace test2