diff options
author | dim <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
commit | 554bcb69c2d785a011a30e7db87a36a87fe7db10 (patch) | |
tree | 9abb1a658a297776086f4e0dfa6ca533de02104e /lib/AST/RecordLayoutBuilder.cpp | |
parent | bb67ca86b31f67faee50bd10c3b036d65751745a (diff) | |
download | FreeBSD-src-554bcb69c2d785a011a30e7db87a36a87fe7db10.zip FreeBSD-src-554bcb69c2d785a011a30e7db87a36a87fe7db10.tar.gz |
Vendor import of clang trunk r161861:
http://llvm.org/svn/llvm-project/cfe/trunk@161861
Diffstat (limited to 'lib/AST/RecordLayoutBuilder.cpp')
-rw-r--r-- | lib/AST/RecordLayoutBuilder.cpp | 307 |
1 files changed, 252 insertions, 55 deletions
diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index c2d9294..d5df63f 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" @@ -161,10 +162,9 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { // Check the fields. for (CXXRecordDecl::field_iterator I = Class->field_begin(), E = Class->field_end(); I != E; ++I) { - const FieldDecl *FD = *I; const RecordType *RT = - Context.getBaseElementType(FD->getType())->getAs<RecordType>(); + Context.getBaseElementType(I->getType())->getAs<RecordType>(); // We only care about record types. if (!RT) @@ -261,12 +261,11 @@ EmptySubobjectMap::CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -310,12 +309,11 @@ void EmptySubobjectMap::UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -380,13 +378,12 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -491,13 +488,12 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -538,6 +534,8 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD, } } +typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy; + class RecordLayoutBuilder { protected: // FIXME: Remove this and make the appropriate fields public. @@ -600,8 +598,9 @@ protected: /// out is virtual. bool PrimaryBaseIsVirtual; - /// VFPtrOffset - Virtual function table offset. Only for MS layout. - CharUnits VFPtrOffset; + /// HasOwnVFPtr - Whether the class provides its own vtable/vftbl + /// pointer, as opposed to inheriting one from a primary base class. + bool HasOwnVFPtr; /// VBPtrOffset - Virtual base table offset. Only for MS layout. CharUnits VBPtrOffset; @@ -612,7 +611,7 @@ protected: BaseOffsetsMapTy Bases; // VBases - virtual base classes and their offsets in the record. - BaseOffsetsMapTy VBases; + ASTRecordLayout::VBaseOffsetsMapTy VBases; /// IndirectPrimaryBases - Virtual base classes, direct or indirect, that are /// primary base classes for some other direct or indirect base class. @@ -652,7 +651,7 @@ protected: NonVirtualAlignment(CharUnits::One()), ZeroLengthBitfield(0), PrimaryBase(0), PrimaryBaseIsVirtual(false), - VFPtrOffset(CharUnits::fromQuantity(-1)), + HasOwnVFPtr(false), VBPtrOffset(CharUnits::fromQuantity(-1)), FirstNearlyEmptyVBase(0) { } @@ -725,15 +724,20 @@ protected: CharUnits Offset); bool needsVFTable(const CXXRecordDecl *RD) const; - bool hasNewVirtualFunction(const CXXRecordDecl *RD) const; + bool hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor = false) const; bool isPossiblePrimaryBase(const CXXRecordDecl *Base) const; + void computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &VtordispVBases); + /// LayoutVirtualBases - Lays out all the virtual bases. void LayoutVirtualBases(const CXXRecordDecl *RD, const CXXRecordDecl *MostDerivedClass); /// LayoutVirtualBase - Lays out a single virtual base. - void LayoutVirtualBase(const BaseSubobjectInfo *Base); + void LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed = false); /// LayoutBase - Will lay out a base and return the offset where it was /// placed, in chars. @@ -1044,8 +1048,7 @@ RecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD) { CharUnits PtrAlign = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); EnsureVTablePointerAlignment(PtrAlign); - if (isMicrosoftCXXABI()) - VFPtrOffset = getSize(); + HasOwnVFPtr = true; setSize(getSize() + PtrWidth); setDataSize(getSize()); } @@ -1142,7 +1145,7 @@ RecordLayoutBuilder::AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info, assert(!VBases.count(Info->PrimaryVirtualBaseInfo->Class) && "primary vbase offset already exists!"); VBases.insert(std::make_pair(Info->PrimaryVirtualBaseInfo->Class, - Offset)); + ASTRecordLayout::VBaseInfo(Offset, false))); // Traverse the primary virtual base. AddPrimaryVirtualBaseOffsets(Info->PrimaryVirtualBaseInfo, Offset); @@ -1193,19 +1196,177 @@ bool RecordLayoutBuilder::needsVFTable(const CXXRecordDecl *RD) const { return hasNewVirtualFunction(RD); } +/// Does the given class inherit non-virtually from any of the classes +/// in the given set? +static bool hasNonVirtualBaseInSet(const CXXRecordDecl *RD, + const ClassSetTy &set) { + for (CXXRecordDecl::base_class_const_iterator + I = RD->bases_begin(), E = RD->bases_end(); I != E; ++I) { + // Ignore virtual links. + if (I->isVirtual()) continue; + + // Check whether the set contains the base. + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + if (set.count(base)) + return true; + + // Otherwise, recurse and propagate. + if (hasNonVirtualBaseInSet(base, set)) + return true; + } + + return false; +} + +/// Does the given method (B::foo()) already override a method (A::foo()) +/// such that A requires a vtordisp in B? If so, we don't need to add a +/// new vtordisp for B in a yet-more-derived class C providing C::foo(). +static bool overridesMethodRequiringVtorDisp(const ASTContext &Context, + const CXXMethodDecl *M) { + CXXMethodDecl::method_iterator + I = M->begin_overridden_methods(), E = M->end_overridden_methods(); + if (I == E) return false; + + const ASTRecordLayout::VBaseOffsetsMapTy &offsets = + Context.getASTRecordLayout(M->getParent()).getVBaseOffsetsMap(); + do { + const CXXMethodDecl *overridden = *I; + + // If the overridden method's class isn't recognized as a virtual + // base in the derived class, ignore it. + ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + it = offsets.find(overridden->getParent()); + if (it == offsets.end()) continue; + + // Otherwise, check if the overridden method's class needs a vtordisp. + if (it->second.hasVtorDisp()) return true; + + } while (++I != E); + return false; +} + +/// In the Microsoft ABI, decide which of the virtual bases require a +/// vtordisp field. +void RecordLayoutBuilder::computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &vtordispVBases) { + // Bail out if we have no virtual bases. + assert(RD->getNumVBases()); + + // Build up the set of virtual bases that we haven't decided yet. + ClassSetTy undecidedVBases; + for (CXXRecordDecl::base_class_const_iterator + I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { + const CXXRecordDecl *vbase = I->getType()->getAsCXXRecordDecl(); + undecidedVBases.insert(vbase); + } + assert(!undecidedVBases.empty()); + + // A virtual base requires a vtordisp field in a derived class if it + // requires a vtordisp field in a base class. Walk all the direct + // bases and collect this information. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + const ASTRecordLayout &baseLayout = Context.getASTRecordLayout(base); + + // Iterate over the set of virtual bases provided by this class. + for (ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + VI = baseLayout.getVBaseOffsetsMap().begin(), + VE = baseLayout.getVBaseOffsetsMap().end(); VI != VE; ++VI) { + // If it doesn't need a vtordisp in this base, ignore it. + if (!VI->second.hasVtorDisp()) continue; + + // If we've already seen it and decided it needs a vtordisp, ignore it. + if (!undecidedVBases.erase(VI->first)) + continue; + + // Add it. + vtordispVBases.insert(VI->first); + + // Quit as soon as we've decided everything. + if (undecidedVBases.empty()) + return; + } + } + + // Okay, we have virtual bases that we haven't yet decided about. A + // virtual base requires a vtordisp if any the non-destructor + // virtual methods declared in this class directly override a method + // provided by that virtual base. (If so, we need to emit a thunk + // for that method, to be used in the construction vftable, which + // applies an additional 'vtordisp' this-adjustment.) + + // Collect the set of bases directly overridden by any method in this class. + // It's possible that some of these classes won't be virtual bases, or won't be + // provided by virtual bases, or won't be virtual bases in the overridden + // instance but are virtual bases elsewhere. Only the last matters for what + // we're doing, and we can ignore those: if we don't directly override + // a method provided by a virtual copy of a base class, but we do directly + // override a method provided by a non-virtual copy of that base class, + // then we must indirectly override the method provided by the virtual base, + // and so we should already have collected it in the loop above. + ClassSetTy overriddenBases; + for (CXXRecordDecl::method_iterator + M = RD->method_begin(), E = RD->method_end(); M != E; ++M) { + // Ignore non-virtual methods and destructors. + if (isa<CXXDestructorDecl>(*M) || !M->isVirtual()) + continue; + + for (CXXMethodDecl::method_iterator I = M->begin_overridden_methods(), + E = M->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *overriddenMethod = (*I); + + // Ignore methods that override methods from vbases that require + // require vtordisps. + if (overridesMethodRequiringVtorDisp(Context, overriddenMethod)) + continue; + + // As an optimization, check immediately whether we're overriding + // something from the undecided set. + const CXXRecordDecl *overriddenBase = overriddenMethod->getParent(); + if (undecidedVBases.erase(overriddenBase)) { + vtordispVBases.insert(overriddenBase); + if (undecidedVBases.empty()) return; + + // We can't 'continue;' here because one of our undecided + // vbases might non-virtually inherit from this base. + // Consider: + // struct A { virtual void foo(); }; + // struct B : A {}; + // struct C : virtual A, virtual B { virtual void foo(); }; + // We need a vtordisp for B here. + } + + // Otherwise, just collect it. + overriddenBases.insert(overriddenBase); + } + } + + // Walk the undecided v-bases and check whether they (non-virtually) + // provide any of the overridden bases. We don't need to consider + // virtual links because the vtordisp inheres to the layout + // subobject containing the base. + for (ClassSetTy::const_iterator + I = undecidedVBases.begin(), E = undecidedVBases.end(); I != E; ++I) { + if (hasNonVirtualBaseInSet(*I, overriddenBases)) + vtordispVBases.insert(*I); + } +} + /// hasNewVirtualFunction - Does the given polymorphic class declare a /// virtual function that does not override a method from any of its /// base classes? bool -RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { - assert(RD->isPolymorphic()); +RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor) const { if (!RD->getNumBases()) return true; for (CXXRecordDecl::method_iterator method = RD->method_begin(); method != RD->method_end(); ++method) { - if (method->isVirtual() && !method->size_overridden_methods()) { + if (method->isVirtual() && !method->size_overridden_methods() && + !(IgnoreDestructor && method->getKind() == Decl::CXXDestructor)) { return true; } } @@ -1215,11 +1376,11 @@ RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { /// isPossiblePrimaryBase - Is the given base class an acceptable /// primary base class? bool -RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { +RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *base) const { // In the Itanium ABI, a class can be a primary base class if it has // a vtable for any reason. if (!isMicrosoftCXXABI()) - return Base->isDynamicClass(); + return base->isDynamicClass(); // In the MS ABI, a class can only be a primary base class if it // provides a vf-table at a static offset. That means it has to be @@ -1228,14 +1389,22 @@ RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { // base, which we have to guard against. // First off, it has to have virtual functions. - if (!Base->isPolymorphic()) return false; + if (!base->isPolymorphic()) return false; + + // If it has no virtual bases, then the vfptr must be at a static offset. + if (!base->getNumVBases()) return true; + + // Otherwise, the necessary information is cached in the layout. + const ASTRecordLayout &layout = Context.getASTRecordLayout(base); + + // If the base has its own vfptr, it can be a primary base. + if (layout.hasOwnVFPtr()) return true; - // If it has no virtual bases, then everything is at a static offset. - if (!Base->getNumVBases()) return true; + // If the base has a primary base class, then it can be a primary base. + if (layout.getPrimaryBase()) return true; - // Okay, just ask the base class's layout. - return (Context.getASTRecordLayout(Base).getVFPtrOffset() - != CharUnits::fromQuantity(-1)); + // Otherwise it can't. + return false; } void @@ -1288,10 +1457,12 @@ RecordLayoutBuilder::LayoutVirtualBases(const CXXRecordDecl *RD, } void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { - if (!RD->getNumVBases()) return; + ClassSetTy VtordispVBases; + computeVtordisps(RD, VtordispVBases); + // This is substantially simplified because there are no virtual // primary bases. for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), @@ -1299,12 +1470,25 @@ void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl(); const BaseSubobjectInfo *BaseInfo = VirtualBaseInfo.lookup(BaseDecl); assert(BaseInfo && "Did not find virtual base info!"); - - LayoutVirtualBase(BaseInfo); + + // If this base requires a vtordisp, add enough space for an int field. + // This is apparently always 32-bits, even on x64. + bool vtordispNeeded = false; + if (VtordispVBases.count(BaseDecl)) { + CharUnits IntSize = + CharUnits::fromQuantity(Context.getTargetInfo().getIntWidth() / 8); + + setSize(getSize() + IntSize); + setDataSize(getSize()); + vtordispNeeded = true; + } + + LayoutVirtualBase(BaseInfo, vtordispNeeded); } } -void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { +void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed) { assert(!Base->Derived && "Trying to lay out a primary virtual base!"); // Layout the base. @@ -1312,9 +1496,11 @@ void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { // Add its base class offset. assert(!VBases.count(Base->Class) && "vbase offset already exists!"); - VBases.insert(std::make_pair(Base->Class, Offset)); - - AddPrimaryVirtualBaseOffsets(Base, Offset); + VBases.insert(std::make_pair(Base->Class, + ASTRecordLayout::VBaseInfo(Offset, IsVtordispNeed))); + + if (!isMicrosoftCXXABI()) + AddPrimaryVirtualBaseOffsets(Base, Offset); } CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { @@ -1461,8 +1647,8 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { Context.getTargetInfo().getCharAlign())); NonVirtualAlignment = Alignment; - if (isMicrosoftCXXABI() && - NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { + if (isMicrosoftCXXABI()) { + if (NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { CharUnits AlignMember = NonVirtualSize.RoundUpToAlignment(Alignment) - NonVirtualSize; @@ -1472,9 +1658,9 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { NonVirtualSize = Context.toCharUnitsFromBits( llvm::RoundUpToAlignment(getSizeInBits(), Context.getTargetInfo().getCharAlign())); + } MSLayoutVirtualBases(RD); - } else { // Lay out the virtual bases and add the primary virtual base offsets. LayoutVirtualBases(RD, RD); @@ -1540,7 +1726,7 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { for (RecordDecl::field_iterator Field = D->field_begin(), FieldEnd = D->field_end(); Field != FieldEnd; ++Field) { if (IsMsStruct) { - FieldDecl *FD = (*Field); + FieldDecl *FD = *Field; if (Context.ZeroBitfieldFollowsBitfield(FD, LastFD)) ZeroLengthBitfield = FD; // Zero-length bitfields following non-bitfield members are @@ -1635,9 +1821,8 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { } else if (!Context.getTargetInfo().useBitFieldTypeAlignment() && Context.getTargetInfo().useZeroLengthBitfieldAlignment()) { - FieldDecl *FD = (*Field); - if (FD->isBitField() && FD->getBitWidthValue(Context) == 0) - ZeroLengthBitfield = FD; + if (Field->isBitField() && Field->getBitWidthValue(Context) == 0) + ZeroLengthBitfield = *Field; } LayoutField(*Field); } @@ -2166,6 +2351,10 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { if (MD->hasInlineBody()) continue; + // Ignore inline deleted or defaulted functions. + if (!MD->isUserProvided()) + continue; + // We found it. return MD; } @@ -2238,7 +2427,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { NewEntry = new (*this) ASTRecordLayout(*this, Builder.getSize(), Builder.Alignment, - Builder.VFPtrOffset, + Builder.HasOwnVFPtr, Builder.VBPtrOffset, DataSize, Builder.FieldOffsets.data(), @@ -2375,7 +2564,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, IndentLevel++; const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); - bool HasVfptr = Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1); + bool HasVfptr = Layout.hasOwnVFPtr(); bool HasVbptr = Layout.getVBPtrOffset() != CharUnits::fromQuantity(-1); // Vtable pointer. @@ -2405,7 +2594,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, // vfptr and vbptr (for Microsoft C++ ABI) if (HasVfptr) { - PrintOffset(OS, Offset + Layout.getVFPtrOffset(), IndentLevel); + PrintOffset(OS, Offset, IndentLevel); OS << '(' << *RD << " vftable pointer)\n"; } if (HasVbptr) { @@ -2417,27 +2606,29 @@ static void DumpCXXRecordLayout(raw_ostream &OS, uint64_t FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *Field = *I; + const FieldDecl &Field = **I; CharUnits FieldOffset = Offset + C.toCharUnitsFromBits(Layout.getFieldOffset(FieldNo)); - if (const RecordType *RT = Field->getType()->getAs<RecordType>()) { + if (const RecordType *RT = Field.getType()->getAs<RecordType>()) { if (const CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(RT->getDecl())) { DumpCXXRecordLayout(OS, D, C, FieldOffset, IndentLevel, - Field->getName().data(), + Field.getName().data(), /*IncludeVirtualBases=*/true); continue; } } PrintOffset(OS, FieldOffset, IndentLevel); - OS << Field->getType().getAsString() << ' ' << *Field << '\n'; + OS << Field.getType().getAsString() << ' ' << Field << '\n'; } if (!IncludeVirtualBases) return; // Dump virtual bases. + const ASTRecordLayout::VBaseOffsetsMapTy &vtordisps = + Layout.getVBaseOffsetsMap(); for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { assert(I->isVirtual() && "Found non-virtual class!"); @@ -2445,6 +2636,12 @@ static void DumpCXXRecordLayout(raw_ostream &OS, cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBase); + + if (vtordisps.find(VBase)->second.hasVtorDisp()) { + PrintOffset(OS, VBaseOffset - CharUnits::fromQuantity(4), IndentLevel); + OS << "(vtordisp for vbase " << *VBase << ")\n"; + } + DumpCXXRecordLayout(OS, VBase, C, VBaseOffset, IndentLevel, VBase == PrimaryBase ? "(primary virtual base)" : "(virtual base)", |