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,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 preserving (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; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1401,6 +1401,9 @@ /// Whether target allows to overalign ABI-specified preferred alignment virtual bool allowsLargerPreferedTypeAlignment() const { return true; } + /// Whether target defaults to the `power` alignment rules of AIX. + virtual bool defaultsToAIXPowerAlignment() const { return false; } + /// Set supported OpenCL extensions and optional core features. virtual void setSupportedOpenCLOpts() {} 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 @@ -2439,8 +2439,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 preserving to overalign a data type. unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { TypeInfo TI = getTypeInfo(T); unsigned ABIAlign = TI.Align; @@ -2450,18 +2450,33 @@ // The preferred alignment of member pointers is that of a pointer. if (T->isMemberPointerType()) return getPreferredTypeAlign(getPointerDiffType().getTypePtr()); - + if (!Target->allowsLargerPreferedTypeAlignment()) return ABIAlign; - // Double and long long should be naturally aligned if possible. + if (const auto *RT = T->getAs()) { + if (TI.AlignIsRequired) + return ABIAlign; + + unsigned PreferredAlign = static_cast( + toBits(getASTRecordLayout(RT->getDecl()).PreferredAlignment)); + assert(PreferredAlign >= ABIAlign && + "PreferredAlign should be at least as large as ABIAlign."); + return PreferredAlign; + } + + // Double (and, for targets supporting AIX `power` alignment, long double) and + // long long should be naturally aligned (despite requiring less alignment) 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->defaultsToAIXPowerAlignment())) // Don't increase the alignment if an alignment attribute was specified on a // typedef declaration. if (!TI.AlignIsRequired) 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,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; 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" @@ -16,6 +15,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/VTableBuilder.h" +#include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/Format.h" @@ -589,6 +589,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; @@ -632,6 +635,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. @@ -652,6 +656,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-empty or empty-but-non-overlapping + /// field has been handled, if any. + bool HandledFirstNonOverlappingEmptyField; + typedef llvm::DenseMap BaseOffsetsMapTy; /// Bases - base classes and their offsets in the record. @@ -678,17 +688,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); @@ -763,9 +775,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 @@ -998,7 +1014,7 @@ setSize(getSize().alignTo(BaseAlign)); // Update the alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedBaseAlign, BaseAlign); } void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases( @@ -1044,6 +1060,10 @@ Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); EnsureVTablePointerAlignment(PtrAlign); HasOwnVFPtr = true; + + assert(!IsUnion && "Unions cannot be dynamic classes."); + HandledFirstNonOverlappingEmptyField = true; + setSize(getSize() + PtrWidth); setDataSize(getSize()); } @@ -1179,9 +1199,9 @@ CharUnits ItaniumRecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { - const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class); - + assert(!IsUnion && "Unions cannot have base classes."); + const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class); CharUnits Offset; // Query the external layout to see if it provides an offset. @@ -1193,45 +1213,77 @@ HasExternalLayout = External.getExternalNVBaseOffset(Base->Class, Offset); } - // Clang <= 6 incorrectly applied the 'packed' attribute to base classes. - // Per GCC's documentation, it only applies to non-static data members. + auto getBaseOrPreferredBaseAlignFromUnpacked = [&](CharUnits UnpackedAlign) { + // Clang <= 6 incorrectly applied the 'packed' attribute to base classes. + // Per GCC's documentation, it only applies to non-static data members. + return (Packed && ((Context.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver6) || + Context.getTargetInfo().getTriple().isPS4() || + Context.getTargetInfo().getTriple().isOSAIX())) + ? CharUnits::One() + : UnpackedAlign; + }; + CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment(); + CharUnits UnpackedPreferredBaseAlign = Layout.getPreferredNVAlignment(); CharUnits BaseAlign = - (Packed && ((Context.getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver6) || - Context.getTargetInfo().getTriple().isPS4())) - ? CharUnits::One() - : UnpackedBaseAlign; + getBaseOrPreferredBaseAlignFromUnpacked(UnpackedBaseAlign); + CharUnits PreferredBaseAlign = + getBaseOrPreferredBaseAlignFromUnpacked(UnpackedPreferredBaseAlign); + + const bool DefaultsToAIXPowerAlignment = + Context.getTargetInfo().defaultsToAIXPowerAlignment(); + if (DefaultsToAIXPowerAlignment) { + // AIX `power` alignment does not apply the preferred alignment for + // non-union classes if the source of the alignment (the current base in + // this context) follows introduction of the first subobject with + // exclusively allocated space or zero-extent array. + if (!Base->Class->isEmpty() && !HandledFirstNonOverlappingEmptyField) { + // By handling a base class that is not empty, we're handling the + // "first (inherited) member". + HandledFirstNonOverlappingEmptyField = true; + } else { + UnpackedPreferredBaseAlign = UnpackedBaseAlign; + PreferredBaseAlign = BaseAlign; + } + } + CharUnits UnpackedAlignTo = !DefaultsToAIXPowerAlignment + ? UnpackedBaseAlign + : UnpackedPreferredBaseAlign; // 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, UnpackedAlignTo, PreferredBaseAlign); return CharUnits::Zero(); } - // The maximum field alignment overrides base align. + // The maximum field alignment overrides the base align/(AIX-only) preferred + // base align. if (!MaxFieldAlignment.isZero()) { BaseAlign = std::min(BaseAlign, MaxFieldAlignment); - UnpackedBaseAlign = std::min(UnpackedBaseAlign, MaxFieldAlignment); + PreferredBaseAlign = std::min(PreferredBaseAlign, MaxFieldAlignment); + UnpackedAlignTo = std::min(UnpackedAlignTo, MaxFieldAlignment); } + CharUnits AlignTo = + !DefaultsToAIXPowerAlignment ? BaseAlign : PreferredBaseAlign; if (!HasExternalLayout) { // Round up the current record size to the base's alignment boundary. - Offset = getDataSize().alignTo(BaseAlign); + Offset = getDataSize().alignTo(AlignTo); // Try to place the base. while (!EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset)) - Offset += BaseAlign; + Offset += AlignTo; } else { bool Allowed = EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset); (void)Allowed; assert(Allowed && "Base subobject externally placed at overlapping offset"); - if (InferAlignment && Offset < getDataSize().alignTo(BaseAlign)) { + if (InferAlignment && Offset < getDataSize().alignTo(AlignTo)) { // The externally-supplied base offset is before the base offset we // computed. Assume that the structure is packed. Alignment = CharUnits::One(); @@ -1248,7 +1300,7 @@ setSize(std::max(getSize(), Offset + Layout.getSize())); // Remember max struct/class alignment. - UpdateAlignment(BaseAlign, UnpackedBaseAlign); + UpdateAlignment(BaseAlign, UnpackedAlignTo, PreferredBaseAlign); return Offset; } @@ -1260,6 +1312,8 @@ } Packed = D->hasAttr(); + HandledFirstNonOverlappingEmptyField = + !Context.getTargetInfo().defaultsToAIXPowerAlignment(); // Honor the default struct packing maximum alignment flag. if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct) { @@ -1274,6 +1328,7 @@ IsMac68kAlign = true; MaxFieldAlignment = CharUnits::fromQuantity(2); Alignment = CharUnits::fromQuantity(2); + PreferredAlignment = CharUnits::fromQuantity(2); } else { if (const MaxFieldAlignmentAttr *MFAA = D->getAttr()) MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment()); @@ -1293,6 +1348,7 @@ if (UseExternalLayout) { if (External.Align > 0) { Alignment = Context.toCharUnitsFromBits(External.Align); + PreferredAlignment = Context.toCharUnitsFromBits(External.Align); } else { // The external source didn't have alignment information; infer it. InferAlignment = true; @@ -1321,6 +1377,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 +1790,46 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, bool InsertExtraPadding) { + auto *FieldClass = D->getType()->getAsCXXRecordDecl(); + bool PotentiallyOverlapping = D->hasAttr() && FieldClass; + bool IsOverlappingEmptyField = + PotentiallyOverlapping && FieldClass->isEmpty(); + + CharUnits FieldOffset = + (IsUnion || IsOverlappingEmptyField) ? CharUnits::Zero() : getDataSize(); + + const bool DefaultsToAIXPowerAlignment = + Context.getTargetInfo().defaultsToAIXPowerAlignment(); + bool FoundFirstNonOverlappingEmptyFieldForAIX = false; + if (DefaultsToAIXPowerAlignment && !HandledFirstNonOverlappingEmptyField) { + assert(FieldOffset == CharUnits::Zero() && + "The first non-overlapping empty field should have been handled."); + + if (!IsOverlappingEmptyField) { + FoundFirstNonOverlappingEmptyFieldForAIX = true; + + // We're going to handle the "first member" based on + // `FoundFirstNonOverlappingEmptyFieldForAIX` during the current + // invocation of this function; record it as handled for future + // invocations (except for unions, because the current field does not + // represent all "firsts"). + HandledFirstNonOverlappingEmptyField = !IsUnion; + } + } + if (D->isBitField()) { 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(); + bool AlignIsRequired = false; CharUnits FieldSize; CharUnits FieldAlign; // The amount of this class's dsize occupied by the field. @@ -1759,25 +1837,27 @@ // into the field's tail padding. CharUnits EffectiveFieldSize; + auto setDeclInfo = [&](bool IsIncompleteArrayType) { + TypeInfo TI = Context.getTypeInfo(D->getType()); + FieldAlign = Context.toCharUnitsFromBits(TI.Align); + // Flexible array members don't have any size, but they have to be + // aligned appropriately for their element type. + EffectiveFieldSize = FieldSize = + IsIncompleteArrayType ? CharUnits::Zero() + : Context.toCharUnitsFromBits(TI.Width); + AlignIsRequired = TI.AlignIsRequired; + }; + if (D->getType()->isIncompleteArrayType()) { - // This is a flexible array member; we can't directly - // query getTypeInfo about these, so we figure it out here. - // Flexible array members don't have any size, but they - // have to be aligned appropriately for their element type. - EffectiveFieldSize = FieldSize = CharUnits::Zero(); - const ArrayType* ATy = Context.getAsArrayType(D->getType()); - FieldAlign = Context.getTypeAlignInChars(ATy->getElementType()); + setDeclInfo(true /* IsIncompleteArrayType */); } else if (const ReferenceType *RT = D->getType()->getAs()) { unsigned AS = Context.getTargetAddressSpace(RT->getPointeeType()); - EffectiveFieldSize = FieldSize = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(AS)); - FieldAlign = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(AS)); + EffectiveFieldSize = FieldSize = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerWidth(AS)); + FieldAlign = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerAlign(AS)); } else { - std::pair FieldInfo = - Context.getTypeInfoInChars(D->getType()); - EffectiveFieldSize = FieldSize = FieldInfo.first; - FieldAlign = FieldInfo.second; + setDeclInfo(false /* IsIncompleteArrayType */); // A potentially-overlapping field occupies its dsize or nvsize, whichever // is larger. @@ -1829,31 +1909,72 @@ } } + // The AIX `power` alignment rules apply the natural alignment of the + // "first member" if it is of a floating-point data type (or is an aggregate + // whose recursively "first" member or element is such a type). The alignment + // associated with these types for subsequent members use an alignment value + // where the floating-point data type is considered to have 4-byte alignment. + // + // For the purposes of the foregoing: vtable pointers, non-empty base classes, + // and zero-width bit-fields count as prior members; members of empty class + // types marked `no_unique_address` are not considered to be prior members. + CharUnits PreferredAlign = FieldAlign; + if (DefaultsToAIXPowerAlignment && !AlignIsRequired && + FoundFirstNonOverlappingEmptyFieldForAIX) { + auto performBuiltinTypeAlignmentUpgrade = [&](const BuiltinType *BTy) { + if (BTy->getKind() == BuiltinType::Double || + BTy->getKind() == BuiltinType::LongDouble) { + assert(PreferredAlign == CharUnits::fromQuantity(4) && + "No need to upgrade the alignment value."); + PreferredAlign = CharUnits::fromQuantity(8); + } + }; + + const Type *Ty = D->getType()->getBaseElementTypeUnsafe(); + if (const ComplexType *CTy = Ty->getAs()) { + performBuiltinTypeAlignmentUpgrade(CTy->getElementType()->castAs()); + } else if (const BuiltinType *BTy = Ty->getAs()) { + performBuiltinTypeAlignmentUpgrade(BTy); + } else if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *RD = RT->getDecl(); + assert(RD && "Expected non-null RecordDecl."); + 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 UnpackedFieldAlign = + !DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign; CharUnits UnpackedFieldOffset = FieldOffset; - if (FieldPacked) + if (FieldPacked) { FieldAlign = CharUnits::One(); + PreferredAlign = CharUnits::One(); + } CharUnits MaxAlignmentInChars = - Context.toCharUnitsFromBits(D->getMaxAlignment()); + 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); } + CharUnits AlignTo = + !DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign; // Round up the current record size to the field's alignment boundary. - FieldOffset = FieldOffset.alignTo(FieldAlign); + FieldOffset = FieldOffset.alignTo(AlignTo); UnpackedFieldOffset = UnpackedFieldOffset.alignTo(UnpackedFieldAlign); if (UseExternalLayout) { FieldOffset = Context.toCharUnitsFromBits( - updateExternalFieldOffset(D, Context.toBits(FieldOffset))); + updateExternalFieldOffset(D, Context.toBits(FieldOffset))); if (!IsUnion && EmptySubobjects) { // Record the fact that we're placing a field at this offset. @@ -1869,9 +1990,9 @@ // We try offset 0 (for an empty field) and then dsize(C) onwards. if (FieldOffset == CharUnits::Zero() && getDataSize() != CharUnits::Zero()) - FieldOffset = getDataSize().alignTo(FieldAlign); + FieldOffset = getDataSize().alignTo(AlignTo); else - FieldOffset += FieldAlign; + FieldOffset += AlignTo; } } } @@ -1908,9 +2029,9 @@ (uint64_t)Context.toBits(FieldOffset + FieldSize))); } - // Remember max struct/class alignment. + // Remember max struct/class ABI-specified alignment. UnadjustedAlignment = std::max(UnadjustedAlignment, FieldAlign); - UpdateAlignment(FieldAlign, UnpackedFieldAlign); + UpdateAlignment(FieldAlign, UnpackedFieldAlign, PreferredAlign); } void ItaniumRecordLayoutBuilder::FinishLayout(const NamedDecl *D) { @@ -1936,8 +2057,12 @@ 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().defaultsToAIXPowerAlignment() + ? Alignment + : PreferredAlignment)); if (UseExternalLayout) { // If we're inferring alignment, and the external size is smaller than @@ -1945,6 +2070,7 @@ // alignment to 1. if (InferAlignment && External.Size < RoundedSize) { Alignment = CharUnits::One(); + PreferredAlignment = CharUnits::One(); InferAlignment = false; } setSize(External.Size); @@ -1981,7 +2107,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 +2125,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 @@ -2009,6 +2142,7 @@ // The externally-supplied field offset is before the field offset we // computed. Assume that the structure is packed. Alignment = CharUnits::One(); + PreferredAlignment = CharUnits::One(); InferAlignment = false; } @@ -3063,10 +3197,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); @@ -3074,8 +3208,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)) { @@ -3095,11 +3229,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); @@ -3108,7 +3244,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); } @@ -3260,14 +3397,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; @@ -3430,22 +3564,26 @@ if (CXXRD && !isMsLayout(C)) OS << ", dsize=" << Layout.getDataSize().getQuantity(); OS << ", align=" << Layout.getAlignment().getQuantity(); + if (C.getTargetInfo().defaultsToAIXPowerAlignment()) + 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().defaultsToAIXPowerAlignment()) + 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; } @@ -3465,9 +3603,13 @@ if (!isMsLayout(*this)) OS << " DataSize:" << toBits(Info.getDataSize()) << "\n"; OS << " Alignment:" << toBits(Info.getAlignment()) << "\n"; + if (Target->defaultsToAIXPowerAlignment()) + 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"; diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/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 defaultsToAIXPowerAlignment() const override { return true; } }; void addWindowsDefines(const llvm::Triple &Triple, const LangOptions &Opts, 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 @@ -371,13 +371,16 @@ PtrDiffType = SignedLong; IntPtrType = SignedLong; SuitableAlign = 64; + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); break; default: break; } if (Triple.isOSFreeBSD() || Triple.isOSNetBSD() || Triple.isOSOpenBSD() || - Triple.getOS() == llvm::Triple::AIX || Triple.isMusl()) { + Triple.isMusl()) { LongDoubleWidth = LongDoubleAlign = 64; LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } @@ -406,6 +409,9 @@ // TODO: Set appropriate ABI for AIX platform. resetDataLayout("E-m:a-i64:64-n32:64"); SuitableAlign = 64; + LongDoubleWidth = 64; + LongDoubleAlign = DoubleAlign = 32; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } else if ((Triple.getArch() == llvm::Triple::ppc64le)) { resetDataLayout("e-m:e-i64:64-n32:64"); ABI = "elfv2"; @@ -414,8 +420,7 @@ ABI = "elfv1"; } - if (Triple.isOSFreeBSD() || Triple.getOS() == llvm::Triple::AIX || - Triple.isMusl()) { + if (Triple.isOSFreeBSD() || Triple.isMusl()) { LongDoubleWidth = LongDoubleAlign = 64; LongDoubleFormat = &llvm::APFloat::IEEEdouble(); } diff --git a/clang/test/Layout/aix-Wpacked-expecting-diagnostics.cpp b/clang/test/Layout/aix-Wpacked-expecting-diagnostics.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-Wpacked-expecting-diagnostics.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -Wpacked \ +// RUN: -fdump-record-layouts -fsyntax-only -verify -x c++ < %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -Wpacked \ +// RUN: -fdump-record-layouts -fsyntax-only -verify -x c++ < %s | \ +// RUN: FileCheck %s + +struct A { + double d; +}; + +struct B { + char x[8]; +}; + +struct [[gnu::packed]] C : B, A { // expected-warning{{packed attribute is unnecessary for 'C'}} + char x alignas(4)[8]; +}; + +int b = sizeof(C); + +// CHECK: 0 | struct C +// CHECK-NEXT: 0 | struct B (base) +// CHECK-NEXT: 0 | char [8] x +// CHECK-NEXT: 8 | struct A (base) +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: 16 | char [8] x +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=4] diff --git a/clang/test/Layout/aix-Wpacked-no-diagnostics.cpp b/clang/test/Layout/aix-Wpacked-no-diagnostics.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-Wpacked-no-diagnostics.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -Wpacked \ +// RUN: -fdump-record-layouts -fsyntax-only -verify -x c++ < %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -Wpacked \ +// RUN: -fdump-record-layouts -fsyntax-only -verify -x c++ < %s | \ +// RUN: FileCheck %s + +// expected-no-diagnostics + +struct [[gnu::packed]] Q { + double x [[gnu::aligned(4)]]; +}; + +struct QQ : Q { char x; }; + +int a = sizeof(QQ); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct Q +// CHECK-NEXT: 0 | double x +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct QQ +// CHECK-NEXT: 0 | struct Q (base) +// CHECK-NEXT: 0 | double x +// CHECK-NEXT: 8 | char x +// CHECK-NEXT: | [sizeof=12, dsize=9, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=9, nvalign=4, preferrednvalign=4] 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,428 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s | \ +// RUN: FileCheck %s + +namespace test1 { +// Test the class layout when having a double which 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 the class layout when having a 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 class layout when having 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 class layout when having multiple base classes. +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 { +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 { +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 test8 { +struct Emp {}; + +struct Y : Emp { + double d; +}; + +struct Z : Emp { + Y y; +}; + +int a = sizeof(Z); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test8::Emp (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 test8::Y +// CHECK-NEXT: 0 | struct test8::Emp (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 test8::Z +// CHECK-NEXT: 0 | struct test8::Emp (base) (empty) +// CHECK-NEXT: 8 | struct test8::Y y +// CHECK-NEXT: 8 | struct test8::Emp (base) (empty) +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +} // namespace test8 + +namespace test9 { +// Test the class layout when having a zero-extent array in a base class, which +// renders the base class not empty. +struct A { char zea[0]; }; + +struct B : A { double d; }; + +struct C { double d; }; +struct D : A, C { char x; }; + +int a = sizeof(B); +int b = sizeof(D); + +// CHECK: 0 | struct test9::B +// CHECK-NEXT: 0 | struct test9::A (base) +// CHECK-NEXT: 0 | char [0] zea +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=4] + +// CHECK: 0 | struct test9::D +// CHECK-NEXT: 0 | struct test9::A (base) +// CHECK-NEXT: 0 | char [0] zea +// CHECK-NEXT: 0 | struct test9::C (base) +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: 8 | char x +// CHECK-NEXT: | [sizeof=12, dsize=9, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=9, nvalign=4, preferrednvalign=4] + +} // namespace test9 + +namespace test10 { +struct A { double x; }; +struct B : A {}; + +int a = sizeof(B); + +// CHECK: 0 | struct test10::B +// CHECK-NEXT: 0 | struct test10::A (base) +// CHECK-NEXT: 0 | double x +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=8] + +} // namespace test10 + +namespace test11 { +// Test how #pragma pack and align attribute interacts with AIX `power` +// alignment rules. +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 test11::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 test11::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 test11::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 test11::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 test11::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 test11::F +// CHECK-NEXT: 0 | struct test11::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 test11::G +// CHECK-NEXT: 0 | struct test11::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 test11 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,158 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff \ +// RUN: -fdump-record-layouts -fsyntax-only %s | \ +// 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] + +struct F { + [[no_unique_address]] Empty emp, emp2; + double d; +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct F +// CHECK-NEXT: 0 | struct Empty emp (empty) +// CHECK-NEXT: 1 | struct Empty emp2 (empty) +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=8] + +int a = sizeof(Empty); +int b = sizeof(A); +int c = sizeof(B1); +int d = sizeof(C1); +int e = sizeof(D1); +int f = sizeof(E); +int g = sizeof(F); diff --git a/clang/test/Layout/aix-pack-attr-on-base.cpp b/clang/test/Layout/aix-pack-attr-on-base.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-pack-attr-on-base.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -S -emit-llvm -triple powerpc-ibm-aix-xcoff -x c++ < %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -S -emit-llvm -triple powerpc64-ibm-aix-xcoff -x c++ < %s | \ +// RUN: FileCheck %s + +struct A { + char x; +}; + +struct B { + int x; +}; + +struct __attribute__((__packed__)) C : A, B {} c; + +int s = sizeof(c); + +// CHECK: @c = global %struct.C zeroinitializer, align 1 +// CHECK: @s = global i32 5 diff --git a/clang/test/Layout/aix-power-alignment-typedef.cpp b/clang/test/Layout/aix-power-alignment-typedef.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-power-alignment-typedef.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm \ +// RUN: -fdump-record-layouts -x c++ < %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm \ +// RUN: -fdump-record-layouts -x c++ < %s | \ +// RUN: FileCheck %s + +namespace test1 { +struct C { + double x; +}; +typedef struct C __attribute__((__aligned__(2))) CC; + +CC cc; + +// CHECK-DAG: @_ZN5test12ccE = global %"struct.test1::C" zeroinitializer, align 2 + +} //namespace test1 + +namespace test2 { +typedef double __attribute__((__aligned__(2))) Dbl; +struct A { + Dbl x; +}; + +int b = sizeof(A); + +// CHECK: 0 | struct test2::A +// CHECK-NEXT: 0 | test2::Dbl x +// CHECK-NEXT: | [sizeof=8, dsize=8, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=8, nvalign=2, preferrednvalign=2] + +} // namespace test2 + +namespace test3 { +typedef double Dbl __attribute__((__aligned__(2))); +typedef Dbl DblArr[]; + +union U { + DblArr da; + char x; +}; + +int x = sizeof(U); + +// CHECK: 0 | union test3::U +// CHECK-NEXT: 0 | test3::DblArr da +// CHECK-NEXT: 0 | char x +// CHECK-NEXT: | [sizeof=2, dsize=2, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=2, nvalign=2, preferrednvalign=2] + +} // namespace test3 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 | \ +// 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 | \ +// 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