// 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;
/// 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.
CXXRecordLayoutInfo *CXXInfo = nullptr;
ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment,
- CharUnits unadjustedAlignment,
+ CharUnits preferredAlignment, CharUnits unadjustedAlignment,
CharUnits requiredAlignment, CharUnits datasize,
ArrayRef<uint64_t> fieldoffsets);
using BaseOffsetsMapTy = CXXRecordLayoutInfo::BaseOffsetsMapTy;
// Constructor for C++ records.
- ASTRecordLayout(const ASTContext &Ctx,
- CharUnits size, CharUnits alignment,
- CharUnits unadjustedAlignment,
- CharUnits requiredAlignment,
- bool hasOwnVFPtr, bool hasExtendableVFPtr,
- CharUnits vbptroffset,
- CharUnits datasize,
- ArrayRef<uint64_t> fieldoffsets,
+ ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment,
+ CharUnits preferredAlignment, CharUnits unadjustedAlignment,
+ CharUnits requiredAlignment, bool hasOwnVFPtr,
+ bool hasExtendableVFPtr, CharUnits vbptroffset,
+ CharUnits datasize, ArrayRef<uint64_t> 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;
/// 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; }
/// 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.
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!");
return !CXXInfo->VBPtrOffset.isNegative();
}
- CharUnits getRequiredAlignment() const {
- return RequiredAlignment;
- }
+ CharUnits getRequiredAlignment() const { return RequiredAlignment; }
bool endsWithZeroSizedObject() const {
return CXXInfo && CXXInfo->EndsWithZeroSizedObject;
/// 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() {}
/// 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;
// 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<RecordType>()) {
+ if (TI.AlignIsRequired)
+ return ABIAlign;
+
+ unsigned PreferredAlign = static_cast<unsigned>(
+ 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<ComplexType>())
T = CT->getElementType().getTypePtr();
if (const auto *ET = T->getAs<EnumType>())
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)
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
CharUnits alignment,
+ CharUnits preferredAlignment,
CharUnits unadjustedAlignment,
CharUnits requiredAlignment,
CharUnits datasize,
ArrayRef<uint64_t> 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<uint64_t> 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<uint64_t> 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;
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/RecordLayout.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Attr.h"
#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"
/// 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;
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.
/// 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<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
/// Bases - base classes and their offsets in the record.
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);
/// 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
setSize(getSize().alignTo(BaseAlign));
// Update the alignment.
- UpdateAlignment(BaseAlign, UnpackedBaseAlign);
+ UpdateAlignment(BaseAlign, UnpackedBaseAlign, BaseAlign);
}
void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases(
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0));
EnsureVTablePointerAlignment(PtrAlign);
HasOwnVFPtr = true;
+
+ assert(!IsUnion && "Unions cannot be dynamic classes.");
+ HandledFirstNonOverlappingEmptyField = true;
+
setSize(getSize() + PtrWidth);
setDataSize(getSize());
}
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.
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();
setSize(std::max(getSize(), Offset + Layout.getSize()));
// Remember max struct/class alignment.
- UpdateAlignment(BaseAlign, UnpackedBaseAlign);
+ UpdateAlignment(BaseAlign, UnpackedAlignTo, PreferredBaseAlign);
return Offset;
}
}
Packed = D->hasAttr<PackedAttr>();
+ HandledFirstNonOverlappingEmptyField =
+ !Context.getTargetInfo().defaultsToAIXPowerAlignment();
// Honor the default struct packing maximum alignment flag.
if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct) {
IsMac68kAlign = true;
MaxFieldAlignment = CharUnits::fromQuantity(2);
Alignment = CharUnits::fromQuantity(2);
+ PreferredAlignment = CharUnits::fromQuantity(2);
} else {
if (const MaxFieldAlignmentAttr *MFAA = D->getAttr<MaxFieldAlignmentAttr>())
MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment());
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;
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);
void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
bool InsertExtraPadding) {
+ auto *FieldClass = D->getType()->getAsCXXRecordDecl();
+ bool PotentiallyOverlapping = D->hasAttr<NoUniqueAddressAttr>() && 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<NoUniqueAddressAttr>() && FieldClass;
- bool IsOverlappingEmptyField = PotentiallyOverlapping && FieldClass->isEmpty();
bool FieldPacked = Packed || D->hasAttr<PackedAttr>();
- 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.
// 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<ReferenceType>()) {
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<CharUnits, CharUnits> 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.
}
}
+ // 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<ComplexType>()) {
+ performBuiltinTypeAlignmentUpgrade(CTy->getElementType()->castAs<BuiltinType>());
+ } else if (const BuiltinType *BTy = Ty->getAs<BuiltinType>()) {
+ performBuiltinTypeAlignmentUpgrade(BTy);
+ } else if (const RecordType *RT = Ty->getAs<RecordType>()) {
+ 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.
// 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;
}
}
}
(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) {
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
// alignment to 1.
if (InferAlignment && External.Size < RoundedSize) {
Alignment = CharUnits::One();
+ PreferredAlignment = CharUnits::One();
InferAlignment = false;
}
setSize(External.Size);
}
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))
"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
// 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;
}
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);
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<CXXRecordDecl>(D)) {
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);
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);
}
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;
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;
}
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";
// 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,
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();
}
// 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";
ABI = "elfv1";
}
- if (Triple.isOSFreeBSD() || Triple.getOS() == llvm::Triple::AIX ||
- Triple.isMusl()) {
+ if (Triple.isOSFreeBSD() || Triple.isMusl()) {
LongDoubleWidth = LongDoubleAlign = 64;
LongDoubleFormat = &llvm::APFloat::IEEEdouble();
}
--- /dev/null
+// 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]
--- /dev/null
+// 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]
--- /dev/null
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -fsyntax-only %s | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -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
--- /dev/null
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -fsyntax-only %s | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -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);
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm -triple powerpc-ibm-aix-xcoff -x c++ < %s | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -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
--- /dev/null
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ < %s | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ < %s | \
+// RUN: FileCheck %s
+
+struct C {
+ double x;
+};
+
+typedef struct C __attribute__((__aligned__(2))) CC;
+
+CC cc;
+
+// CHECK: @cc = global %struct.C zeroinitializer, align 2
--- /dev/null
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts %s | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts %s | \
+// RUN: FileCheck %s
+
+namespace test1 {
+typedef double __attribute__((__aligned__(2))) Dbl;
+struct A {
+ Dbl x;
+};
+
+int b = sizeof(A);
+
+// CHECK: 0 | struct test1::A
+// CHECK-NEXT: 0 | test1::Dbl x
+// CHECK-NEXT: | [sizeof=8, dsize=8, align=2, preferredalign=2,
+// CHECK-NEXT: | nvsize=8, nvalign=2, preferrednvalign=2]
+
+} // namespace test1
+
+namespace test2 {
+typedef double Dbl __attribute__((__aligned__(2)));
+typedef Dbl DblArr[];
+
+union U {
+ DblArr da;
+ char x;
+};
+
+int x = sizeof(U);
+
+// CHECK: 0 | union test2::U
+// CHECK-NEXT: 0 | test2::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 test2
--- /dev/null
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -fsyntax-only %s | \
+// RUN: FileCheck --check-prefixes=CHECK,CHECK32 %s
+
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \
+// RUN: -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