diff options
| author | David Majnemer <david.majnemer@gmail.com> | 2016-05-23 17:16:12 +0000 |
|---|---|---|
| committer | David Majnemer <david.majnemer@gmail.com> | 2016-05-23 17:16:12 +0000 |
| commit | cd3ebfe293d02e18397356daa21517e0fa06c32a (patch) | |
| tree | 2023ddf201767c4df5d193074196bcf588508c7e /clang/lib/AST | |
| parent | 64c034da2b422b9ef2772441644e087f2d4c44ac (diff) | |
| download | bcm5719-llvm-cd3ebfe293d02e18397356daa21517e0fa06c32a.tar.gz bcm5719-llvm-cd3ebfe293d02e18397356daa21517e0fa06c32a.zip | |
[MS ABI] Implement __declspec(empty_bases) and __declspec(layout_version)
The layout_version attribute is pretty straightforward: use the layout
rules from version XYZ of MSVC when used like
struct __declspec(layout_version(XYZ)) S {};
The empty_bases attribute is more interesting. It tries to get the C++
empty base optimization to fire more often by tweaking the MSVC ABI
rules in subtle ways:
1. Disable the leading and trailing zero-sized object flags if a class
is marked __declspec(empty_bases) and is empty.
This means that given:
struct __declspec(empty_bases) A {};
struct __declspec(empty_bases) B {};
struct C : A, B {};
'C' will have size 1 and nvsize 0 despite not being annotated
__declspec(empty_bases).
2. When laying out virtual or non-virtual bases, disable the injection
of padding between classes if the most derived class is marked
__declspec(empty_bases).
This means that given:
struct A {};
struct B {};
struct __declspec(empty_bases) C : A, B {};
'C' will have size 1 and nvsize 0.
3. When calculating the offset of a non-virtual base, choose offset zero
if the most derived class is marked __declspec(empty_bases) and the
base is empty _and_ has an nvsize of 0.
Because of the ABI rules, this does not mean that empty bases
reliably get placed at offset 0!
For example:
struct A {};
struct B {};
struct __declspec(empty_bases) C : A, B { virtual ~C(); };
'C' will be pointer sized to account for the vfptr at offset 0.
'A' and 'B' will _not_ be at offset 0 despite being empty!
Instead, they will be located right after the vfptr.
This occurs due to the interaction betweeen non-virtual base layout
and virtual function pointer injection: injection occurs after the
nv-bases and shifts them down by the size of a pointer.
llvm-svn: 270457
Diffstat (limited to 'clang/lib/AST')
| -rw-r--r-- | clang/lib/AST/RecordLayout.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/AST/RecordLayoutBuilder.cpp | 62 |
2 files changed, 48 insertions, 18 deletions
diff --git a/clang/lib/AST/RecordLayout.cpp b/clang/lib/AST/RecordLayout.cpp index b2c244e3790..4cee2e06311 100644 --- a/clang/lib/AST/RecordLayout.cpp +++ b/clang/lib/AST/RecordLayout.cpp @@ -58,7 +58,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const CXXRecordDecl *BaseSharingVBPtr, - bool HasZeroSizedSubObject, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase, const BaseOffsetsMapTy& BaseOffsets, const VBaseOffsetsMapTy& VBaseOffsets) @@ -82,7 +82,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CXXInfo->VBPtrOffset = vbptroffset; CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr; CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr; - CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject; + CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject; CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase; diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 6a7d5144c8c..d03273f9175 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -2228,7 +2228,8 @@ public: /// laid out. void initializeCXXLayout(const CXXRecordDecl *RD); void layoutNonVirtualBases(const CXXRecordDecl *RD); - void layoutNonVirtualBase(const CXXRecordDecl *BaseDecl, + void layoutNonVirtualBase(const CXXRecordDecl *RD, + const CXXRecordDecl *BaseDecl, const ASTRecordLayout &BaseLayout, const ASTRecordLayout *&PreviousBaseLayout); void injectVFPtr(const CXXRecordDecl *RD); @@ -2334,7 +2335,7 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo( if (!MaxFieldAlignment.isZero()) Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment); // Track zero-sized subobjects here where it's already available. - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); // Respect required alignment, this is necessary because we may have adjusted // the alignment in the case of pragam pack. Note that the required alignment // doesn't actually apply to the struct alignment at this point. @@ -2369,7 +2370,7 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo( if (auto RT = FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) { auto const &Layout = Context.getASTRecordLayout(RT->getDecl()); - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); FieldRequiredAlignment = std::max(FieldRequiredAlignment, Layout.getRequiredAlignment()); } @@ -2502,7 +2503,7 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) { LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); } // Lay out the base. - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); } // Figure out if we need a fresh VFPtr for this class. if (!PrimaryBase && RD->isDynamicClass()) @@ -2531,7 +2532,7 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) { LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); } // Lay out the base. - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize(); } // Set our VBPtroffset if we know it at this point. @@ -2543,15 +2544,32 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) { } } +static bool recordUsesEBO(const RecordDecl *RD) { + if (!isa<CXXRecordDecl>(RD)) + return false; + if (RD->hasAttr<EmptyBasesAttr>()) + return true; + if (auto *LVA = RD->getAttr<LayoutVersionAttr>()) + // TODO: Double check with the next version of MSVC. + if (LVA->getVersion() <= LangOptions::MSVC2015) + return false; + // TODO: Some later version of MSVC will change the default behavior of the + // compiler to enable EBO by default. When this happens, we will need an + // additional isCompatibleWithMSVC check. + return false; +} + void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase( + const CXXRecordDecl *RD, const CXXRecordDecl *BaseDecl, const ASTRecordLayout &BaseLayout, const ASTRecordLayout *&PreviousBaseLayout) { // 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() && - BaseLayout.leadsWithZeroSizedBase()) + bool MDCUsesEBO = recordUsesEBO(RD); + if (PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() && + BaseLayout.leadsWithZeroSizedBase() && !MDCUsesEBO) Size++; ElementInfo Info = getAdjustedElementInfo(BaseLayout); CharUnits BaseOffset; @@ -2560,14 +2578,23 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase( bool FoundBase = false; if (UseExternalLayout) { FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset); - if (FoundBase) + if (FoundBase) { assert(BaseOffset >= Size && "base offset already allocated"); + Size = BaseOffset; + } } - if (!FoundBase) - BaseOffset = Size.alignTo(Info.Alignment); + if (!FoundBase) { + if (MDCUsesEBO && BaseDecl->isEmpty() && + BaseLayout.getNonVirtualSize() == CharUnits::Zero()) { + BaseOffset = CharUnits::Zero(); + } else { + // Otherwise, lay the base out at the end of the MDC. + BaseOffset = Size = Size.alignTo(Info.Alignment); + } + } Bases.insert(std::make_pair(BaseDecl, BaseOffset)); - Size = BaseOffset + BaseLayout.getNonVirtualSize(); + Size += BaseLayout.getNonVirtualSize(); PreviousBaseLayout = &BaseLayout; } @@ -2746,8 +2773,9 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) { // with a zero sized base. The padding between virtual bases is 4 // bytes (in both 32 and 64 bits modes) and always involves rounding up to // the required alignment, we don't know why. - if ((PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() && - BaseLayout.leadsWithZeroSizedBase()) || HasVtordisp) { + if ((PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() && + BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) || + HasVtordisp) { Size = Size.alignTo(VtorDispAlignment) + VtorDispSize; Alignment = std::max(VtorDispAlignment, Alignment); } @@ -2785,8 +2813,10 @@ void MicrosoftRecordLayoutBuilder::finalizeLayout(const RecordDecl *RD) { Size = Size.alignTo(RoundingAlignment); } if (Size.isZero()) { - EndsWithZeroSizedObject = true; - LeadsWithZeroSizedBase = true; + if (!recordUsesEBO(RD) || !cast<CXXRecordDecl>(RD)->isEmpty()) { + EndsWithZeroSizedObject = true; + LeadsWithZeroSizedBase = true; + } // Zero-sized structures have size equal to their alignment if a // __declspec(align) came into play. if (RequiredAlignment >= MinEmptyStructSize) @@ -2919,7 +2949,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { NewEntry = new (*this) ASTRecordLayout( *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment, Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase, - Builder.VBPtrOffset, Builder.NonVirtualSize, + Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets.data(), Builder.FieldOffsets.size(), Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(), Builder.PrimaryBase, false, Builder.SharedVBPtrBase, |

