summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/AST/RecordLayout.h16
-rw-r--r--clang/lib/AST/RecordLayout.cpp13
-rw-r--r--clang/lib/AST/RecordLayoutBuilder.cpp77
-rw-r--r--clang/test/Layout/ms-x86-pack-and-align.cpp120
4 files changed, 189 insertions, 37 deletions
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 7268b3a8240..5ee87b91843 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -66,6 +66,10 @@ private:
// Alignment - Alignment of record in characters.
CharUnits Alignment;
+ /// RequiredAlignment - The required alignment of the object. In the MS-ABI
+ /// the __declspec(align()) trumps #pramga pack and must always be obeyed.
+ CharUnits RequiredAlignment;
+
/// FieldOffsets - Array of field offsets in bits.
uint64_t *FieldOffsets;
@@ -100,10 +104,6 @@ private:
/// a primary base class.
bool HasExtendableVFPtr : 1;
- /// AlignAfterVBases - Force appropriate alignment after virtual bases are
- /// laid out in MS-C++-ABI.
- bool AlignAfterVBases : 1;
-
/// PrimaryBase - The primary base info for this record.
llvm::PointerIntPair<const CXXRecordDecl *, 1, bool> PrimaryBase;
@@ -127,6 +127,7 @@ private:
friend class ASTContext;
ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment,
+ CharUnits requiredAlignment,
CharUnits datasize, const uint64_t *fieldoffsets,
unsigned fieldcount);
@@ -134,6 +135,7 @@ private:
typedef CXXRecordLayoutInfo::BaseOffsetsMapTy BaseOffsetsMapTy;
ASTRecordLayout(const ASTContext &Ctx,
CharUnits size, CharUnits alignment,
+ CharUnits requiredAlignment,
bool hasOwnVFPtr, bool hasExtendableVFPtr,
CharUnits vbptroffset,
CharUnits datasize,
@@ -143,7 +145,6 @@ private:
const CXXRecordDecl *PrimaryBase,
bool IsPrimaryBaseVirtual,
const CXXRecordDecl *BaseSharingVBPtr,
- bool ForceAlign,
const BaseOffsetsMapTy& BaseOffsets,
const VBaseOffsetsMapTy& VBaseOffsets);
@@ -267,9 +268,8 @@ public:
return !CXXInfo->VBPtrOffset.isNegative();
}
- bool getAlignAfterVBases() const {
- assert(CXXInfo && "Record layout does not have C++ specific info!");
- return CXXInfo->AlignAfterVBases;
+ CharUnits getRequiredAlignment() const {
+ return RequiredAlignment;
}
/// getVBPtrOffset - Get the offset for virtual base table pointer.
diff --git a/clang/lib/AST/RecordLayout.cpp b/clang/lib/AST/RecordLayout.cpp
index 71e44ecf921..a4a59c4f330 100644
--- a/clang/lib/AST/RecordLayout.cpp
+++ b/clang/lib/AST/RecordLayout.cpp
@@ -29,10 +29,13 @@ void ASTRecordLayout::Destroy(ASTContext &Ctx) {
}
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
- CharUnits alignment, CharUnits datasize,
+ CharUnits alignment,
+ CharUnits requiredAlignment,
+ CharUnits datasize,
const uint64_t *fieldoffsets,
unsigned fieldcount)
- : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0),
+ : Size(size), DataSize(datasize), Alignment(alignment),
+ RequiredAlignment(requiredAlignment), FieldOffsets(0),
FieldCount(fieldcount), CXXInfo(0) {
if (FieldCount > 0) {
FieldOffsets = new (Ctx) uint64_t[FieldCount];
@@ -43,6 +46,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
// Constructor for C++ records.
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
CharUnits size, CharUnits alignment,
+ CharUnits requiredAlignment,
bool hasOwnVFPtr, bool hasExtendableVFPtr,
CharUnits vbptroffset,
CharUnits datasize,
@@ -54,10 +58,10 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
const CXXRecordDecl *PrimaryBase,
bool IsPrimaryBaseVirtual,
const CXXRecordDecl *BaseSharingVBPtr,
- bool AlignAfterVBases,
const BaseOffsetsMapTy& BaseOffsets,
const VBaseOffsetsMapTy& VBaseOffsets)
- : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0),
+ : Size(size), DataSize(datasize), Alignment(alignment),
+ RequiredAlignment(requiredAlignment), FieldOffsets(0),
FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo)
{
if (FieldCount > 0) {
@@ -76,7 +80,6 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
CXXInfo->VBPtrOffset = vbptroffset;
CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr;
CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr;
- CXXInfo->AlignAfterVBases = AlignAfterVBases;
#ifndef NDEBUG
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index f0142525b1a..a1f6c41ef86 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2064,6 +2064,7 @@ public:
void updateAlignment(CharUnits NewAlignment) {
Alignment = std::max(Alignment, NewAlignment);
}
+ CharUnits getBaseAlignment(const ASTRecordLayout& Layout);
/// \brief Gets the size and alignment taking attributes into account.
std::pair<CharUnits, CharUnits> getAdjustedFieldInfo(const FieldDecl *FD);
/// \brief Places a field at offset 0.
@@ -2089,9 +2090,9 @@ public:
SmallVector<uint64_t, 16> FieldOffsets;
/// \brief The maximum allowed field alignment. This is set by #pragma pack.
CharUnits MaxFieldAlignment;
- /// \brief Alignment does not occur for virtual bases unless something
- /// forces it to by explicitly using __declspec(align())
- bool AlignAfterVBases : 1;
+ /// \brief The alignment that this record must obey. This is imposed by
+ /// __declspec(align()) on the record itself or one of its fields or bases.
+ CharUnits RequiredAlignment;
bool IsUnion : 1;
/// \brief True if the last field laid out was a bitfield and was not 0
/// width.
@@ -2148,6 +2149,16 @@ public:
};
} // namespace
+CharUnits
+MicrosoftRecordLayoutBuilder::getBaseAlignment(const ASTRecordLayout& Layout) {
+ CharUnits BaseAlignment = Layout.getAlignment();
+ if (!MaxFieldAlignment.isZero())
+ BaseAlignment = std::min(BaseAlignment,
+ std::max(MaxFieldAlignment,
+ Layout.getRequiredAlignment()));
+ return BaseAlignment;
+}
+
std::pair<CharUnits, CharUnits>
MicrosoftRecordLayoutBuilder::getAdjustedFieldInfo(const FieldDecl *FD) {
std::pair<CharUnits, CharUnits> FieldInfo =
@@ -2174,9 +2185,18 @@ MicrosoftRecordLayoutBuilder::getAdjustedFieldInfo(const FieldDecl *FD) {
// Respect alignment attributes.
if (unsigned fieldAlign = FD->getMaxAlignment()) {
CharUnits FieldAlign = Context.toCharUnitsFromBits(fieldAlign);
- AlignAfterVBases = true;
+ RequiredAlignment = std::max(RequiredAlignment, FieldAlign);
FieldInfo.second = std::max(FieldInfo.second, FieldAlign);
}
+ // Respect attributes applied inside field base types.
+ if (const RecordType *RT =
+ FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
+ const ASTRecordLayout &Layout = Context.getASTRecordLayout(RT->getDecl());
+ RequiredAlignment = std::max(RequiredAlignment,
+ Layout.getRequiredAlignment());
+ FieldInfo.second = std::max(FieldInfo.second,
+ Layout.getRequiredAlignment());
+ }
return FieldInfo;
}
@@ -2186,7 +2206,10 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {
Size = CharUnits::Zero();
Alignment = CharUnits::One();
- AlignAfterVBases = false;
+ // 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();
// Compute the maximum field alignment.
MaxFieldAlignment = CharUnits::Zero();
@@ -2236,7 +2259,6 @@ MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) {
SharedVBPtrBase = 0;
PrimaryBase = 0;
VirtualAlignment = CharUnits::One();
- AlignAfterVBases = Is64BitMode;
// 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
@@ -2247,12 +2269,12 @@ MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) {
const CXXRecordDecl *BaseDecl =
cast<CXXRecordDecl>(i->getType()->getAs<RecordType>()->getDecl());
const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
- // Handle forced alignment.
- if (Layout.getAlignAfterVBases())
- AlignAfterVBases = true;
+ // Handle required alignment.
+ RequiredAlignment = std::max(RequiredAlignment,
+ Layout.getRequiredAlignment());
// Handle virtual bases.
if (i->isVirtual()) {
- VirtualAlignment = std::max(VirtualAlignment, Layout.getAlignment());
+ VirtualAlignment = std::max(VirtualAlignment, getBaseAlignment(Layout));
HasVBPtr = true;
continue;
}
@@ -2266,7 +2288,7 @@ MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) {
SharedVBPtrBase = BaseDecl;
HasVBPtr = true;
}
- updateAlignment(Layout.getAlignment());
+ updateAlignment(getBaseAlignment(Layout));
}
// Use LayoutFields to compute the alignment of the fields. The layout
@@ -2335,7 +2357,7 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(const CXXRecordDecl *RD) {
if (LazyEmptyBase) {
const ASTRecordLayout &LazyLayout =
Context.getASTRecordLayout(LazyEmptyBase);
- Size = Size.RoundUpToAlignment(LazyLayout.getAlignment());
+ 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)
@@ -2360,7 +2382,7 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(const CXXRecordDecl *RD) {
}
// Insert the base here.
- CharUnits BaseOffset = Size.RoundUpToAlignment(Layout->getAlignment());
+ 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
@@ -2536,7 +2558,7 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBase(const CXXRecordDecl *RD,
if (LazyEmptyBase) {
const ASTRecordLayout &LazyLayout =
Context.getASTRecordLayout(LazyEmptyBase);
- Size = Size.RoundUpToAlignment(LazyLayout.getAlignment());
+ 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.
@@ -2560,7 +2582,7 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBase(const CXXRecordDecl *RD,
}
CharUnits BaseNVSize = Layout.getNonVirtualSize();
- CharUnits BaseAlign = Layout.getAlignment();
+ CharUnits BaseAlign = getBaseAlignment(Layout);
// vtordisps are always 4 bytes (even in 64-bit mode)
if (HasVtordisp)
@@ -2580,7 +2602,7 @@ void MicrosoftRecordLayoutBuilder::finalizeCXXLayout(const CXXRecordDecl *RD) {
// Flush the lazy virtual base.
layoutVirtualBase(0, false);
- if (RD->vbases_begin() == RD->vbases_end() || AlignAfterVBases)
+ if (RD->vbases_begin() == RD->vbases_end() || !RequiredAlignment.isZero())
Size = Size.RoundUpToAlignment(Alignment);
if (Size.isZero())
@@ -2588,9 +2610,10 @@ void MicrosoftRecordLayoutBuilder::finalizeCXXLayout(const CXXRecordDecl *RD) {
}
void MicrosoftRecordLayoutBuilder::honorDeclspecAlign(const RecordDecl *RD) {
- if (unsigned MaxAlign = RD->getMaxAlignment()) {
- AlignAfterVBases = true;
- updateAlignment(Context.toCharUnitsFromBits(MaxAlign));
+ RequiredAlignment = std::max(RequiredAlignment,
+ Context.toCharUnitsFromBits(RD->getMaxAlignment()));
+ if (!RequiredAlignment.isZero()) {
+ updateAlignment(RequiredAlignment);
Size = Size.RoundUpToAlignment(Alignment);
}
}
@@ -2684,19 +2707,19 @@ ASTContext::BuildMicrosoftASTRecordLayout(const RecordDecl *D) const {
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
Builder.cxxLayout(RD);
return new (*this) ASTRecordLayout(
- *this, Builder.Size, Builder.Alignment,
+ *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
Builder.HasExtendableVFPtr && !Builder.PrimaryBase,
Builder.HasExtendableVFPtr,
Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets.data(),
Builder.FieldOffsets.size(), Builder.DataSize,
Builder.NonVirtualAlignment, CharUnits::Zero(), Builder.PrimaryBase,
- false, Builder.SharedVBPtrBase, Builder.AlignAfterVBases, Builder.Bases,
+ false, Builder.SharedVBPtrBase, Builder.Bases,
Builder.VBases);
} else {
Builder.layout(D);
return new (*this) ASTRecordLayout(
- *this, Builder.Size, Builder.Alignment, Builder.Size,
- Builder.FieldOffsets.data(), Builder.FieldOffsets.size());
+ *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
+ Builder.Size, Builder.FieldOffsets.data(), Builder.FieldOffsets.size());
}
}
@@ -2747,6 +2770,8 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
NewEntry =
new (*this) ASTRecordLayout(*this, Builder.getSize(),
Builder.Alignment,
+ /*RequiredAlignment : used by MS-ABI)*/
+ Builder.Alignment,
Builder.HasOwnVFPtr,
RD->isDynamicClass(),
CharUnits::fromQuantity(-1),
@@ -2758,7 +2783,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
EmptySubobjects.SizeOfLargestEmptySubobject,
Builder.PrimaryBase,
Builder.PrimaryBaseIsVirtual,
- 0, true,
+ 0,
Builder.Bases, Builder.VBases);
} else {
RecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/0);
@@ -2767,6 +2792,8 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
NewEntry =
new (*this) ASTRecordLayout(*this, Builder.getSize(),
Builder.Alignment,
+ /*RequiredAlignment : used by MS-ABI)*/
+ Builder.Alignment,
Builder.getSize(),
Builder.FieldOffsets.data(),
Builder.FieldOffsets.size());
@@ -2876,6 +2903,8 @@ ASTContext::getObjCLayout(const ObjCInterfaceDecl *D,
const ASTRecordLayout *NewEntry =
new (*this) ASTRecordLayout(*this, Builder.getSize(),
Builder.Alignment,
+ /*RequiredAlignment : used by MS-ABI)*/
+ Builder.Alignment,
Builder.getDataSize(),
Builder.FieldOffsets.data(),
Builder.FieldOffsets.size());
diff --git a/clang/test/Layout/ms-x86-pack-and-align.cpp b/clang/test/Layout/ms-x86-pack-and-align.cpp
new file mode 100644
index 00000000000..f9f2b30f5f7
--- /dev/null
+++ b/clang/test/Layout/ms-x86-pack-and-align.cpp
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts -fsyntax-only -cxx-abi microsoft %s 2>&1 \
+// 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, ...);
+char buffer[419430400];
+
+struct A {
+ char a;
+ A() {
+ printf("A = %d\n", (int)((char*)this - buffer));
+ printf("A.a = %d\n", (int)((char*)&a - buffer));
+ }
+};
+
+struct B {
+ __declspec(align(4)) long long a;
+ B() {
+ printf("B = %d\n", (int)((char*)this - buffer));
+ printf("B.a = %d\n", (int)((char*)&a - buffer));
+ }
+};
+
+#pragma pack(push, 2)
+struct X {
+ B a;
+ char b;
+ int c;
+ X() {
+ printf("X = %d\n", (int)((char*)this - buffer));
+ printf("X.a = %d\n", (int)((char*)&a - buffer));
+ printf("X.b = %d\n", (int)((char*)&b - buffer));
+ printf("X.c = %d\n", (int)((char*)&c - buffer));
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct X
+// CHECK: 0 | struct B a
+// CHECK: 0 | long long a
+// CHECK: 8 | char b
+// CHECK: 10 | int c
+// CHECK: | [sizeof=16, align=4
+// CHECK: | nvsize=16, nvalign=4]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct X
+// CHECK-X64: 0 | struct B a
+// CHECK-X64: 0 | long long a
+// CHECK-X64: 8 | char b
+// CHECK-X64: 10 | int c
+// CHECK-X64: | [sizeof=16, align=4
+// CHECK-X64: | nvsize=16, nvalign=4]
+
+struct Y : A, B {
+ char a;
+ int b;
+ Y() {
+ printf("Y = %d\n", (int)((char*)this - buffer));
+ printf("Y.a = %d\n", (int)((char*)&a - buffer));
+ printf("Y.b = %d\n", (int)((char*)&b - buffer));
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct Y
+// CHECK: 0 | struct A (base)
+// CHECK: 0 | char a
+// CHECK: 4 | struct B (base)
+// CHECK: 4 | long long a
+// CHECK: 12 | char a
+// CHECK: 14 | int b
+// CHECK: | [sizeof=20, align=4
+// CHECK: | nvsize=20, nvalign=4]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct Y
+// CHECK-X64: 0 | struct A (base)
+// CHECK-X64: 0 | char a
+// CHECK-X64: 4 | struct B (base)
+// CHECK-X64: 4 | long long a
+// CHECK-X64: 12 | char a
+// CHECK-X64: 14 | int b
+// CHECK-X64: | [sizeof=20, align=4
+// CHECK-X64: | nvsize=20, nvalign=4]
+
+struct Z : virtual B {
+ char a;
+ int b;
+ Z() {
+ printf("Z = %d\n", (int)((char*)this - buffer));
+ printf("Z.a = %d\n", (int)((char*)&a - buffer));
+ printf("Z.b = %d\n", (int)((char*)&b - buffer));
+ }
+};
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK: 0 | struct Z
+// CHECK: 0 | (Z vbtable pointer)
+// CHECK: 4 | char a
+// CHECK: 6 | int b
+// CHECK: 12 | struct B (virtual base)
+// CHECK: 12 | long long a
+// CHECK: | [sizeof=20, align=4
+// CHECK: | nvsize=10, nvalign=2]
+// CHECK-X64: *** Dumping AST Record Layout
+// CHECK-X64: 0 | struct Z
+// CHECK-X64: 0 | (Z vbtable pointer)
+// CHECK-X64: 8 | char a
+// CHECK-X64: 10 | int b
+// CHECK-X64: 16 | struct B (virtual base)
+// CHECK-X64: 16 | long long a
+// CHECK-X64: | [sizeof=24, align=4
+// CHECK-X64: | nvsize=14, nvalign=2]
+
+#pragma pack(pop)
+
+int a[
+sizeof(X)+
+sizeof(Y)+
+sizeof(Z)];
OpenPOWER on IntegriCloud