// obvious reason.
// * When laying out empty non-virtual bases, an extra byte of padding is added
// if the non-virtual base before the empty non-virtual base has a vbptr.
+// * The ABI attempts to avoid aliasing of zero sized bases by adding padding
+// between bases or vbases with specific properties. The criteria for
+// additional padding between two bases is that the first base is zero sized
+// or has a zero sized subobject and the second base is zero sized or leads
+// with a zero sized base (sharing of vfptrs can reorder the layout of the
+// so the leading base is not always the first one declared). The padding
+// added for bases is 1 byte. The padding added for vbases depends on the
+// alignment of the object but is at least 4 bytes (in both 32 and 64 bit
+// modes).
namespace {
CharUnits PointerAlignment;
/// \brief Holds an empty base we haven't yet laid out.
const CXXRecordDecl *LazyEmptyBase;
- /// \brief Lets us know if the last base we laid out was empty. Only used
- /// when adjusting the placement of a last zero-sized base in 64 bit mode.
- bool LastBaseWasEmpty;
+ /// \brief A pointer to the Layout for the most recent non-virtual base that
+ /// has been laid out.
+ const ASTRecordLayout *PreviousBaseLayout;
/// \brief Lets us know if we're in 64-bit mode
- bool Is64BitMode;
- /// \brief True if the last non-virtual base has a vbptr.
- bool LastNonVirtualBaseHasVBPtr;
+ bool Is64BitMode : 1;
+ /// \brief True if this class contains a zero sized member or base or a base
+ /// with a zero sized member or base. Only used for MS-ABI.
+ bool HasZeroSizedSubObject : 1;
+ /// \brief True if this class is zero sized or first base is zero sized or
+ /// has this property. Only used for MS-ABI.
+ bool LeadsWithZeroSizedBase : 1;
};
} // namespace
std::pair<CharUnits, CharUnits> FieldInfo =
Context.getTypeInfoInChars(FD->getType());
- // If we're not on win32 and using ms_struct the field alignment will be wrong
- // for 64 bit types, so we fix that here.
- if (FD->getASTContext().getTargetInfo().getTriple().getOS() !=
- llvm::Triple::Win32) {
- QualType T = Context.getBaseElementType(FD->getType());
- if (const BuiltinType *BTy = T->getAs<BuiltinType>()) {
- CharUnits TypeSize = Context.getTypeSizeInChars(BTy);
- if (TypeSize > FieldInfo.second)
- FieldInfo.second = TypeSize;
- }
- }
-
// Respect packed attribute.
if (FD->hasAttr<PackedAttr>())
FieldInfo.second = CharUnits::One();
RequiredAlignment = std::max(RequiredAlignment, FieldAlign);
FieldInfo.second = std::max(FieldInfo.second, FieldAlign);
}
- // Respect attributes applied inside field base types.
+ // Respect attributes applied to subobjects of the field.
if (const RecordType *RT =
FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RT->getDecl());
Layout.getRequiredAlignment());
FieldInfo.second = std::max(FieldInfo.second,
Layout.getRequiredAlignment());
+ // Track zero-sized subobjects here where it's already avaliable.
+ if (Layout.hasZeroSizedSubObject())
+ HasZeroSizedSubObject = true;
}
return FieldInfo;
}
Size = CharUnits::Zero();
Alignment = CharUnits::One();
+
// In 64-bit mode we always perform an alignment step after laying out vbases.
// In 32-bit mode we do not. The check to see if we need to perform alignment
// checks the RequiredAlignment field and performs alignment if it isn't 0.
RequiredAlignment = Is64BitMode ? CharUnits::One() : CharUnits::Zero();
+ HasZeroSizedSubObject = false;
// Compute the maximum field alignment.
MaxFieldAlignment = CharUnits::Zero();
SharedVBPtrBase = 0;
PrimaryBase = 0;
VirtualAlignment = CharUnits::One();
+ LeadsWithZeroSizedBase = false;
// If the record has a dynamic base class, attempt to choose a primary base
// class. It is the first (in direct base class order) non-virtual dynamic
// Handle required alignment.
RequiredAlignment = std::max(RequiredAlignment,
Layout.getRequiredAlignment());
+ // Check for zero sized subobjects
+ if (Layout.hasZeroSizedSubObject())
+ HasZeroSizedSubObject = true;
// Handle virtual bases.
if (i->isVirtual()) {
VirtualAlignment = std::max(VirtualAlignment, getBaseAlignment(Layout));
void
MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
LazyEmptyBase = 0;
- LastBaseWasEmpty = false;
- LastNonVirtualBaseHasVBPtr = false;
+ PreviousBaseLayout = 0;
// Lay out the primary base first.
if (PrimaryBase)
layoutNonVirtualBase(PrimaryBase);
+ const CXXRecordDecl *LeadingBase = PrimaryBase;
// Iterate through the bases and lay out the non-virtual ones.
for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(),
e = RD->bases_end();
continue;
const CXXRecordDecl *BaseDecl =
cast<CXXRecordDecl>(i->getType()->castAs<RecordType>()->getDecl());
+ if (!LeadingBase) {
+ const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
+ LeadsWithZeroSizedBase = Layout.leadsWithZeroSizedBase();
+ LeadingBase = BaseDecl;
+ }
if (BaseDecl != PrimaryBase)
layoutNonVirtualBase(BaseDecl);
}
Size = Size.RoundUpToAlignment(getBaseAlignment(LazyLayout));
// If the last non-virtual base has a vbptr we add a byte of padding for no
// obvious reason.
- if (LastNonVirtualBaseHasVBPtr)
+ if (PreviousBaseLayout && PreviousBaseLayout->hasVBPtr())
Size++;
Bases.insert(std::make_pair(LazyEmptyBase, Size));
// Empty bases only consume space when followed by another empty base.
- if (RD && Layout->getNonVirtualSize().isZero()) {
- LastBaseWasEmpty = true;
+ if (RD && Layout->getNonVirtualSize().isZero())
Size++;
- }
LazyEmptyBase = 0;
- LastNonVirtualBaseHasVBPtr = false;
+ PreviousBaseLayout = &LazyLayout;
}
// RD is null when flushing the final lazy base.
return;
}
+ // Insert padding between two bases if the left first one is zero sized or
+ // contains a zero sized subobject and the right is zero sized or one leads
+ // with a zero sized base.
+ if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() &&
+ Layout->leadsWithZeroSizedBase())
+ Size++;
// Insert the base here.
CharUnits BaseOffset = Size.RoundUpToAlignment(getBaseAlignment(*Layout));
Bases.insert(std::make_pair(RD, BaseOffset));
Size = BaseOffset + Layout->getDataSize();
// Note: we don't update alignment here because it was accounted
- // for during initialization.
- LastBaseWasEmpty = false;
- LastNonVirtualBaseHasVBPtr = Layout->hasVBPtr();
+ // for during initalization.
+ PreviousBaseLayout = Layout;
}
void MicrosoftRecordLayoutBuilder::layoutVBPtr(const CXXRecordDecl *RD) {
// on if the second to last base was also zero sized.
Size += OldSize % BasesAndFieldsAlignment.getQuantity();
} else {
- if (Is64BitMode)
- Size += LastBaseWasEmpty ? CharUnits::One() : CharUnits::Zero();
- else
+ if (!Is64BitMode)
Size = OldSize + BasesAndFieldsAlignment;
+ else if (PreviousBaseLayout &&
+ PreviousBaseLayout->getNonVirtualSize().isZero())
+ Size += CharUnits::One();
}
updateAlignment(PointerAlignment);
}
return;
updateAlignment(VirtualAlignment);
+ PreviousBaseLayout = 0;
// Zero-sized v-bases obey the alignment attribute so apply it here. The
// alignment attribute is normally accounted for in FinalizeLayout.
void MicrosoftRecordLayoutBuilder::layoutVirtualBase(const CXXRecordDecl *RD,
bool HasVtordisp) {
- if (LazyEmptyBase) {
- const ASTRecordLayout &LazyLayout =
- Context.getASTRecordLayout(LazyEmptyBase);
- Size = Size.RoundUpToAlignment(getBaseAlignment(LazyLayout));
- VBases.insert(
- std::make_pair(LazyEmptyBase, ASTRecordLayout::VBaseInfo(Size, false)));
- // Empty bases only consume space when followed by another empty base.
- // The space consumed is in an Alignment sized/aligned block and the v-base
- // is placed at its alignment offset into the chunk, unless its alignment
- // is less than 4 bytes, at which it is placed at 4 byte offset in the
- // chunk. We have no idea why.
- if (RD && Context.getASTRecordLayout(RD).getNonVirtualSize().isZero())
- Size = Size.RoundUpToAlignment(Alignment) + CharUnits::fromQuantity(4);
- LazyEmptyBase = 0;
- }
-
- // RD is null when flushing the final lazy virtual base.
- if (!RD)
- return;
-
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
- if (Layout.getNonVirtualSize().isZero() && !HasVtordisp) {
- LazyEmptyBase = RD;
- return;
- }
+ // Insert padding between two bases if the left first one is zero sized or
+ // contains a zero sized subobject and the right is zero sized or one leads
+ // with a zero sized base. The minimal padding between virtual bases is 4
+ // bytes (in both 32 and 64 bits modes), we don't know why.
+ if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() &&
+ Layout.leadsWithZeroSizedBase())
+ Size = Size.RoundUpToAlignment(Alignment) +
+ std::max(CharUnits::fromQuantity(4), Layout.getAlignment());
CharUnits BaseNVSize = Layout.getNonVirtualSize();
CharUnits BaseAlign = getBaseAlignment(Layout);
Size = BaseOffset + BaseNVSize;
// Note: we don't update alignment here because it was accounted for in
// InitializeLayout.
+
+ PreviousBaseLayout = &Layout;
}
void MicrosoftRecordLayoutBuilder::finalizeCXXLayout(const CXXRecordDecl *RD) {
- // Flush the lazy virtual base.
- layoutVirtualBase(0, false);
-
if (RD->vbases_begin() == RD->vbases_end() || !RequiredAlignment.isZero())
Size = Size.RoundUpToAlignment(Alignment);
- if (Size.isZero())
+ if (Size.isZero()) {
+ HasZeroSizedSubObject = true;
+ LeadsWithZeroSizedBase = true;
Size = Alignment;
+ }
}
void MicrosoftRecordLayoutBuilder::honorDeclspecAlign(const RecordDecl *RD) {
Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets.data(),
Builder.FieldOffsets.size(), Builder.DataSize,
Builder.NonVirtualAlignment, CharUnits::Zero(), Builder.PrimaryBase,
- false, Builder.SharedVBPtrBase, Builder.Bases,
- Builder.VBases);
+ false, Builder.SharedVBPtrBase,
+ Builder.HasZeroSizedSubObject, Builder.LeadsWithZeroSizedBase,
+ Builder.Bases, Builder.VBases);
} else {
Builder.layout(D);
return new (*this) ASTRecordLayout(
EmptySubobjects.SizeOfLargestEmptySubobject,
Builder.PrimaryBase,
Builder.PrimaryBaseIsVirtual,
- 0,
+ 0, false, false,
Builder.Bases, Builder.VBases);
} else {
RecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/0);
--- /dev/null
+// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts -fsyntax-only -cxx-abi microsoft %s 2>/dev/null \
+// RUN: | FileCheck %s
+// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple x86_64-pc-win32 -fdump-record-layouts -fsyntax-only -cxx-abi microsoft %s 2>/dev/null \
+// RUN: | FileCheck %s -check-prefix CHECK-X64
+
+extern "C" int printf(const char *fmt, ...);
+__declspec(align(4096)) char buffer[4096];
+
+struct AT {};
+
+struct V : AT {
+ char c;
+ V() {
+ printf("V - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+struct AT0 {
+ union { struct { int a; AT t; } y; int b; } x;
+ char c;
+ AT0() {
+ printf("AT0 - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+struct AT1 : V {
+ int a;
+ AT1() {
+ printf("AT1 - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+struct AT2 {
+ AT0 t;
+ char AT2FieldName0;
+ AT2() {
+ printf("AT2 - this: %d\n", (int)((char*)this - buffer));
+ printf("AT2 - Fiel: %d\n", (int)((char*)&AT2FieldName0 - buffer));
+ }
+};
+
+struct AT3 : AT2, AT1 {
+ AT3() {
+ printf("AT3 - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct AT3
+// CHECK: 0 | struct AT2 (base)
+// CHECK: 0 | struct AT0 t
+// CHECK: 0 | union AT0::<anonymous at {{.*}} x
+// CHECK: 0 | struct AT0::<anonymous at {{.*}} y
+// CHECK: 0 | int a
+// CHECK: 4 | struct AT t (empty)
+// CHECK: 0 | int b
+// CHECK: 8 | char c
+// CHECK: 12 | char AT2FieldName0
+// CHECK: 20 | struct AT1 (base)
+// CHECK: 20 | struct V (base)
+// CHECK: 20 | struct AT (base) (empty)
+// CHECK: 20 | char c
+// CHECK: 24 | int a
+// CHECK: | [sizeof=28, align=4
+// CHECK: | nvsize=28, nvalign=4]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct AT3
+// CHECK-X64: 0 | struct AT2 (base)
+// CHECK-X64: 0 | struct AT0 t
+// CHECK-X64: 0 | union AT0::<anonymous at {{.*}} x
+// CHECK-X64: 0 | struct AT0::<anonymous at {{.*}} y
+// CHECK-X64: 0 | int a
+// CHECK-X64: 4 | struct AT t (empty)
+// CHECK-X64: 0 | int b
+// CHECK-X64: 8 | char c
+// CHECK-X64: 12 | char AT2FieldName0
+// CHECK-X64: 20 | struct AT1 (base)
+// CHECK-X64: 20 | struct V (base)
+// CHECK-X64: 20 | struct AT (base) (empty)
+// CHECK-X64: 20 | char c
+// CHECK-X64: 24 | int a
+// CHECK-X64: | [sizeof=28, align=4
+// CHECK-X64: | nvsize=28, nvalign=4]
+
+struct BT0 {
+ BT0() {
+ printf("BT0 - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+struct BT2 : BT0 {
+ char BT2FieldName0;
+ BT2() {
+ printf("BT2 - this: %d\n", (int)((char*)this - buffer));
+ printf("BT2 - Fiel: %d\n", (int)((char*)&BT2FieldName0 - buffer));
+ }
+};
+
+struct BT3 : BT0, BT2 {
+ BT3() {
+ printf("BT3 - this: %d\n", (int)((char*)this - buffer));
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct BT3
+// CHECK: 0 | struct BT0 (base) (empty)
+// CHECK: 1 | struct BT2 (base)
+// CHECK: 1 | struct BT0 (base) (empty)
+// CHECK: 1 | char BT2FieldName0
+// CHECK: | [sizeof=2, align=1
+// CHECK: | nvsize=2, nvalign=1]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct BT3
+// CHECK-X64: 0 | struct BT0 (base) (empty)
+// CHECK-X64: 1 | struct BT2 (base)
+// CHECK-X64: 1 | struct BT0 (base) (empty)
+// CHECK-X64: 1 | char BT2FieldName0
+// CHECK-X64: | [sizeof=2, align=1
+// CHECK-X64: | nvsize=2, nvalign=1]
+
+struct T0 : AT {
+ T0() {
+ printf("T0 (this) : %d\n", (char*)this - buffer);
+ }
+};
+
+struct T1 : T0 {
+ char a;
+ T1() {
+ printf("T1 (this) : %d\n", (char*)this - buffer);
+ printf("T1 (fiel) : %d\n", (char*)&a - buffer);
+ }
+};
+
+struct T2 : AT {
+ char a;
+ T2() {
+ printf("T2 (this) : %d\n", (char*)this - buffer);
+ printf("T2 (fiel) : %d\n", (char*)&a - buffer);
+ }
+};
+
+struct __declspec(align(1)) T3 : virtual T1, virtual T2 {
+ T3() {
+ printf("T3 (this) : %d\n", (char*)this - buffer);
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct T3
+// CHECK: 0 | (T3 vbtable pointer)
+// CHECK: 4 | struct T1 (virtual base)
+// CHECK: 4 | struct T0 (base) (empty)
+// CHECK: 4 | struct AT (base) (empty)
+// CHECK: 4 | char a
+// CHECK: 12 | struct T2 (virtual base)
+// CHECK: 12 | struct AT (base) (empty)
+// CHECK: 12 | char a
+// CHECK: | [sizeof=16, align=4
+// CHECK: | nvsize=4, nvalign=4]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct T3
+// CHECK-X64: 0 | (T3 vbtable pointer)
+// CHECK-X64: 8 | struct T1 (virtual base)
+// CHECK-X64: 8 | struct T0 (base) (empty)
+// CHECK-X64: 8 | struct AT (base) (empty)
+// CHECK-X64: 8 | char a
+// CHECK-X64: 20 | struct T2 (virtual base)
+// CHECK-X64: 20 | struct AT (base) (empty)
+// CHECK-X64: 20 | char a
+// CHECK-X64: | [sizeof=24, align=8
+// CHECK-X64: | nvsize=8, nvalign=8]
+
+int a[
+sizeof(AT3) +
+sizeof(BT3) +
+sizeof(T3)];