//===--- VTableBuilder.cpp - C++ vtable layout builder --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This contains code dealing with generation of the layout of virtual tables. // //===----------------------------------------------------------------------===// #include "clang/AST/VTableBuilder.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace clang; #define DUMP_OVERRIDERS 0 namespace { /// BaseOffset - Represents an offset from a derived class to a direct or /// indirect base class. struct BaseOffset { /// DerivedClass - The derived class. const CXXRecordDecl *DerivedClass; /// VirtualBase - If the path from the derived class to the base class /// involves virtual base classes, this holds the declaration of the last /// virtual base in this path (i.e. closest to the base class). const CXXRecordDecl *VirtualBase; /// NonVirtualOffset - The offset from the derived class to the base class. /// (Or the offset from the virtual base class to the base class, if the /// path from the derived class to the base class involves a virtual base /// class. CharUnits NonVirtualOffset; BaseOffset() : DerivedClass(nullptr), VirtualBase(nullptr), NonVirtualOffset(CharUnits::Zero()) { } BaseOffset(const CXXRecordDecl *DerivedClass, const CXXRecordDecl *VirtualBase, CharUnits NonVirtualOffset) : DerivedClass(DerivedClass), VirtualBase(VirtualBase), NonVirtualOffset(NonVirtualOffset) { } bool isEmpty() const { return NonVirtualOffset.isZero() && !VirtualBase; } }; /// FinalOverriders - Contains the final overrider member functions for all /// member functions in the base subobjects of a class. class FinalOverriders { public: /// OverriderInfo - Information about a final overrider. struct OverriderInfo { /// Method - The method decl of the overrider. const CXXMethodDecl *Method; /// VirtualBase - The virtual base class subobject of this overrider. /// Note that this records the closest derived virtual base class subobject. const CXXRecordDecl *VirtualBase; /// Offset - the base offset of the overrider's parent in the layout class. CharUnits Offset; OverriderInfo() : Method(nullptr), VirtualBase(nullptr), Offset(CharUnits::Zero()) { } }; private: /// MostDerivedClass - The most derived class for which the final overriders /// are stored. const CXXRecordDecl *MostDerivedClass; /// MostDerivedClassOffset - If we're building final overriders for a /// construction vtable, this holds the offset from the layout class to the /// most derived class. const CharUnits MostDerivedClassOffset; /// LayoutClass - The class we're using for layout information. Will be /// different than the most derived class if the final overriders are for a /// construction vtable. const CXXRecordDecl *LayoutClass; ASTContext &Context; /// MostDerivedClassLayout - the AST record layout of the most derived class. const ASTRecordLayout &MostDerivedClassLayout; /// MethodBaseOffsetPairTy - Uniquely identifies a member function /// in a base subobject. typedef std::pair MethodBaseOffsetPairTy; typedef llvm::DenseMap OverridersMapTy; /// OverridersMap - The final overriders for all virtual member functions of /// all the base subobjects of the most derived class. OverridersMapTy OverridersMap; /// SubobjectsToOffsetsMapTy - A mapping from a base subobject (represented /// as a record decl and a subobject number) and its offsets in the most /// derived class as well as the layout class. typedef llvm::DenseMap, CharUnits> SubobjectOffsetMapTy; typedef llvm::DenseMap SubobjectCountMapTy; /// ComputeBaseOffsets - Compute the offsets for all base subobjects of the /// given base. void ComputeBaseOffsets(BaseSubobject Base, bool IsVirtual, CharUnits OffsetInLayoutClass, SubobjectOffsetMapTy &SubobjectOffsets, SubobjectOffsetMapTy &SubobjectLayoutClassOffsets, SubobjectCountMapTy &SubobjectCounts); typedef llvm::SmallPtrSet VisitedVirtualBasesSetTy; /// dump - dump the final overriders for a base subobject, and all its direct /// and indirect base subobjects. void dump(raw_ostream &Out, BaseSubobject Base, VisitedVirtualBasesSetTy& VisitedVirtualBases); public: FinalOverriders(const CXXRecordDecl *MostDerivedClass, CharUnits MostDerivedClassOffset, const CXXRecordDecl *LayoutClass); /// getOverrider - Get the final overrider for the given method declaration in /// the subobject with the given base offset. OverriderInfo getOverrider(const CXXMethodDecl *MD, CharUnits BaseOffset) const { assert(OverridersMap.count(std::make_pair(MD, BaseOffset)) && "Did not find overrider!"); return OverridersMap.lookup(std::make_pair(MD, BaseOffset)); } /// dump - dump the final overriders. void dump() { VisitedVirtualBasesSetTy VisitedVirtualBases; dump(llvm::errs(), BaseSubobject(MostDerivedClass, CharUnits::Zero()), VisitedVirtualBases); } }; FinalOverriders::FinalOverriders(const CXXRecordDecl *MostDerivedClass, CharUnits MostDerivedClassOffset, const CXXRecordDecl *LayoutClass) : MostDerivedClass(MostDerivedClass), MostDerivedClassOffset(MostDerivedClassOffset), LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()), MostDerivedClassLayout(Context.getASTRecordLayout(MostDerivedClass)) { // Compute base offsets. SubobjectOffsetMapTy SubobjectOffsets; SubobjectOffsetMapTy SubobjectLayoutClassOffsets; SubobjectCountMapTy SubobjectCounts; ComputeBaseOffsets(BaseSubobject(MostDerivedClass, CharUnits::Zero()), /*IsVirtual=*/false, MostDerivedClassOffset, SubobjectOffsets, SubobjectLayoutClassOffsets, SubobjectCounts); // Get the final overriders. CXXFinalOverriderMap FinalOverriders; MostDerivedClass->getFinalOverriders(FinalOverriders); for (const auto &Overrider : FinalOverriders) { const CXXMethodDecl *MD = Overrider.first; const OverridingMethods &Methods = Overrider.second; for (const auto &M : Methods) { unsigned SubobjectNumber = M.first; assert(SubobjectOffsets.count(std::make_pair(MD->getParent(), SubobjectNumber)) && "Did not find subobject offset!"); CharUnits BaseOffset = SubobjectOffsets[std::make_pair(MD->getParent(), SubobjectNumber)]; assert(M.second.size() == 1 && "Final overrider is not unique!"); const UniqueVirtualMethod &Method = M.second.front(); const CXXRecordDecl *OverriderRD = Method.Method->getParent(); assert(SubobjectLayoutClassOffsets.count( std::make_pair(OverriderRD, Method.Subobject)) && "Did not find subobject offset!"); CharUnits OverriderOffset = SubobjectLayoutClassOffsets[std::make_pair(OverriderRD, Method.Subobject)]; OverriderInfo& Overrider = OverridersMap[std::make_pair(MD, BaseOffset)]; assert(!Overrider.Method && "Overrider should not exist yet!"); Overrider.Offset = OverriderOffset; Overrider.Method = Method.Method; Overrider.VirtualBase = Method.InVirtualSubobject; } } #if DUMP_OVERRIDERS // And dump them (for now). dump(); #endif } static BaseOffset ComputeBaseOffset(const ASTContext &Context, const CXXRecordDecl *DerivedRD, const CXXBasePath &Path) { CharUnits NonVirtualOffset = CharUnits::Zero(); unsigned NonVirtualStart = 0; const CXXRecordDecl *VirtualBase = nullptr; // First, look for the virtual base class. for (int I = Path.size(), E = 0; I != E; --I) { const CXXBasePathElement &Element = Path[I - 1]; if (Element.Base->isVirtual()) { NonVirtualStart = I; QualType VBaseType = Element.Base->getType(); VirtualBase = VBaseType->getAsCXXRecordDecl(); break; } } // Now compute the non-virtual offset. for (unsigned I = NonVirtualStart, E = Path.size(); I != E; ++I) { const CXXBasePathElement &Element = Path[I]; // Check the base class offset. const ASTRecordLayout &Layout = Context.getASTRecordLayout(Element.Class); const CXXRecordDecl *Base = Element.Base->getType()->getAsCXXRecordDecl(); NonVirtualOffset += Layout.getBaseClassOffset(Base); } // FIXME: This should probably use CharUnits or something. Maybe we should // even change the base offsets in ASTRecordLayout to be specified in // CharUnits. return BaseOffset(DerivedRD, VirtualBase, NonVirtualOffset); } static BaseOffset ComputeBaseOffset(const ASTContext &Context, const CXXRecordDecl *BaseRD, const CXXRecordDecl *DerivedRD) { CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true, /*DetectVirtual=*/false); if (!DerivedRD->isDerivedFrom(BaseRD, Paths)) llvm_unreachable("Class must be derived from the passed in base class!"); return ComputeBaseOffset(Context, DerivedRD, Paths.front()); } static BaseOffset ComputeReturnAdjustmentBaseOffset(ASTContext &Context, const CXXMethodDecl *DerivedMD, const CXXMethodDecl *BaseMD) { const auto *BaseFT = BaseMD->getType()->castAs(); const auto *DerivedFT = DerivedMD->getType()->castAs(); // Canonicalize the return types. CanQualType CanDerivedReturnType = Context.getCanonicalType(DerivedFT->getReturnType()); CanQualType CanBaseReturnType = Context.getCanonicalType(BaseFT->getReturnType()); assert(CanDerivedReturnType->getTypeClass() == CanBaseReturnType->getTypeClass() && "Types must have same type class!"); if (CanDerivedReturnType == CanBaseReturnType) { // No adjustment needed. return BaseOffset(); } if (isa(CanDerivedReturnType)) { CanDerivedReturnType = CanDerivedReturnType->getAs()->getPointeeType(); CanBaseReturnType = CanBaseReturnType->getAs()->getPointeeType(); } else if (isa(CanDerivedReturnType)) { CanDerivedReturnType = CanDerivedReturnType->getAs()->getPointeeType(); CanBaseReturnType = CanBaseReturnType->getAs()->getPointeeType(); } else { llvm_unreachable("Unexpected return type!"); } // We need to compare unqualified types here; consider // const T *Base::foo(); // T *Derived::foo(); if (CanDerivedReturnType.getUnqualifiedType() == CanBaseReturnType.getUnqualifiedType()) { // No adjustment needed. return BaseOffset(); } const CXXRecordDecl *DerivedRD = cast(cast(CanDerivedReturnType)->getDecl()); const CXXRecordDecl *BaseRD = cast(cast(CanBaseReturnType)->getDecl()); return ComputeBaseOffset(Context, BaseRD, DerivedRD); } void FinalOverriders::ComputeBaseOffsets(BaseSubobject Base, bool IsVirtual, CharUnits OffsetInLayoutClass, SubobjectOffsetMapTy &SubobjectOffsets, SubobjectOffsetMapTy &SubobjectLayoutClassOffsets, SubobjectCountMapTy &SubobjectCounts) { const CXXRecordDecl *RD = Base.getBase(); unsigned SubobjectNumber = 0; if (!IsVirtual) SubobjectNumber = ++SubobjectCounts[RD]; // Set up the subobject to offset mapping. assert(!SubobjectOffsets.count(std::make_pair(RD, SubobjectNumber)) && "Subobject offset already exists!"); assert(!SubobjectLayoutClassOffsets.count(std::make_pair(RD, SubobjectNumber)) && "Subobject offset already exists!"); SubobjectOffsets[std::make_pair(RD, SubobjectNumber)] = Base.getBaseOffset(); SubobjectLayoutClassOffsets[std::make_pair(RD, SubobjectNumber)] = OffsetInLayoutClass; // Traverse our bases. for (const auto &B : RD->bases()) { const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); CharUnits BaseOffset; CharUnits BaseOffsetInLayoutClass; if (B.isVirtual()) { // Check if we've visited this virtual base before. if (SubobjectOffsets.count(std::make_pair(BaseDecl, 0))) continue; const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); BaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(BaseDecl); } else { const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); CharUnits Offset = Layout.getBaseClassOffset(BaseDecl); BaseOffset = Base.getBaseOffset() + Offset; BaseOffsetInLayoutClass = OffsetInLayoutClass + Offset; } ComputeBaseOffsets(BaseSubobject(BaseDecl, BaseOffset), B.isVirtual(), BaseOffsetInLayoutClass, SubobjectOffsets, SubobjectLayoutClassOffsets, SubobjectCounts); } } void FinalOverriders::dump(raw_ostream &Out, BaseSubobject Base, VisitedVirtualBasesSetTy &VisitedVirtualBases) { const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); for (const auto &B : RD->bases()) { const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); // Ignore bases that don't have any virtual member functions. if (!BaseDecl->isPolymorphic()) continue; CharUnits BaseOffset; if (B.isVirtual()) { if (!VisitedVirtualBases.insert(BaseDecl).second) { // We've visited this base before. continue; } BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); } else { BaseOffset = Layout.getBaseClassOffset(BaseDecl) + Base.getBaseOffset(); } dump(Out, BaseSubobject(BaseDecl, BaseOffset), VisitedVirtualBases); } Out << "Final overriders for ("; RD->printQualifiedName(Out); Out << ", "; Out << Base.getBaseOffset().getQuantity() << ")\n"; // Now dump the overriders for this base subobject. for (const auto *MD : RD->methods()) { if (!VTableContextBase::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); OverriderInfo Overrider = getOverrider(MD, Base.getBaseOffset()); Out << " "; MD->printQualifiedName(Out); Out << " - ("; Overrider.Method->printQualifiedName(Out); Out << ", " << Overrider.Offset.getQuantity() << ')'; BaseOffset Offset; if (!Overrider.Method->isPureVirtual()) Offset = ComputeReturnAdjustmentBaseOffset(Context, Overrider.Method, MD); if (!Offset.isEmpty()) { Out << " [ret-adj: "; if (Offset.VirtualBase) { Offset.VirtualBase->printQualifiedName(Out); Out << " vbase, "; } Out << Offset.NonVirtualOffset.getQuantity() << " nv]"; } Out << "\n"; } } /// VCallOffsetMap - Keeps track of vcall offsets when building a vtable. struct VCallOffsetMap { typedef std::pair MethodAndOffsetPairTy; /// Offsets - Keeps track of methods and their offsets. // FIXME: This should be a real map and not a vector. SmallVector Offsets; /// MethodsCanShareVCallOffset - Returns whether two virtual member functions /// can share the same vcall offset. static bool MethodsCanShareVCallOffset(const CXXMethodDecl *LHS, const CXXMethodDecl *RHS); public: /// AddVCallOffset - Adds a vcall offset to the map. Returns true if the /// add was successful, or false if there was already a member function with /// the same signature in the map. bool AddVCallOffset(const CXXMethodDecl *MD, CharUnits OffsetOffset); /// getVCallOffsetOffset - Returns the vcall offset offset (relative to the /// vtable address point) for the given virtual member function. CharUnits getVCallOffsetOffset(const CXXMethodDecl *MD); // empty - Return whether the offset map is empty or not. bool empty() const { return Offsets.empty(); } }; static bool HasSameVirtualSignature(const CXXMethodDecl *LHS, const CXXMethodDecl *RHS) { const FunctionProtoType *LT = cast(LHS->getType().getCanonicalType()); const FunctionProtoType *RT = cast(RHS->getType().getCanonicalType()); // Fast-path matches in the canonical types. if (LT == RT) return true; // Force the signatures to match. We can't rely on the overrides // list here because there isn't necessarily an inheritance // relationship between the two methods. if (LT->getMethodQuals() != RT->getMethodQuals()) return false; return LT->getParamTypes() == RT->getParamTypes(); } bool VCallOffsetMap::MethodsCanShareVCallOffset(const CXXMethodDecl *LHS, const CXXMethodDecl *RHS) { assert(VTableContextBase::hasVtableSlot(LHS) && "LHS must be virtual!"); assert(VTableContextBase::hasVtableSlot(RHS) && "RHS must be virtual!"); // A destructor can share a vcall offset with another destructor. if (isa(LHS)) return isa(RHS); // FIXME: We need to check more things here. // The methods must have the same name. DeclarationName LHSName = LHS->getDeclName(); DeclarationName RHSName = RHS->getDeclName(); if (LHSName != RHSName) return false; // And the same signatures. return HasSameVirtualSignature(LHS, RHS); } bool VCallOffsetMap::AddVCallOffset(const CXXMethodDecl *MD, CharUnits OffsetOffset) { // Check if we can reuse an offset. for (const auto &OffsetPair : Offsets) { if (MethodsCanShareVCallOffset(OffsetPair.first, MD)) return false; } // Add the offset. Offsets.push_back(MethodAndOffsetPairTy(MD, OffsetOffset)); return true; } CharUnits VCallOffsetMap::getVCallOffsetOffset(const CXXMethodDecl *MD) { // Look for an offset. for (const auto &OffsetPair : Offsets) { if (MethodsCanShareVCallOffset(OffsetPair.first, MD)) return OffsetPair.second; } llvm_unreachable("Should always find a vcall offset offset!"); } /// VCallAndVBaseOffsetBuilder - Class for building vcall and vbase offsets. class VCallAndVBaseOffsetBuilder { public: typedef llvm::DenseMap VBaseOffsetOffsetsMapTy; private: const ItaniumVTableContext &VTables; /// MostDerivedClass - The most derived class for which we're building vcall /// and vbase offsets. const CXXRecordDecl *MostDerivedClass; /// LayoutClass - The class we're using for layout information. Will be /// different than the most derived class if we're building a construction /// vtable. const CXXRecordDecl *LayoutClass; /// Context - The ASTContext which we will use for layout information. ASTContext &Context; /// Components - vcall and vbase offset components typedef SmallVector VTableComponentVectorTy; VTableComponentVectorTy Components; /// VisitedVirtualBases - Visited virtual bases. llvm::SmallPtrSet VisitedVirtualBases; /// VCallOffsets - Keeps track of vcall offsets. VCallOffsetMap VCallOffsets; /// VBaseOffsetOffsets - Contains the offsets of the virtual base offsets, /// relative to the address point. VBaseOffsetOffsetsMapTy VBaseOffsetOffsets; /// FinalOverriders - The final overriders of the most derived class. /// (Can be null when we're not building a vtable of the most derived class). const FinalOverriders *Overriders; /// AddVCallAndVBaseOffsets - Add vcall offsets and vbase offsets for the /// given base subobject. void AddVCallAndVBaseOffsets(BaseSubobject Base, bool BaseIsVirtual, CharUnits RealBaseOffset); /// AddVCallOffsets - Add vcall offsets for the given base subobject. void AddVCallOffsets(BaseSubobject Base, CharUnits VBaseOffset); /// AddVBaseOffsets - Add vbase offsets for the given class. void AddVBaseOffsets(const CXXRecordDecl *Base, CharUnits OffsetInLayoutClass); /// getCurrentOffsetOffset - Get the current vcall or vbase offset offset in /// chars, relative to the vtable address point. CharUnits getCurrentOffsetOffset() const; public: VCallAndVBaseOffsetBuilder(const ItaniumVTableContext &VTables, const CXXRecordDecl *MostDerivedClass, const CXXRecordDecl *LayoutClass, const FinalOverriders *Overriders, BaseSubobject Base, bool BaseIsVirtual, CharUnits OffsetInLayoutClass) : VTables(VTables), MostDerivedClass(MostDerivedClass), LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()), Overriders(Overriders) { // Add vcall and vbase offsets. AddVCallAndVBaseOffsets(Base, BaseIsVirtual, OffsetInLayoutClass); } /// Methods for iterating over the components. typedef VTableComponentVectorTy::const_reverse_iterator const_iterator; const_iterator components_begin() const { return Components.rbegin(); } const_iterator components_end() const { return Components.rend(); } const VCallOffsetMap &getVCallOffsets() const { return VCallOffsets; } const VBaseOffsetOffsetsMapTy &getVBaseOffsetOffsets() const { return VBaseOffsetOffsets; } }; void VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base, bool BaseIsVirtual, CharUnits RealBaseOffset) { const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base.getBase()); // Itanium C++ ABI 2.5.2: // ..in classes sharing a virtual table with a primary base class, the vcall // and vbase offsets added by the derived class all come before the vcall // and vbase offsets required by the base class, so that the latter may be // laid out as required by the base class without regard to additions from // the derived class(es). // (Since we're emitting the vcall and vbase offsets in reverse order, we'll // emit them for the primary base first). if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { bool PrimaryBaseIsVirtual = Layout.isPrimaryBaseVirtual(); CharUnits PrimaryBaseOffset; // Get the base offset of the primary base. if (PrimaryBaseIsVirtual) { assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = Context.getASTRecordLayout(MostDerivedClass); PrimaryBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(PrimaryBase); } else { assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); } AddVCallAndVBaseOffsets( BaseSubobject(PrimaryBase,PrimaryBaseOffset), PrimaryBaseIsVirtual, RealBaseOffset); } AddVBaseOffsets(Base.getBase(), RealBaseOffset); // We only want to add vcall offsets for virtual bases. if (BaseIsVirtual) AddVCallOffsets(Base, RealBaseOffset); } CharUnits VCallAndVBaseOffsetBuilder::getCurrentOffsetOffset() const { // OffsetIndex is the index of this vcall or vbase offset, relative to the // vtable address point. (We subtract 3 to account for the information just // above the address point, the RTTI info, the offset to top, and the // vcall offset itself). size_t NumComponentsAboveAddrPoint = 3; if (Context.getLangOpts().OmitVTableRTTI) NumComponentsAboveAddrPoint--; int64_t OffsetIndex = -(int64_t)(NumComponentsAboveAddrPoint + Components.size()); // Under the relative ABI, the offset widths are 32-bit ints instead of // pointer widths. CharUnits OffsetWidth = Context.toCharUnitsFromBits( VTables.isRelativeLayout() ? 32 : Context.getTargetInfo().getPointerWidth(LangAS::Default)); CharUnits OffsetOffset = OffsetWidth * OffsetIndex; return OffsetOffset; } void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base, CharUnits VBaseOffset) { const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); // Handle the primary base first. // We only want to add vcall offsets if the base is non-virtual; a virtual // primary base will have its vcall and vbase offsets emitted already. if (PrimaryBase && !Layout.isPrimaryBaseVirtual()) { // Get the base offset of the primary base. assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); AddVCallOffsets(BaseSubobject(PrimaryBase, Base.getBaseOffset()), VBaseOffset); } // Add the vcall offsets. for (const auto *MD : RD->methods()) { if (!VTableContextBase::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); CharUnits OffsetOffset = getCurrentOffsetOffset(); // Don't add a vcall offset if we already have one for this member function // signature. if (!VCallOffsets.AddVCallOffset(MD, OffsetOffset)) continue; CharUnits Offset = CharUnits::Zero(); if (Overriders) { // Get the final overrider. FinalOverriders::OverriderInfo Overrider = Overriders->getOverrider(MD, Base.getBaseOffset()); /// The vcall offset is the offset from the virtual base to the object /// where the function was overridden. Offset = Overrider.Offset - VBaseOffset; } Components.push_back( VTableComponent::MakeVCallOffset(Offset)); } // And iterate over all non-virtual bases (ignoring the primary base). for (const auto &B : RD->bases()) { if (B.isVirtual()) continue; const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); if (BaseDecl == PrimaryBase) continue; // Get the base offset of this base. CharUnits BaseOffset = Base.getBaseOffset() + Layout.getBaseClassOffset(BaseDecl); AddVCallOffsets(BaseSubobject(BaseDecl, BaseOffset), VBaseOffset); } } void VCallAndVBaseOffsetBuilder::AddVBaseOffsets(const CXXRecordDecl *RD, CharUnits OffsetInLayoutClass) { const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); // Add vbase offsets. for (const auto &B : RD->bases()) { const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); // Check if this is a virtual base that we haven't visited before. if (B.isVirtual() && VisitedVirtualBases.insert(BaseDecl).second) { CharUnits Offset = LayoutClassLayout.getVBaseClassOffset(BaseDecl) - OffsetInLayoutClass; // Add the vbase offset offset. assert(!VBaseOffsetOffsets.count(BaseDecl) && "vbase offset offset already exists!"); CharUnits VBaseOffsetOffset = getCurrentOffsetOffset(); VBaseOffsetOffsets.insert( std::make_pair(BaseDecl, VBaseOffsetOffset)); Components.push_back( VTableComponent::MakeVBaseOffset(Offset)); } // Check the base class looking for more vbase offsets. AddVBaseOffsets(BaseDecl, OffsetInLayoutClass); } } /// ItaniumVTableBuilder - Class for building vtable layout information. class ItaniumVTableBuilder { public: /// PrimaryBasesSetVectorTy - A set vector of direct and indirect /// primary bases. typedef llvm::SmallSetVector PrimaryBasesSetVectorTy; typedef llvm::DenseMap VBaseOffsetOffsetsMapTy; typedef VTableLayout::AddressPointsMapTy AddressPointsMapTy; typedef llvm::DenseMap MethodVTableIndicesTy; private: /// VTables - Global vtable information. ItaniumVTableContext &VTables; /// MostDerivedClass - The most derived class for which we're building this /// vtable. const CXXRecordDecl *MostDerivedClass; /// MostDerivedClassOffset - If we're building a construction vtable, this /// holds the offset from the layout class to the most derived class. const CharUnits MostDerivedClassOffset; /// MostDerivedClassIsVirtual - Whether the most derived class is a virtual /// base. (This only makes sense when building a construction vtable). bool MostDerivedClassIsVirtual; /// LayoutClass - The class we're using for layout information. Will be /// different than the most derived class if we're building a construction /// vtable. const CXXRecordDecl *LayoutClass; /// Context - The ASTContext which we will use for layout information. ASTContext &Context; /// FinalOverriders - The final overriders of the most derived class. const FinalOverriders Overriders; /// VCallOffsetsForVBases - Keeps track of vcall offsets for the virtual /// bases in this vtable. llvm::DenseMap VCallOffsetsForVBases; /// VBaseOffsetOffsets - Contains the offsets of the virtual base offsets for /// the most derived class. VBaseOffsetOffsetsMapTy VBaseOffsetOffsets; /// Components - The components of the vtable being built. SmallVector Components; /// AddressPoints - Address points for the vtable being built. AddressPointsMapTy AddressPoints; /// MethodInfo - Contains information about a method in a vtable. /// (Used for computing 'this' pointer adjustment thunks. struct MethodInfo { /// BaseOffset - The base offset of this method. const CharUnits BaseOffset; /// BaseOffsetInLayoutClass - The base offset in the layout class of this /// method. const CharUnits BaseOffsetInLayoutClass; /// VTableIndex - The index in the vtable that this method has. /// (For destructors, this is the index of the complete destructor). const uint64_t VTableIndex; MethodInfo(CharUnits BaseOffset, CharUnits BaseOffsetInLayoutClass, uint64_t VTableIndex) : BaseOffset(BaseOffset), BaseOffsetInLayoutClass(BaseOffsetInLayoutClass), VTableIndex(VTableIndex) { } MethodInfo() : BaseOffset(CharUnits::Zero()), BaseOffsetInLayoutClass(CharUnits::Zero()), VTableIndex(0) { } MethodInfo(MethodInfo const&) = default; }; typedef llvm::DenseMap MethodInfoMapTy; /// MethodInfoMap - The information for all methods in the vtable we're /// currently building. MethodInfoMapTy MethodInfoMap; /// MethodVTableIndices - Contains the index (relative to the vtable address /// point) where the function pointer for a virtual function is stored. MethodVTableIndicesTy MethodVTableIndices; typedef llvm::DenseMap VTableThunksMapTy; /// VTableThunks - The thunks by vtable index in the vtable currently being /// built. VTableThunksMapTy VTableThunks; typedef SmallVector ThunkInfoVectorTy; typedef llvm::DenseMap ThunksMapTy; /// Thunks - A map that contains all the thunks needed for all methods in the /// most derived class for which the vtable is currently being built. ThunksMapTy Thunks; /// AddThunk - Add a thunk for the given method. void AddThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk); /// ComputeThisAdjustments - Compute the 'this' pointer adjustments for the /// part of the vtable we're currently building. void ComputeThisAdjustments(); typedef llvm::SmallPtrSet VisitedVirtualBasesSetTy; /// PrimaryVirtualBases - All known virtual bases who are a primary base of /// some other base. VisitedVirtualBasesSetTy PrimaryVirtualBases; /// ComputeReturnAdjustment - Compute the return adjustment given a return /// adjustment base offset. ReturnAdjustment ComputeReturnAdjustment(BaseOffset Offset); /// ComputeThisAdjustmentBaseOffset - Compute the base offset for adjusting /// the 'this' pointer from the base subobject to the derived subobject. BaseOffset ComputeThisAdjustmentBaseOffset(BaseSubobject Base, BaseSubobject Derived) const; /// ComputeThisAdjustment - Compute the 'this' pointer adjustment for the /// given virtual member function, its offset in the layout class and its /// final overrider. ThisAdjustment ComputeThisAdjustment(const CXXMethodDecl *MD, CharUnits BaseOffsetInLayoutClass, FinalOverriders::OverriderInfo Overrider); /// AddMethod - Add a single virtual member function to the vtable /// components vector. void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment); /// IsOverriderUsed - Returns whether the overrider will ever be used in this /// part of the vtable. /// /// Itanium C++ ABI 2.5.2: /// /// struct A { virtual void f(); }; /// struct B : virtual public A { int i; }; /// struct C : virtual public A { int j; }; /// struct D : public B, public C {}; /// /// When B and C are declared, A is a primary base in each case, so although /// vcall offsets are allocated in the A-in-B and A-in-C vtables, no this /// adjustment is required and no thunk is generated. However, inside D /// objects, A is no longer a primary base of C, so if we allowed calls to /// C::f() to use the copy of A's vtable in the C subobject, we would need /// to adjust this from C* to B::A*, which would require a third-party /// thunk. Since we require that a call to C::f() first convert to A*, /// C-in-D's copy of A's vtable is never referenced, so this is not /// necessary. bool IsOverriderUsed(const CXXMethodDecl *Overrider, CharUnits BaseOffsetInLayoutClass, const CXXRecordDecl *FirstBaseInPrimaryBaseChain, CharUnits FirstBaseOffsetInLayoutClass) const; /// AddMethods - Add the methods of this base subobject and all its /// primary bases to the vtable components vector. void AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, const CXXRecordDecl *FirstBaseInPrimaryBaseChain, CharUnits FirstBaseOffsetInLayoutClass, PrimaryBasesSetVectorTy &PrimaryBases); // LayoutVTable - Layout the vtable for the given base class, including its // secondary vtables and any vtables for virtual bases. void LayoutVTable(); /// LayoutPrimaryAndSecondaryVTables - Layout the primary vtable for the /// given base subobject, as well as all its secondary vtables. /// /// \param BaseIsMorallyVirtual whether the base subobject is a virtual base /// or a direct or indirect base of a virtual base. /// /// \param BaseIsVirtualInLayoutClass - Whether the base subobject is virtual /// in the layout class. void LayoutPrimaryAndSecondaryVTables(BaseSubobject Base, bool BaseIsMorallyVirtual, bool BaseIsVirtualInLayoutClass, CharUnits OffsetInLayoutClass); /// LayoutSecondaryVTables - Layout the secondary vtables for the given base /// subobject. /// /// \param BaseIsMorallyVirtual whether the base subobject is a virtual base /// or a direct or indirect base of a virtual base. void LayoutSecondaryVTables(BaseSubobject Base, bool BaseIsMorallyVirtual, CharUnits OffsetInLayoutClass); /// DeterminePrimaryVirtualBases - Determine the primary virtual bases in this /// class hierarchy. void DeterminePrimaryVirtualBases(const CXXRecordDecl *RD, CharUnits OffsetInLayoutClass, VisitedVirtualBasesSetTy &VBases); /// LayoutVTablesForVirtualBases - Layout vtables for all virtual bases of the /// given base (excluding any primary bases). void LayoutVTablesForVirtualBases(const CXXRecordDecl *RD, VisitedVirtualBasesSetTy &VBases); /// isBuildingConstructionVTable - Return whether this vtable builder is /// building a construction vtable. bool isBuildingConstructorVTable() const { return MostDerivedClass != LayoutClass; } public: /// Component indices of the first component of each of the vtables in the /// vtable group. SmallVector VTableIndices; ItaniumVTableBuilder(ItaniumVTableContext &VTables, const CXXRecordDecl *MostDerivedClass, CharUnits MostDerivedClassOffset, bool MostDerivedClassIsVirtual, const CXXRecordDecl *LayoutClass) : VTables(VTables), MostDerivedClass(MostDerivedClass), MostDerivedClassOffset(MostDerivedClassOffset), MostDerivedClassIsVirtual(MostDerivedClassIsVirtual), LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()), Overriders(MostDerivedClass, MostDerivedClassOffset, LayoutClass) { assert(!Context.getTargetInfo().getCXXABI().isMicrosoft()); LayoutVTable(); if (Context.getLangOpts().DumpVTableLayouts) dumpLayout(llvm::outs()); } uint64_t getNumThunks() const { return Thunks.size(); } ThunksMapTy::const_iterator thunks_begin() const { return Thunks.begin(); } ThunksMapTy::const_iterator thunks_end() const { return Thunks.end(); } const VBaseOffsetOffsetsMapTy &getVBaseOffsetOffsets() const { return VBaseOffsetOffsets; } const AddressPointsMapTy &getAddressPoints() const { return AddressPoints; } MethodVTableIndicesTy::const_iterator vtable_indices_begin() const { return MethodVTableIndices.begin(); } MethodVTableIndicesTy::const_iterator vtable_indices_end() const { return MethodVTableIndices.end(); } ArrayRef vtable_components() const { return Components; } AddressPointsMapTy::const_iterator address_points_begin() const { return AddressPoints.begin(); } AddressPointsMapTy::const_iterator address_points_end() const { return AddressPoints.end(); } VTableThunksMapTy::const_iterator vtable_thunks_begin() const { return VTableThunks.begin(); } VTableThunksMapTy::const_iterator vtable_thunks_end() const { return VTableThunks.end(); } /// dumpLayout - Dump the vtable layout. void dumpLayout(raw_ostream&); }; void ItaniumVTableBuilder::AddThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk) { assert(!isBuildingConstructorVTable() && "Can't add thunks for construction vtable"); SmallVectorImpl &ThunksVector = Thunks[MD]; // Check if we have this thunk already. if (llvm::is_contained(ThunksVector, Thunk)) return; ThunksVector.push_back(Thunk); } typedef llvm::SmallPtrSet OverriddenMethodsSetTy; /// Visit all the methods overridden by the given method recursively, /// in a depth-first pre-order. The Visitor's visitor method returns a bool /// indicating whether to continue the recursion for the given overridden /// method (i.e. returning false stops the iteration). template static void visitAllOverriddenMethods(const CXXMethodDecl *MD, VisitorTy &Visitor) { assert(VTableContextBase::hasVtableSlot(MD) && "Method is not virtual!"); for (const CXXMethodDecl *OverriddenMD : MD->overridden_methods()) { if (!Visitor(OverriddenMD)) continue; visitAllOverriddenMethods(OverriddenMD, Visitor); } } /// ComputeAllOverriddenMethods - Given a method decl, will return a set of all /// the overridden methods that the function decl overrides. static void ComputeAllOverriddenMethods(const CXXMethodDecl *MD, OverriddenMethodsSetTy& OverriddenMethods) { auto OverriddenMethodsCollector = [&](const CXXMethodDecl *MD) { // Don't recurse on this method if we've already collected it. return OverriddenMethods.insert(MD).second; }; visitAllOverriddenMethods(MD, OverriddenMethodsCollector); } void ItaniumVTableBuilder::ComputeThisAdjustments() { // Now go through the method info map and see if any of the methods need // 'this' pointer adjustments. for (const auto &MI : MethodInfoMap) { const CXXMethodDecl *MD = MI.first; const MethodInfo &MethodInfo = MI.second; // Ignore adjustments for unused function pointers. uint64_t VTableIndex = MethodInfo.VTableIndex; if (Components[VTableIndex].getKind() == VTableComponent::CK_UnusedFunctionPointer) continue; // Get the final overrider for this method. FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, MethodInfo.BaseOffset); // Check if we need an adjustment at all. if (MethodInfo.BaseOffsetInLayoutClass == Overrider.Offset) { // When a return thunk is needed by a derived class that overrides a // virtual base, gcc uses a virtual 'this' adjustment as well. // While the thunk itself might be needed by vtables in subclasses or // in construction vtables, there doesn't seem to be a reason for using // the thunk in this vtable. Still, we do so to match gcc. if (VTableThunks.lookup(VTableIndex).Return.isEmpty()) continue; } ThisAdjustment ThisAdjustment = ComputeThisAdjustment(MD, MethodInfo.BaseOffsetInLayoutClass, Overrider); if (ThisAdjustment.isEmpty()) continue; // Add it. auto SetThisAdjustmentThunk = [&](uint64_t Idx) { // If a this pointer adjustment is required, record the method that // created the vtable entry. MD is not necessarily the method that // created the entry since derived classes overwrite base class // information in MethodInfoMap, hence findOriginalMethodInMap is called // here. // // For example, in the following class hierarchy, if MD = D1::m and // Overrider = D2:m, the original method that created the entry is B0:m, // which is what findOriginalMethodInMap(MD) returns: // // struct B0 { int a; virtual void m(); }; // struct D0 : B0 { int a; void m() override; }; // struct D1 : B0 { int a; void m() override; }; // struct D2 : D0, D1 { int a; void m() override; }; // // We need to record the method because we cannot // call findOriginalMethod to find the method that created the entry if // the method in the entry requires adjustment. // // Do not set ThunkInfo::Method if Idx is already in VTableThunks. This // can happen when covariant return adjustment is required too. if (!VTableThunks.count(Idx)) { const CXXMethodDecl *Method = VTables.findOriginalMethodInMap(MD); VTableThunks[Idx].Method = Method; VTableThunks[Idx].ThisType = Method->getThisType().getTypePtr(); } VTableThunks[Idx].This = ThisAdjustment; }; SetThisAdjustmentThunk(VTableIndex); if (isa(MD)) { // Add an adjustment for the deleting destructor as well. SetThisAdjustmentThunk(VTableIndex + 1); } } /// Clear the method info map. MethodInfoMap.clear(); if (isBuildingConstructorVTable()) { // We don't need to store thunk information for construction vtables. return; } for (const auto &TI : VTableThunks) { const VTableComponent &Component = Components[TI.first]; const ThunkInfo &Thunk = TI.second; const CXXMethodDecl *MD; switch (Component.getKind()) { default: llvm_unreachable("Unexpected vtable component kind!"); case VTableComponent::CK_FunctionPointer: MD = Component.getFunctionDecl(); break; case VTableComponent::CK_CompleteDtorPointer: MD = Component.getDestructorDecl(); break; case VTableComponent::CK_DeletingDtorPointer: // We've already added the thunk when we saw the complete dtor pointer. continue; } if (MD->getParent() == MostDerivedClass) AddThunk(MD, Thunk); } } ReturnAdjustment ItaniumVTableBuilder::ComputeReturnAdjustment(BaseOffset Offset) { ReturnAdjustment Adjustment; if (!Offset.isEmpty()) { if (Offset.VirtualBase) { // Get the virtual base offset offset. if (Offset.DerivedClass == MostDerivedClass) { // We can get the offset offset directly from our map. Adjustment.Virtual.Itanium.VBaseOffsetOffset = VBaseOffsetOffsets.lookup(Offset.VirtualBase).getQuantity(); } else { Adjustment.Virtual.Itanium.VBaseOffsetOffset = VTables.getVirtualBaseOffsetOffset(Offset.DerivedClass, Offset.VirtualBase).getQuantity(); } } Adjustment.NonVirtual = Offset.NonVirtualOffset.getQuantity(); } return Adjustment; } BaseOffset ItaniumVTableBuilder::ComputeThisAdjustmentBaseOffset( BaseSubobject Base, BaseSubobject Derived) const { const CXXRecordDecl *BaseRD = Base.getBase(); const CXXRecordDecl *DerivedRD = Derived.getBase(); CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, /*DetectVirtual=*/true); if (!DerivedRD->isDerivedFrom(BaseRD, Paths)) llvm_unreachable("Class must be derived from the passed in base class!"); // We have to go through all the paths, and see which one leads us to the // right base subobject. for (const CXXBasePath &Path : Paths) { BaseOffset Offset = ComputeBaseOffset(Context, DerivedRD, Path); CharUnits OffsetToBaseSubobject = Offset.NonVirtualOffset; if (Offset.VirtualBase) { // If we have a virtual base class, the non-virtual offset is relative // to the virtual base class offset. const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); /// Get the virtual base offset, relative to the most derived class /// layout. OffsetToBaseSubobject += LayoutClassLayout.getVBaseClassOffset(Offset.VirtualBase); } else { // Otherwise, the non-virtual offset is relative to the derived class // offset. OffsetToBaseSubobject += Derived.getBaseOffset(); } // Check if this path gives us the right base subobject. if (OffsetToBaseSubobject == Base.getBaseOffset()) { // Since we're going from the base class _to_ the derived class, we'll // invert the non-virtual offset here. Offset.NonVirtualOffset = -Offset.NonVirtualOffset; return Offset; } } return BaseOffset(); } ThisAdjustment ItaniumVTableBuilder::ComputeThisAdjustment( const CXXMethodDecl *MD, CharUnits BaseOffsetInLayoutClass, FinalOverriders::OverriderInfo Overrider) { // Ignore adjustments for pure virtual member functions. if (Overrider.Method->isPureVirtual()) return ThisAdjustment(); BaseSubobject OverriddenBaseSubobject(MD->getParent(), BaseOffsetInLayoutClass); BaseSubobject OverriderBaseSubobject(Overrider.Method->getParent(), Overrider.Offset); // Compute the adjustment offset. BaseOffset Offset = ComputeThisAdjustmentBaseOffset(OverriddenBaseSubobject, OverriderBaseSubobject); if (Offset.isEmpty()) return ThisAdjustment(); ThisAdjustment Adjustment; if (Offset.VirtualBase) { // Get the vcall offset map for this virtual base. VCallOffsetMap &VCallOffsets = VCallOffsetsForVBases[Offset.VirtualBase]; if (VCallOffsets.empty()) { // We don't have vcall offsets for this virtual base, go ahead and // build them. VCallAndVBaseOffsetBuilder Builder( VTables, MostDerivedClass, MostDerivedClass, /*Overriders=*/nullptr, BaseSubobject(Offset.VirtualBase, CharUnits::Zero()), /*BaseIsVirtual=*/true, /*OffsetInLayoutClass=*/ CharUnits::Zero()); VCallOffsets = Builder.getVCallOffsets(); } Adjustment.Virtual.Itanium.VCallOffsetOffset = VCallOffsets.getVCallOffsetOffset(MD).getQuantity(); } // Set the non-virtual part of the adjustment. Adjustment.NonVirtual = Offset.NonVirtualOffset.getQuantity(); return Adjustment; } void ItaniumVTableBuilder::AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment) { if (const CXXDestructorDecl *DD = dyn_cast(MD)) { assert(ReturnAdjustment.isEmpty() && "Destructor can't have return adjustment!"); // Add both the complete destructor and the deleting destructor. Components.push_back(VTableComponent::MakeCompleteDtor(DD)); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { // Add the return adjustment if necessary. if (!ReturnAdjustment.isEmpty()) VTableThunks[Components.size()].Return = ReturnAdjustment; // Add the function. Components.push_back(VTableComponent::MakeFunction(MD)); } } /// OverridesIndirectMethodInBase - Return whether the given member function /// overrides any methods in the set of given bases. /// Unlike OverridesMethodInBase, this checks "overriders of overriders". /// For example, if we have: /// /// struct A { virtual void f(); } /// struct B : A { virtual void f(); } /// struct C : B { virtual void f(); } /// /// OverridesIndirectMethodInBase will return true if given C::f as the method /// and { A } as the set of bases. static bool OverridesIndirectMethodInBases( const CXXMethodDecl *MD, ItaniumVTableBuilder::PrimaryBasesSetVectorTy &Bases) { if (Bases.count(MD->getParent())) return true; for (const CXXMethodDecl *OverriddenMD : MD->overridden_methods()) { // Check "indirect overriders". if (OverridesIndirectMethodInBases(OverriddenMD, Bases)) return true; } return false; } bool ItaniumVTableBuilder::IsOverriderUsed( const CXXMethodDecl *Overrider, CharUnits BaseOffsetInLayoutClass, const CXXRecordDecl *FirstBaseInPrimaryBaseChain, CharUnits FirstBaseOffsetInLayoutClass) const { // If the base and the first base in the primary base chain have the same // offsets, then this overrider will be used. if (BaseOffsetInLayoutClass == FirstBaseOffsetInLayoutClass) return true; // We know now that Base (or a direct or indirect base of it) is a primary // base in part of the class hierarchy, but not a primary base in the most // derived class. // If the overrider is the first base in the primary base chain, we know // that the overrider will be used. if (Overrider->getParent() == FirstBaseInPrimaryBaseChain) return true; ItaniumVTableBuilder::PrimaryBasesSetVectorTy PrimaryBases; const CXXRecordDecl *RD = FirstBaseInPrimaryBaseChain; PrimaryBases.insert(RD); // Now traverse the base chain, starting with the first base, until we find // the base that is no longer a primary base. while (true) { const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); if (!PrimaryBase) break; if (Layout.isPrimaryBaseVirtual()) { assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); // Now check if this is the primary base that is not a primary base in the // most derived class. if (LayoutClassLayout.getVBaseClassOffset(PrimaryBase) != FirstBaseOffsetInLayoutClass) { // We found it, stop walking the chain. break; } } else { assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); } if (!PrimaryBases.insert(PrimaryBase)) llvm_unreachable("Found a duplicate primary base!"); RD = PrimaryBase; } // If the final overrider is an override of one of the primary bases, // then we know that it will be used. return OverridesIndirectMethodInBases(Overrider, PrimaryBases); } typedef llvm::SmallSetVector BasesSetVectorTy; /// FindNearestOverriddenMethod - Given a method, returns the overridden method /// from the nearest base. Returns null if no method was found. /// The Bases are expected to be sorted in a base-to-derived order. static const CXXMethodDecl * FindNearestOverriddenMethod(const CXXMethodDecl *MD, BasesSetVectorTy &Bases) { OverriddenMethodsSetTy OverriddenMethods; ComputeAllOverriddenMethods(MD, OverriddenMethods); for (const CXXRecordDecl *PrimaryBase : llvm::reverse(Bases)) { // Now check the overridden methods. for (const CXXMethodDecl *OverriddenMD : OverriddenMethods) { // We found our overridden method. if (OverriddenMD->getParent() == PrimaryBase) return OverriddenMD; } } return nullptr; } void ItaniumVTableBuilder::AddMethods( BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, const CXXRecordDecl *FirstBaseInPrimaryBaseChain, CharUnits FirstBaseOffsetInLayoutClass, PrimaryBasesSetVectorTy &PrimaryBases) { // Itanium C++ ABI 2.5.2: // The order of the virtual function pointers in a virtual table is the // order of declaration of the corresponding member functions in the class. // // There is an entry for any virtual function declared in a class, // whether it is a new function or overrides a base class function, // unless it overrides a function from the primary base, and conversion // between their return types does not require an adjustment. const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { CharUnits PrimaryBaseOffset; CharUnits PrimaryBaseOffsetInLayoutClass; if (Layout.isPrimaryBaseVirtual()) { assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = Context.getASTRecordLayout(MostDerivedClass); PrimaryBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(PrimaryBase); const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); PrimaryBaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(PrimaryBase); } else { assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); PrimaryBaseOffsetInLayoutClass = BaseOffsetInLayoutClass; } AddMethods(BaseSubobject(PrimaryBase, PrimaryBaseOffset), PrimaryBaseOffsetInLayoutClass, FirstBaseInPrimaryBaseChain, FirstBaseOffsetInLayoutClass, PrimaryBases); if (!PrimaryBases.insert(PrimaryBase)) llvm_unreachable("Found a duplicate primary base!"); } typedef llvm::SmallVector NewVirtualFunctionsTy; NewVirtualFunctionsTy NewVirtualFunctions; llvm::SmallVector NewImplicitVirtualFunctions; // Now go through all virtual member functions and add them. for (const auto *MD : RD->methods()) { if (!ItaniumVTableContext::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); // Get the final overrider. FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, Base.getBaseOffset()); // Check if this virtual member function overrides a method in a primary // base. If this is the case, and the return type doesn't require adjustment // then we can just use the member function from the primary base. if (const CXXMethodDecl *OverriddenMD = FindNearestOverriddenMethod(MD, PrimaryBases)) { if (ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD).isEmpty()) { VTables.setOriginalMethod(MD, OverriddenMD); // Replace the method info of the overridden method with our own // method. assert(MethodInfoMap.count(OverriddenMD) && "Did not find the overridden method!"); MethodInfo &OverriddenMethodInfo = MethodInfoMap[OverriddenMD]; MethodInfo MethodInfo(Base.getBaseOffset(), BaseOffsetInLayoutClass, OverriddenMethodInfo.VTableIndex); assert(!MethodInfoMap.count(MD) && "Should not have method info for this method yet!"); MethodInfoMap.insert(std::make_pair(MD, MethodInfo)); MethodInfoMap.erase(OverriddenMD); // If the overridden method exists in a virtual base class or a direct // or indirect base class of a virtual base class, we need to emit a // thunk if we ever have a class hierarchy where the base class is not // a primary base in the complete object. if (!isBuildingConstructorVTable() && OverriddenMD != MD) { // Compute the this adjustment. ThisAdjustment ThisAdjustment = ComputeThisAdjustment(OverriddenMD, BaseOffsetInLayoutClass, Overrider); if (ThisAdjustment.Virtual.Itanium.VCallOffsetOffset && Overrider.Method->getParent() == MostDerivedClass) { // There's no return adjustment from OverriddenMD and MD, // but that doesn't mean there isn't one between MD and // the final overrider. BaseOffset ReturnAdjustmentOffset = ComputeReturnAdjustmentBaseOffset(Context, Overrider.Method, MD); ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); // This is a virtual thunk for the most derived class, add it. AddThunk(Overrider.Method, ThunkInfo(ThisAdjustment, ReturnAdjustment, OverriddenMD->getThisType().getTypePtr())); } } continue; } } if (MD->isImplicit()) NewImplicitVirtualFunctions.push_back(MD); else NewVirtualFunctions.push_back(MD); } std::stable_sort( NewImplicitVirtualFunctions.begin(), NewImplicitVirtualFunctions.end(), [](const CXXMethodDecl *A, const CXXMethodDecl *B) { if (A == B) return false; if (A->isCopyAssignmentOperator() != B->isCopyAssignmentOperator()) return A->isCopyAssignmentOperator(); if (A->isMoveAssignmentOperator() != B->isMoveAssignmentOperator()) return A->isMoveAssignmentOperator(); if (isa(A) != isa(B)) return isa(A); assert(A->getOverloadedOperator() == OO_EqualEqual && B->getOverloadedOperator() == OO_EqualEqual && "unexpected or duplicate implicit virtual function"); // We rely on Sema to have declared the operator== members in the // same order as the corresponding operator<=> members. return false; }); NewVirtualFunctions.append(NewImplicitVirtualFunctions.begin(), NewImplicitVirtualFunctions.end()); for (const CXXMethodDecl *MD : NewVirtualFunctions) { // Get the final overrider. FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, Base.getBaseOffset()); // Insert the method info for this method. MethodInfo MethodInfo(Base.getBaseOffset(), BaseOffsetInLayoutClass, Components.size()); assert(!MethodInfoMap.count(MD) && "Should not have method info for this method yet!"); MethodInfoMap.insert(std::make_pair(MD, MethodInfo)); // Check if this overrider is going to be used. const CXXMethodDecl *OverriderMD = Overrider.Method; if (!IsOverriderUsed(OverriderMD, BaseOffsetInLayoutClass, FirstBaseInPrimaryBaseChain, FirstBaseOffsetInLayoutClass)) { Components.push_back(VTableComponent::MakeUnusedFunction(OverriderMD)); continue; } // Check if this overrider needs a return adjustment. // We don't want to do this for pure virtual member functions. BaseOffset ReturnAdjustmentOffset; if (!OverriderMD->isPureVirtual()) { ReturnAdjustmentOffset = ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD); } ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); // If a return adjustment is required, record the method that created the // vtable entry. We need to record the method because we cannot call // findOriginalMethod to find the method that created the entry if the // method in the entry requires adjustment. if (!ReturnAdjustment.isEmpty()) { VTableThunks[Components.size()].Method = MD; VTableThunks[Components.size()].ThisType = MD->getThisType().getTypePtr(); } AddMethod(Overrider.Method, ReturnAdjustment); } } void ItaniumVTableBuilder::LayoutVTable() { LayoutPrimaryAndSecondaryVTables(BaseSubobject(MostDerivedClass, CharUnits::Zero()), /*BaseIsMorallyVirtual=*/false, MostDerivedClassIsVirtual, MostDerivedClassOffset); VisitedVirtualBasesSetTy VBases; // Determine the primary virtual bases. DeterminePrimaryVirtualBases(MostDerivedClass, MostDerivedClassOffset, VBases); VBases.clear(); LayoutVTablesForVirtualBases(MostDerivedClass, VBases); // -fapple-kext adds an extra entry at end of vtbl. bool IsAppleKext = Context.getLangOpts().AppleKext; if (IsAppleKext) Components.push_back(VTableComponent::MakeVCallOffset(CharUnits::Zero())); } void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables( BaseSubobject Base, bool BaseIsMorallyVirtual, bool BaseIsVirtualInLayoutClass, CharUnits OffsetInLayoutClass) { assert(Base.getBase()->isDynamicClass() && "class does not have a vtable!"); unsigned VTableIndex = Components.size(); VTableIndices.push_back(VTableIndex); // Add vcall and vbase offsets for this vtable. VCallAndVBaseOffsetBuilder Builder( VTables, MostDerivedClass, LayoutClass, &Overriders, Base, BaseIsVirtualInLayoutClass, OffsetInLayoutClass); Components.append(Builder.components_begin(), Builder.components_end()); // Check if we need to add these vcall offsets. if (BaseIsVirtualInLayoutClass && !Builder.getVCallOffsets().empty()) { VCallOffsetMap &VCallOffsets = VCallOffsetsForVBases[Base.getBase()]; if (VCallOffsets.empty()) VCallOffsets = Builder.getVCallOffsets(); } // If we're laying out the most derived class we want to keep track of the // virtual base class offset offsets. if (Base.getBase() == MostDerivedClass) VBaseOffsetOffsets = Builder.getVBaseOffsetOffsets(); // Add the offset to top. CharUnits OffsetToTop = MostDerivedClassOffset - OffsetInLayoutClass; Components.push_back(VTableComponent::MakeOffsetToTop(OffsetToTop)); // Next, add the RTTI. if (!Context.getLangOpts().OmitVTableRTTI) Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass)); uint64_t AddressPoint = Components.size(); // Now go through all virtual member functions and add them. PrimaryBasesSetVectorTy PrimaryBases; AddMethods(Base, OffsetInLayoutClass, Base.getBase(), OffsetInLayoutClass, PrimaryBases); const CXXRecordDecl *RD = Base.getBase(); if (RD == MostDerivedClass) { assert(MethodVTableIndices.empty()); for (const auto &I : MethodInfoMap) { const CXXMethodDecl *MD = I.first; const MethodInfo &MI = I.second; if (const CXXDestructorDecl *DD = dyn_cast(MD)) { MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = MI.VTableIndex - AddressPoint; MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = MI.VTableIndex + 1 - AddressPoint; } else { MethodVTableIndices[MD] = MI.VTableIndex - AddressPoint; } } } // Compute 'this' pointer adjustments. ComputeThisAdjustments(); // Add all address points. while (true) { AddressPoints.insert( std::make_pair(BaseSubobject(RD, OffsetInLayoutClass), VTableLayout::AddressPointLocation{ unsigned(VTableIndices.size() - 1), unsigned(AddressPoint - VTableIndex)})); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); if (!PrimaryBase) break; if (Layout.isPrimaryBaseVirtual()) { // Check if this virtual primary base is a primary base in the layout // class. If it's not, we don't want to add it. const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); if (LayoutClassLayout.getVBaseClassOffset(PrimaryBase) != OffsetInLayoutClass) { // We don't want to add this class (or any of its primary bases). break; } } RD = PrimaryBase; } // Layout secondary vtables. LayoutSecondaryVTables(Base, BaseIsMorallyVirtual, OffsetInLayoutClass); } void ItaniumVTableBuilder::LayoutSecondaryVTables(BaseSubobject Base, bool BaseIsMorallyVirtual, CharUnits OffsetInLayoutClass) { // Itanium C++ ABI 2.5.2: // Following the primary virtual table of a derived class are secondary // virtual tables for each of its proper base classes, except any primary // base(s) with which it shares its primary virtual table. const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); for (const auto &B : RD->bases()) { // Ignore virtual bases, we'll emit them later. if (B.isVirtual()) continue; const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); // Ignore bases that don't have a vtable. if (!BaseDecl->isDynamicClass()) continue; if (isBuildingConstructorVTable()) { // Itanium C++ ABI 2.6.4: // Some of the base class subobjects may not need construction virtual // tables, which will therefore not be present in the construction // virtual table group, even though the subobject virtual tables are // present in the main virtual table group for the complete object. if (!BaseIsMorallyVirtual && !BaseDecl->getNumVBases()) continue; } // Get the base offset of this base. CharUnits RelativeBaseOffset = Layout.getBaseClassOffset(BaseDecl); CharUnits BaseOffset = Base.getBaseOffset() + RelativeBaseOffset; CharUnits BaseOffsetInLayoutClass = OffsetInLayoutClass + RelativeBaseOffset; // Don't emit a secondary vtable for a primary base. We might however want // to emit secondary vtables for other bases of this base. if (BaseDecl == PrimaryBase) { LayoutSecondaryVTables(BaseSubobject(BaseDecl, BaseOffset), BaseIsMorallyVirtual, BaseOffsetInLayoutClass); continue; } // Layout the primary vtable (and any secondary vtables) for this base. LayoutPrimaryAndSecondaryVTables( BaseSubobject(BaseDecl, BaseOffset), BaseIsMorallyVirtual, /*BaseIsVirtualInLayoutClass=*/false, BaseOffsetInLayoutClass); } } void ItaniumVTableBuilder::DeterminePrimaryVirtualBases( const CXXRecordDecl *RD, CharUnits OffsetInLayoutClass, VisitedVirtualBasesSetTy &VBases) { const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); // Check if this base has a primary base. if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { // Check if it's virtual. if (Layout.isPrimaryBaseVirtual()) { bool IsPrimaryVirtualBase = true; if (isBuildingConstructorVTable()) { // Check if the base is actually a primary base in the class we use for // layout. const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); CharUnits PrimaryBaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(PrimaryBase); // We know that the base is not a primary base in the layout class if // the base offsets are different. if (PrimaryBaseOffsetInLayoutClass != OffsetInLayoutClass) IsPrimaryVirtualBase = false; } if (IsPrimaryVirtualBase) PrimaryVirtualBases.insert(PrimaryBase); } } // Traverse bases, looking for more primary virtual bases. for (const auto &B : RD->bases()) { const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); CharUnits BaseOffsetInLayoutClass; if (B.isVirtual()) { if (!VBases.insert(BaseDecl).second) continue; const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); BaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(BaseDecl); } else { BaseOffsetInLayoutClass = OffsetInLayoutClass + Layout.getBaseClassOffset(BaseDecl); } DeterminePrimaryVirtualBases(BaseDecl, BaseOffsetInLayoutClass, VBases); } } void ItaniumVTableBuilder::LayoutVTablesForVirtualBases( const CXXRecordDecl *RD, VisitedVirtualBasesSetTy &VBases) { // Itanium C++ ABI 2.5.2: // Then come the virtual base virtual tables, also in inheritance graph // order, and again excluding primary bases (which share virtual tables with // the classes for which they are primary). for (const auto &B : RD->bases()) { const CXXRecordDecl *BaseDecl = B.getType()->getAsCXXRecordDecl(); // Check if this base needs a vtable. (If it's virtual, not a primary base // of some other class, and we haven't visited it before). if (B.isVirtual() && BaseDecl->isDynamicClass() && !PrimaryVirtualBases.count(BaseDecl) && VBases.insert(BaseDecl).second) { const ASTRecordLayout &MostDerivedClassLayout = Context.getASTRecordLayout(MostDerivedClass); CharUnits BaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl); const ASTRecordLayout &LayoutClassLayout = Context.getASTRecordLayout(LayoutClass); CharUnits BaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(BaseDecl); LayoutPrimaryAndSecondaryVTables( BaseSubobject(BaseDecl, BaseOffset), /*BaseIsMorallyVirtual=*/true, /*BaseIsVirtualInLayoutClass=*/true, BaseOffsetInLayoutClass); } // We only need to check the base for virtual base vtables if it actually // has virtual bases. if (BaseDecl->getNumVBases()) LayoutVTablesForVirtualBases(BaseDecl, VBases); } } static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) { if (!Info.Method) return; std::string Str = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, Info.Method); Out << " method: " << Str; } /// dumpLayout - Dump the vtable layout. void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { // FIXME: write more tests that actually use the dumpLayout output to prevent // ItaniumVTableBuilder regressions. Out << "Original map\n"; for (const auto &P : VTables.getOriginalMethodMap()) { std::string Str0 = PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual, P.first); std::string Str1 = PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual, P.second); Out << " " << Str0 << " -> " << Str1 << "\n"; } if (isBuildingConstructorVTable()) { Out << "Construction vtable for ('"; MostDerivedClass->printQualifiedName(Out); Out << "', "; Out << MostDerivedClassOffset.getQuantity() << ") in '"; LayoutClass->printQualifiedName(Out); } else { Out << "Vtable for '"; MostDerivedClass->printQualifiedName(Out); } Out << "' (" << Components.size() << " entries).\n"; // Iterate through the address points and insert them into a new map where // they are keyed by the index and not the base object. // Since an address point can be shared by multiple subobjects, we use an // STL multimap. std::multimap AddressPointsByIndex; for (const auto &AP : AddressPoints) { const BaseSubobject &Base = AP.first; uint64_t Index = VTableIndices[AP.second.VTableIndex] + AP.second.AddressPointIndex; AddressPointsByIndex.insert(std::make_pair(Index, Base)); } for (unsigned I = 0, E = Components.size(); I != E; ++I) { uint64_t Index = I; Out << llvm::format("%4d | ", I); const VTableComponent &Component = Components[I]; // Dump the component. switch (Component.getKind()) { case VTableComponent::CK_VCallOffset: Out << "vcall_offset (" << Component.getVCallOffset().getQuantity() << ")"; break; case VTableComponent::CK_VBaseOffset: Out << "vbase_offset (" << Component.getVBaseOffset().getQuantity() << ")"; break; case VTableComponent::CK_OffsetToTop: Out << "offset_to_top (" << Component.getOffsetToTop().getQuantity() << ")"; break; case VTableComponent::CK_RTTI: Component.getRTTIDecl()->printQualifiedName(Out); Out << " RTTI"; break; case VTableComponent::CK_FunctionPointer: { const CXXMethodDecl *MD = Component.getFunctionDecl(); std::string Str = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); Out << Str; if (MD->isPureVirtual()) Out << " [pure]"; if (MD->isDeleted()) Out << " [deleted]"; ThunkInfo Thunk = VTableThunks.lookup(I); if (!Thunk.isEmpty()) { // If this function pointer has a return adjustment, dump it. if (!Thunk.Return.isEmpty()) { Out << "\n [return adjustment: "; Out << Thunk.Return.NonVirtual << " non-virtual"; if (Thunk.Return.Virtual.Itanium.VBaseOffsetOffset) { Out << ", " << Thunk.Return.Virtual.Itanium.VBaseOffsetOffset; Out << " vbase offset offset"; } Out << ']'; printThunkMethod(Thunk, Out); } // If this function pointer has a 'this' pointer adjustment, dump it. if (!Thunk.This.isEmpty()) { Out << "\n [this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } Out << ']'; printThunkMethod(Thunk, Out); } } break; } case VTableComponent::CK_CompleteDtorPointer: case VTableComponent::CK_DeletingDtorPointer: { bool IsComplete = Component.getKind() == VTableComponent::CK_CompleteDtorPointer; const CXXDestructorDecl *DD = Component.getDestructorDecl(); DD->printQualifiedName(Out); if (IsComplete) Out << "() [complete]"; else Out << "() [deleting]"; if (DD->isPureVirtual()) Out << " [pure]"; ThunkInfo Thunk = VTableThunks.lookup(I); if (!Thunk.isEmpty()) { // If this destructor has a 'this' pointer adjustment, dump it. if (!Thunk.This.isEmpty()) { Out << "\n [this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } Out << ']'; } printThunkMethod(Thunk, Out); } break; } case VTableComponent::CK_UnusedFunctionPointer: { const CXXMethodDecl *MD = Component.getUnusedFunctionDecl(); std::string Str = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); Out << "[unused] " << Str; if (MD->isPureVirtual()) Out << " [pure]"; } } Out << '\n'; // Dump the next address point. uint64_t NextIndex = Index + 1; if (AddressPointsByIndex.count(NextIndex)) { if (AddressPointsByIndex.count(NextIndex) == 1) { const BaseSubobject &Base = AddressPointsByIndex.find(NextIndex)->second; Out << " -- ("; Base.getBase()->printQualifiedName(Out); Out << ", " << Base.getBaseOffset().getQuantity(); Out << ") vtable address --\n"; } else { CharUnits BaseOffset = AddressPointsByIndex.lower_bound(NextIndex)->second.getBaseOffset(); // We store the class names in a set to get a stable order. std::set ClassNames; for (const auto &I : llvm::make_range(AddressPointsByIndex.equal_range(NextIndex))) { assert(I.second.getBaseOffset() == BaseOffset && "Invalid base offset!"); const CXXRecordDecl *RD = I.second.getBase(); ClassNames.insert(RD->getQualifiedNameAsString()); } for (const std::string &Name : ClassNames) { Out << " -- (" << Name; Out << ", " << BaseOffset.getQuantity() << ") vtable address --\n"; } } } } Out << '\n'; if (isBuildingConstructorVTable()) return; if (MostDerivedClass->getNumVBases()) { // We store the virtual base class names and their offsets in a map to get // a stable order. std::map ClassNamesAndOffsets; for (const auto &I : VBaseOffsetOffsets) { std::string ClassName = I.first->getQualifiedNameAsString(); CharUnits OffsetOffset = I.second; ClassNamesAndOffsets.insert(std::make_pair(ClassName, OffsetOffset)); } Out << "Virtual base offset offsets for '"; MostDerivedClass->printQualifiedName(Out); Out << "' ("; Out << ClassNamesAndOffsets.size(); Out << (ClassNamesAndOffsets.size() == 1 ? " entry" : " entries") << ").\n"; for (const auto &I : ClassNamesAndOffsets) Out << " " << I.first << " | " << I.second.getQuantity() << '\n'; Out << "\n"; } if (!Thunks.empty()) { // We store the method names in a map to get a stable order. std::map MethodNamesAndDecls; for (const auto &I : Thunks) { const CXXMethodDecl *MD = I.first; std::string MethodName = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); MethodNamesAndDecls.insert(std::make_pair(MethodName, MD)); } for (const auto &I : MethodNamesAndDecls) { const std::string &MethodName = I.first; const CXXMethodDecl *MD = I.second; ThunkInfoVectorTy ThunksVector = Thunks[MD]; llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) { return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return); }); Out << "Thunks for '" << MethodName << "' (" << ThunksVector.size(); Out << (ThunksVector.size() == 1 ? " entry" : " entries") << ").\n"; for (unsigned I = 0, E = ThunksVector.size(); I != E; ++I) { const ThunkInfo &Thunk = ThunksVector[I]; Out << llvm::format("%4d | ", I); // If this function pointer has a return pointer adjustment, dump it. if (!Thunk.Return.isEmpty()) { Out << "return adjustment: " << Thunk.Return.NonVirtual; Out << " non-virtual"; if (Thunk.Return.Virtual.Itanium.VBaseOffsetOffset) { Out << ", " << Thunk.Return.Virtual.Itanium.VBaseOffsetOffset; Out << " vbase offset offset"; } if (!Thunk.This.isEmpty()) Out << "\n "; } // If this function pointer has a 'this' pointer adjustment, dump it. if (!Thunk.This.isEmpty()) { Out << "this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } } Out << '\n'; } Out << '\n'; } } // Compute the vtable indices for all the member functions. // Store them in a map keyed by the index so we'll get a sorted table. std::map IndicesMap; for (const auto *MD : MostDerivedClass->methods()) { // We only want virtual member functions. if (!ItaniumVTableContext::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); std::string MethodName = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); if (const CXXDestructorDecl *DD = dyn_cast(MD)) { GlobalDecl GD(DD, Dtor_Complete); assert(MethodVTableIndices.count(GD)); uint64_t VTableIndex = MethodVTableIndices[GD]; IndicesMap[VTableIndex] = MethodName + " [complete]"; IndicesMap[VTableIndex + 1] = MethodName + " [deleting]"; } else { assert(MethodVTableIndices.count(MD)); IndicesMap[MethodVTableIndices[MD]] = MethodName; } } // Print the vtable indices for all the member functions. if (!IndicesMap.empty()) { Out << "VTable indices for '"; MostDerivedClass->printQualifiedName(Out); Out << "' (" << IndicesMap.size() << " entries).\n"; for (const auto &I : IndicesMap) { uint64_t VTableIndex = I.first; const std::string &MethodName = I.second; Out << llvm::format("%4" PRIu64 " | ", VTableIndex) << MethodName << '\n'; } } Out << '\n'; } } static VTableLayout::AddressPointsIndexMapTy MakeAddressPointIndices(const VTableLayout::AddressPointsMapTy &addressPoints, unsigned numVTables) { VTableLayout::AddressPointsIndexMapTy indexMap(numVTables); for (auto it = addressPoints.begin(); it != addressPoints.end(); ++it) { const auto &addressPointLoc = it->second; unsigned vtableIndex = addressPointLoc.VTableIndex; unsigned addressPoint = addressPointLoc.AddressPointIndex; if (indexMap[vtableIndex]) { // Multiple BaseSubobjects can map to the same AddressPointLocation, but // every vtable index should have a unique address point. assert(indexMap[vtableIndex] == addressPoint && "Every vtable index should have a unique address point. Found a " "vtable that has two different address points."); } else { indexMap[vtableIndex] = addressPoint; } } // Note that by this point, not all the address may be initialized if the // AddressPoints map is empty. This is ok if the map isn't needed. See // MicrosoftVTableContext::computeVTableRelatedInformation() which uses an // emprt map. return indexMap; } VTableLayout::VTableLayout(ArrayRef VTableIndices, ArrayRef VTableComponents, ArrayRef VTableThunks, const AddressPointsMapTy &AddressPoints) : VTableComponents(VTableComponents), VTableThunks(VTableThunks), AddressPoints(AddressPoints), AddressPointIndices(MakeAddressPointIndices( AddressPoints, VTableIndices.size())) { if (VTableIndices.size() <= 1) assert(VTableIndices.size() == 1 && VTableIndices[0] == 0); else this->VTableIndices = OwningArrayRef(VTableIndices); llvm::sort(this->VTableThunks, [](const VTableLayout::VTableThunkTy &LHS, const VTableLayout::VTableThunkTy &RHS) { assert((LHS.first != RHS.first || LHS.second == RHS.second) && "Different thunks should have unique indices!"); return LHS.first < RHS.first; }); } VTableLayout::~VTableLayout() { } bool VTableContextBase::hasVtableSlot(const CXXMethodDecl *MD) { return MD->isVirtual() && !MD->isImmediateFunction(); } ItaniumVTableContext::ItaniumVTableContext( ASTContext &Context, VTableComponentLayout ComponentLayout) : VTableContextBase(/*MS=*/false), ComponentLayout(ComponentLayout) {} ItaniumVTableContext::~ItaniumVTableContext() {} uint64_t ItaniumVTableContext::getMethodVTableIndex(GlobalDecl GD) { GD = GD.getCanonicalDecl(); MethodVTableIndicesTy::iterator I = MethodVTableIndices.find(GD); if (I != MethodVTableIndices.end()) return I->second; const CXXRecordDecl *RD = cast(GD.getDecl())->getParent(); computeVTableRelatedInformation(RD); I = MethodVTableIndices.find(GD); assert(I != MethodVTableIndices.end() && "Did not find index!"); return I->second; } CharUnits ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, const CXXRecordDecl *VBase) { ClassPairTy ClassPair(RD, VBase); VirtualBaseClassOffsetOffsetsMapTy::iterator I = VirtualBaseClassOffsetOffsets.find(ClassPair); if (I != VirtualBaseClassOffsetOffsets.end()) return I->second; VCallAndVBaseOffsetBuilder Builder(*this, RD, RD, /*Overriders=*/nullptr, BaseSubobject(RD, CharUnits::Zero()), /*BaseIsVirtual=*/false, /*OffsetInLayoutClass=*/CharUnits::Zero()); for (const auto &I : Builder.getVBaseOffsetOffsets()) { // Insert all types. ClassPairTy ClassPair(RD, I.first); VirtualBaseClassOffsetOffsets.insert(std::make_pair(ClassPair, I.second)); } I = VirtualBaseClassOffsetOffsets.find(ClassPair); assert(I != VirtualBaseClassOffsetOffsets.end() && "Did not find index!"); return I->second; } GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) { const auto *MD = cast(GD.getDecl()); computeVTableRelatedInformation(MD->getParent()); const CXXMethodDecl *OriginalMD = findOriginalMethodInMap(MD); if (const auto *DD = dyn_cast(OriginalMD)) return GlobalDecl(DD, GD.getDtorType()); return OriginalMD; } const CXXMethodDecl * ItaniumVTableContext::findOriginalMethodInMap(const CXXMethodDecl *MD) const { // Traverse the chain of virtual methods until we find the method that added // the v-table slot. while (true) { auto I = OriginalMethodMap.find(MD); // MD doesn't exist in OriginalMethodMap, so it must be the method we are // looking for. if (I == OriginalMethodMap.end()) break; // Set MD to the overridden method. MD = I->second; } return MD; } static std::unique_ptr CreateVTableLayout(const ItaniumVTableBuilder &Builder) { SmallVector VTableThunks(Builder.vtable_thunks_begin(), Builder.vtable_thunks_end()); return std::make_unique( Builder.VTableIndices, Builder.vtable_components(), VTableThunks, Builder.getAddressPoints()); } void ItaniumVTableContext::computeVTableRelatedInformation(const CXXRecordDecl *RD) { std::unique_ptr &Entry = VTableLayouts[RD]; // Check if we've computed this information before. if (Entry) return; ItaniumVTableBuilder Builder(*this, RD, CharUnits::Zero(), /*MostDerivedClassIsVirtual=*/false, RD); Entry = CreateVTableLayout(Builder); MethodVTableIndices.insert(Builder.vtable_indices_begin(), Builder.vtable_indices_end()); // Add the known thunks. Thunks.insert(Builder.thunks_begin(), Builder.thunks_end()); // If we don't have the vbase information for this class, insert it. // getVirtualBaseOffsetOffset will compute it separately without computing // the rest of the vtable related information. if (!RD->getNumVBases()) return; const CXXRecordDecl *VBase = RD->vbases_begin()->getType()->getAsCXXRecordDecl(); if (VirtualBaseClassOffsetOffsets.count(std::make_pair(RD, VBase))) return; for (const auto &I : Builder.getVBaseOffsetOffsets()) { // Insert all types. ClassPairTy ClassPair(RD, I.first); VirtualBaseClassOffsetOffsets.insert(std::make_pair(ClassPair, I.second)); } } std::unique_ptr ItaniumVTableContext::createConstructionVTableLayout( const CXXRecordDecl *MostDerivedClass, CharUnits MostDerivedClassOffset, bool MostDerivedClassIsVirtual, const CXXRecordDecl *LayoutClass) { ItaniumVTableBuilder Builder(*this, MostDerivedClass, MostDerivedClassOffset, MostDerivedClassIsVirtual, LayoutClass); return CreateVTableLayout(Builder); } namespace { // Vtables in the Microsoft ABI are different from the Itanium ABI. // // The main differences are: // 1. Separate vftable and vbtable. // // 2. Each subobject with a vfptr gets its own vftable rather than an address // point in a single vtable shared between all the subobjects. // Each vftable is represented by a separate section and virtual calls // must be done using the vftable which has a slot for the function to be // called. // // 3. Virtual method definitions expect their 'this' parameter to point to the // first vfptr whose table provides a compatible overridden method. In many // cases, this permits the original vf-table entry to directly call // the method instead of passing through a thunk. // See example before VFTableBuilder::ComputeThisOffset below. // // A compatible overridden method is one which does not have a non-trivial // covariant-return adjustment. // // The first vfptr is the one with the lowest offset in the complete-object // layout of the defining class, and the method definition will subtract // that constant offset from the parameter value to get the real 'this' // value. Therefore, if the offset isn't really constant (e.g. if a virtual // function defined in a virtual base is overridden in a more derived // virtual base and these bases have a reverse order in the complete // object), the vf-table may require a this-adjustment thunk. // // 4. vftables do not contain new entries for overrides that merely require // this-adjustment. Together with #3, this keeps vf-tables smaller and // eliminates the need for this-adjustment thunks in many cases, at the cost // of often requiring redundant work to adjust the "this" pointer. // // 5. Instead of VTT and constructor vtables, vbtables and vtordisps are used. // Vtordisps are emitted into the class layout if a class has // a) a user-defined ctor/dtor // and // b) a method overriding a method in a virtual base. // // To get a better understanding of this code, // you might want to see examples in test/CodeGenCXX/microsoft-abi-vtables-*.cpp class VFTableBuilder { public: typedef llvm::DenseMap MethodVFTableLocationsTy; typedef llvm::iterator_range method_locations_range; private: /// VTables - Global vtable information. MicrosoftVTableContext &VTables; /// Context - The ASTContext which we will use for layout information. ASTContext &Context; /// MostDerivedClass - The most derived class for which we're building this /// vtable. const CXXRecordDecl *MostDerivedClass; const ASTRecordLayout &MostDerivedClassLayout; const VPtrInfo &WhichVFPtr; /// FinalOverriders - The final overriders of the most derived class. const FinalOverriders Overriders; /// Components - The components of the vftable being built. SmallVector Components; MethodVFTableLocationsTy MethodVFTableLocations; /// Does this class have an RTTI component? bool HasRTTIComponent = false; /// MethodInfo - Contains information about a method in a vtable. /// (Used for computing 'this' pointer adjustment thunks. struct MethodInfo { /// VBTableIndex - The nonzero index in the vbtable that /// this method's base has, or zero. const uint64_t VBTableIndex; /// VFTableIndex - The index in the vftable that this method has. const uint64_t VFTableIndex; /// Shadowed - Indicates if this vftable slot is shadowed by /// a slot for a covariant-return override. If so, it shouldn't be printed /// or used for vcalls in the most derived class. bool Shadowed; /// UsesExtraSlot - Indicates if this vftable slot was created because /// any of the overridden slots required a return adjusting thunk. bool UsesExtraSlot; MethodInfo(uint64_t VBTableIndex, uint64_t VFTableIndex, bool UsesExtraSlot = false) : VBTableIndex(VBTableIndex), VFTableIndex(VFTableIndex), Shadowed(false), UsesExtraSlot(UsesExtraSlot) {} MethodInfo() : VBTableIndex(0), VFTableIndex(0), Shadowed(false), UsesExtraSlot(false) {} }; typedef llvm::DenseMap MethodInfoMapTy; /// MethodInfoMap - The information for all methods in the vftable we're /// currently building. MethodInfoMapTy MethodInfoMap; typedef llvm::DenseMap VTableThunksMapTy; /// VTableThunks - The thunks by vftable index in the vftable currently being /// built. VTableThunksMapTy VTableThunks; typedef SmallVector ThunkInfoVectorTy; typedef llvm::DenseMap ThunksMapTy; /// Thunks - A map that contains all the thunks needed for all methods in the /// most derived class for which the vftable is currently being built. ThunksMapTy Thunks; /// AddThunk - Add a thunk for the given method. void AddThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk) { SmallVector &ThunksVector = Thunks[MD]; // Check if we have this thunk already. if (llvm::is_contained(ThunksVector, Thunk)) return; ThunksVector.push_back(Thunk); } /// ComputeThisOffset - Returns the 'this' argument offset for the given /// method, relative to the beginning of the MostDerivedClass. CharUnits ComputeThisOffset(FinalOverriders::OverriderInfo Overrider); void CalculateVtordispAdjustment(FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset, ThisAdjustment &TA); /// AddMethod - Add a single virtual member function to the vftable /// components vector. void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) { if (!TI.isEmpty()) { VTableThunks[Components.size()] = TI; AddThunk(MD, TI); } if (const CXXDestructorDecl *DD = dyn_cast(MD)) { assert(TI.Return.isEmpty() && "Destructor can't have return adjustment!"); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { Components.push_back(VTableComponent::MakeFunction(MD)); } } /// AddMethods - Add the methods of this base subobject and the relevant /// subbases to the vftable we're currently laying out. void AddMethods(BaseSubobject Base, unsigned BaseDepth, const CXXRecordDecl *LastVBase, BasesSetVectorTy &VisitedBases); void LayoutVFTable() { // RTTI data goes before all other entries. if (HasRTTIComponent) Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass)); BasesSetVectorTy VisitedBases; AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, nullptr, VisitedBases); // Note that it is possible for the vftable to contain only an RTTI // pointer, if all virtual functions are constewval. assert(!Components.empty() && "vftable can't be empty"); assert(MethodVFTableLocations.empty()); for (const auto &I : MethodInfoMap) { const CXXMethodDecl *MD = I.first; const MethodInfo &MI = I.second; assert(MD == MD->getCanonicalDecl()); // Skip the methods that the MostDerivedClass didn't override // and the entries shadowed by return adjusting thunks. if (MD->getParent() != MostDerivedClass || MI.Shadowed) continue; MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.getVBaseWithVPtr(), WhichVFPtr.NonVirtualOffset, MI.VFTableIndex); if (const CXXDestructorDecl *DD = dyn_cast(MD)) { MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc; } else { MethodVFTableLocations[MD] = Loc; } } } public: VFTableBuilder(MicrosoftVTableContext &VTables, const CXXRecordDecl *MostDerivedClass, const VPtrInfo &Which) : VTables(VTables), Context(MostDerivedClass->getASTContext()), MostDerivedClass(MostDerivedClass), MostDerivedClassLayout(Context.getASTRecordLayout(MostDerivedClass)), WhichVFPtr(Which), Overriders(MostDerivedClass, CharUnits(), MostDerivedClass) { // Provide the RTTI component if RTTIData is enabled. If the vftable would // be available externally, we should not provide the RTTI componenent. It // is currently impossible to get available externally vftables with either // dllimport or extern template instantiations, but eventually we may add a // flag to support additional devirtualization that needs this. if (Context.getLangOpts().RTTIData) HasRTTIComponent = true; LayoutVFTable(); if (Context.getLangOpts().DumpVTableLayouts) dumpLayout(llvm::outs()); } uint64_t getNumThunks() const { return Thunks.size(); } ThunksMapTy::const_iterator thunks_begin() const { return Thunks.begin(); } ThunksMapTy::const_iterator thunks_end() const { return Thunks.end(); } method_locations_range vtable_locations() const { return method_locations_range(MethodVFTableLocations.begin(), MethodVFTableLocations.end()); } ArrayRef vtable_components() const { return Components; } VTableThunksMapTy::const_iterator vtable_thunks_begin() const { return VTableThunks.begin(); } VTableThunksMapTy::const_iterator vtable_thunks_end() const { return VTableThunks.end(); } void dumpLayout(raw_ostream &); }; } // end namespace // Let's study one class hierarchy as an example: // struct A { // virtual void f(); // int x; // }; // // struct B : virtual A { // virtual void f(); // }; // // Record layouts: // struct A: // 0 | (A vftable pointer) // 4 | int x // // struct B: // 0 | (B vbtable pointer) // 4 | struct A (virtual base) // 4 | (A vftable pointer) // 8 | int x // // Let's assume we have a pointer to the A part of an object of dynamic type B: // B b; // A *a = (A*)&b; // a->f(); // // In this hierarchy, f() belongs to the vftable of A, so B::f() expects // "this" parameter to point at the A subobject, which is B+4. // In the B::f() prologue, it adjusts "this" back to B by subtracting 4, // performed as a *static* adjustment. // // Interesting thing happens when we alter the relative placement of A and B // subobjects in a class: // struct C : virtual B { }; // // C c; // A *a = (A*)&c; // a->f(); // // Respective record layout is: // 0 | (C vbtable pointer) // 4 | struct A (virtual base) // 4 | (A vftable pointer) // 8 | int x // 12 | struct B (virtual base) // 12 | (B vbtable pointer) // // The final overrider of f() in class C is still B::f(), so B+4 should be // passed as "this" to that code. However, "a" points at B-8, so the respective // vftable entry should hold a thunk that adds 12 to the "this" argument before // performing a tail call to B::f(). // // With this example in mind, we can now calculate the 'this' argument offset // for the given method, relative to the beginning of the MostDerivedClass. CharUnits VFTableBuilder::ComputeThisOffset(FinalOverriders::OverriderInfo Overrider) { BasesSetVectorTy Bases; { // Find the set of least derived bases that define the given method. OverriddenMethodsSetTy VisitedOverriddenMethods; auto InitialOverriddenDefinitionCollector = [&]( const CXXMethodDecl *OverriddenMD) { if (OverriddenMD->size_overridden_methods() == 0) Bases.insert(OverriddenMD->getParent()); // Don't recurse on this method if we've already collected it. return VisitedOverriddenMethods.insert(OverriddenMD).second; }; visitAllOverriddenMethods(Overrider.Method, InitialOverriddenDefinitionCollector); } // If there are no overrides then 'this' is located // in the base that defines the method. if (Bases.size() == 0) return Overrider.Offset; CXXBasePaths Paths; Overrider.Method->getParent()->lookupInBases( [&Bases](const CXXBaseSpecifier *Specifier, CXXBasePath &) { return Bases.count(Specifier->getType()->getAsCXXRecordDecl()); }, Paths); // This will hold the smallest this offset among overridees of MD. // This implies that an offset of a non-virtual base will dominate an offset // of a virtual base to potentially reduce the number of thunks required // in the derived classes that inherit this method. CharUnits Ret; bool First = true; const ASTRecordLayout &OverriderRDLayout = Context.getASTRecordLayout(Overrider.Method->getParent()); for (const CXXBasePath &Path : Paths) { CharUnits ThisOffset = Overrider.Offset; CharUnits LastVBaseOffset; // For each path from the overrider to the parents of the overridden // methods, traverse the path, calculating the this offset in the most // derived class. for (const CXXBasePathElement &Element : Path) { QualType CurTy = Element.Base->getType(); const CXXRecordDecl *PrevRD = Element.Class, *CurRD = CurTy->getAsCXXRecordDecl(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD); if (Element.Base->isVirtual()) { // The interesting things begin when you have virtual inheritance. // The final overrider will use a static adjustment equal to the offset // of the vbase in the final overrider class. // For example, if the final overrider is in a vbase B of the most // derived class and it overrides a method of the B's own vbase A, // it uses A* as "this". In its prologue, it can cast A* to B* with // a static offset. This offset is used regardless of the actual // offset of A from B in the most derived class, requiring an // this-adjusting thunk in the vftable if A and B are laid out // differently in the most derived class. LastVBaseOffset = ThisOffset = Overrider.Offset + OverriderRDLayout.getVBaseClassOffset(CurRD); } else { ThisOffset += Layout.getBaseClassOffset(CurRD); } } if (isa(Overrider.Method)) { if (LastVBaseOffset.isZero()) { // If a "Base" class has at least one non-virtual base with a virtual // destructor, the "Base" virtual destructor will take the address // of the "Base" subobject as the "this" argument. ThisOffset = Overrider.Offset; } else { // A virtual destructor of a virtual base takes the address of the // virtual base subobject as the "this" argument. ThisOffset = LastVBaseOffset; } } if (Ret > ThisOffset || First) { First = false; Ret = ThisOffset; } } assert(!First && "Method not found in the given subobject?"); return Ret; } // Things are getting even more complex when the "this" adjustment has to // use a dynamic offset instead of a static one, or even two dynamic offsets. // This is sometimes required when a virtual call happens in the middle of // a non-most-derived class construction or destruction. // // Let's take a look at the following example: // struct A { // virtual void f(); // }; // // void foo(A *a) { a->f(); } // Knows nothing about siblings of A. // // struct B : virtual A { // virtual void f(); // B() { // foo(this); // } // }; // // struct C : virtual B { // virtual void f(); // }; // // Record layouts for these classes are: // struct A // 0 | (A vftable pointer) // // struct B // 0 | (B vbtable pointer) // 4 | (vtordisp for vbase A) // 8 | struct A (virtual base) // 8 | (A vftable pointer) // // struct C // 0 | (C vbtable pointer) // 4 | (vtordisp for vbase A) // 8 | struct A (virtual base) // A precedes B! // 8 | (A vftable pointer) // 12 | struct B (virtual base) // 12 | (B vbtable pointer) // // When one creates an object of type C, the C constructor: // - initializes all the vbptrs, then // - calls the A subobject constructor // (initializes A's vfptr with an address of A vftable), then // - calls the B subobject constructor // (initializes A's vfptr with an address of B vftable and vtordisp for A), // that in turn calls foo(), then // - initializes A's vfptr with an address of C vftable and zeroes out the // vtordisp // FIXME: if a structor knows it belongs to MDC, why doesn't it use a vftable // without vtordisp thunks? // FIXME: how are vtordisp handled in the presence of nooverride/final? // // When foo() is called, an object with a layout of class C has a vftable // referencing B::f() that assumes a B layout, so the "this" adjustments are // incorrect, unless an extra adjustment is done. This adjustment is called // "vtordisp adjustment". Vtordisp basically holds the difference between the // actual location of a vbase in the layout class and the location assumed by // the vftable of the class being constructed/destructed. Vtordisp is only // needed if "this" escapes a // structor (or we can't prove otherwise). // [i.e. vtordisp is a dynamic adjustment for a static adjustment, which is an // estimation of a dynamic adjustment] // // foo() gets a pointer to the A vbase and doesn't know anything about B or C, // so it just passes that pointer as "this" in a virtual call. // If there was no vtordisp, that would just dispatch to B::f(). // However, B::f() assumes B+8 is passed as "this", // yet the pointer foo() passes along is B-4 (i.e. C+8). // An extra adjustment is needed, so we emit a thunk into the B vftable. // This vtordisp thunk subtracts the value of vtordisp // from the "this" argument (-12) before making a tailcall to B::f(). // // Let's consider an even more complex example: // struct D : virtual B, virtual C { // D() { // foo(this); // } // }; // // struct D // 0 | (D vbtable pointer) // 4 | (vtordisp for vbase A) // 8 | struct A (virtual base) // A precedes both B and C! // 8 | (A vftable pointer) // 12 | struct B (virtual base) // B precedes C! // 12 | (B vbtable pointer) // 16 | struct C (virtual base) // 16 | (C vbtable pointer) // // When D::D() calls foo(), we find ourselves in a thunk that should tailcall // to C::f(), which assumes C+8 as its "this" parameter. This time, foo() // passes along A, which is C-8. The A vtordisp holds // "D.vbptr[index_of_A] - offset_of_A_in_D" // and we statically know offset_of_A_in_D, so can get a pointer to D. // When we know it, we can make an extra vbtable lookup to locate the C vbase // and one extra static adjustment to calculate the expected value of C+8. void VFTableBuilder::CalculateVtordispAdjustment( FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset, ThisAdjustment &TA) { const ASTRecordLayout::VBaseOffsetsMapTy &VBaseMap = MostDerivedClassLayout.getVBaseOffsetsMap(); const ASTRecordLayout::VBaseOffsetsMapTy::const_iterator &VBaseMapEntry = VBaseMap.find(WhichVFPtr.getVBaseWithVPtr()); assert(VBaseMapEntry != VBaseMap.end()); // If there's no vtordisp or the final overrider is defined in the same vbase // as the initial declaration, we don't need any vtordisp adjustment. if (!VBaseMapEntry->second.hasVtorDisp() || Overrider.VirtualBase == WhichVFPtr.getVBaseWithVPtr()) return; // OK, now we know we need to use a vtordisp thunk. // The implicit vtordisp field is located right before the vbase. CharUnits OffsetOfVBaseWithVFPtr = VBaseMapEntry->second.VBaseOffset; TA.Virtual.Microsoft.VtordispOffset = (OffsetOfVBaseWithVFPtr - WhichVFPtr.FullOffsetInMDC).getQuantity() - 4; // A simple vtordisp thunk will suffice if the final overrider is defined // in either the most derived class or its non-virtual base. if (Overrider.Method->getParent() == MostDerivedClass || !Overrider.VirtualBase) return; // Otherwise, we need to do use the dynamic offset of the final overrider // in order to get "this" adjustment right. TA.Virtual.Microsoft.VBPtrOffset = (OffsetOfVBaseWithVFPtr + WhichVFPtr.NonVirtualOffset - MostDerivedClassLayout.getVBPtrOffset()).getQuantity(); TA.Virtual.Microsoft.VBOffsetOffset = Context.getTypeSizeInChars(Context.IntTy).getQuantity() * VTables.getVBTableIndex(MostDerivedClass, Overrider.VirtualBase); TA.NonVirtual = (ThisOffset - Overrider.Offset).getQuantity(); } static void GroupNewVirtualOverloads( const CXXRecordDecl *RD, SmallVector &VirtualMethods) { // Put the virtual methods into VirtualMethods in the proper order: // 1) Group overloads by declaration name. New groups are added to the // vftable in the order of their first declarations in this class // (including overrides, non-virtual methods and any other named decl that // might be nested within the class). // 2) In each group, new overloads appear in the reverse order of declaration. typedef SmallVector MethodGroup; SmallVector Groups; typedef llvm::DenseMap VisitedGroupIndicesTy; VisitedGroupIndicesTy VisitedGroupIndices; for (const auto *D : RD->decls()) { const auto *ND = dyn_cast(D); if (!ND) continue; VisitedGroupIndicesTy::iterator J; bool Inserted; std::tie(J, Inserted) = VisitedGroupIndices.insert( std::make_pair(ND->getDeclName(), Groups.size())); if (Inserted) Groups.push_back(MethodGroup()); if (const auto *MD = dyn_cast(ND)) if (MicrosoftVTableContext::hasVtableSlot(MD)) Groups[J->second].push_back(MD->getCanonicalDecl()); } for (const MethodGroup &Group : Groups) VirtualMethods.append(Group.rbegin(), Group.rend()); } static bool isDirectVBase(const CXXRecordDecl *Base, const CXXRecordDecl *RD) { for (const auto &B : RD->bases()) { if (B.isVirtual() && B.getType()->getAsCXXRecordDecl() == Base) return true; } return false; } void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, const CXXRecordDecl *LastVBase, BasesSetVectorTy &VisitedBases) { const CXXRecordDecl *RD = Base.getBase(); if (!RD->isPolymorphic()) return; const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); // See if this class expands a vftable of the base we look at, which is either // the one defined by the vfptr base path or the primary base of the current // class. const CXXRecordDecl *NextBase = nullptr, *NextLastVBase = LastVBase; CharUnits NextBaseOffset; if (BaseDepth < WhichVFPtr.PathToIntroducingObject.size()) { NextBase = WhichVFPtr.PathToIntroducingObject[BaseDepth]; if (isDirectVBase(NextBase, RD)) { NextLastVBase = NextBase; NextBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(NextBase); } else { NextBaseOffset = Base.getBaseOffset() + Layout.getBaseClassOffset(NextBase); } } else if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { assert(!Layout.isPrimaryBaseVirtual() && "No primary virtual bases in this ABI"); NextBase = PrimaryBase; NextBaseOffset = Base.getBaseOffset(); } if (NextBase) { AddMethods(BaseSubobject(NextBase, NextBaseOffset), BaseDepth + 1, NextLastVBase, VisitedBases); if (!VisitedBases.insert(NextBase)) llvm_unreachable("Found a duplicate primary base!"); } SmallVector VirtualMethods; // Put virtual methods in the proper order. GroupNewVirtualOverloads(RD, VirtualMethods); // Now go through all virtual member functions and add them to the current // vftable. This is done by // - replacing overridden methods in their existing slots, as long as they // don't require return adjustment; calculating This adjustment if needed. // - adding new slots for methods of the current base not present in any // sub-bases; // - adding new slots for methods that require Return adjustment. // We keep track of the methods visited in the sub-bases in MethodInfoMap. for (const CXXMethodDecl *MD : VirtualMethods) { FinalOverriders::OverriderInfo FinalOverrider = Overriders.getOverrider(MD, Base.getBaseOffset()); const CXXMethodDecl *FinalOverriderMD = FinalOverrider.Method; const CXXMethodDecl *OverriddenMD = FindNearestOverriddenMethod(MD, VisitedBases); ThisAdjustment ThisAdjustmentOffset; bool ReturnAdjustingThunk = false, ForceReturnAdjustmentMangling = false; CharUnits ThisOffset = ComputeThisOffset(FinalOverrider); ThisAdjustmentOffset.NonVirtual = (ThisOffset - WhichVFPtr.FullOffsetInMDC).getQuantity(); if ((OverriddenMD || FinalOverriderMD != MD) && WhichVFPtr.getVBaseWithVPtr()) CalculateVtordispAdjustment(FinalOverrider, ThisOffset, ThisAdjustmentOffset); unsigned VBIndex = LastVBase ? VTables.getVBTableIndex(MostDerivedClass, LastVBase) : 0; if (OverriddenMD) { // If MD overrides anything in this vftable, we need to update the // entries. MethodInfoMapTy::iterator OverriddenMDIterator = MethodInfoMap.find(OverriddenMD); // If the overridden method went to a different vftable, skip it. if (OverriddenMDIterator == MethodInfoMap.end()) continue; MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second; VBIndex = OverriddenMethodInfo.VBTableIndex; // Let's check if the overrider requires any return adjustments. // We must create a new slot if the MD's return type is not trivially // convertible to the OverriddenMD's one. // Once a chain of method overrides adds a return adjusting vftable slot, // all subsequent overrides will also use an extra method slot. ReturnAdjustingThunk = !ComputeReturnAdjustmentBaseOffset( Context, MD, OverriddenMD).isEmpty() || OverriddenMethodInfo.UsesExtraSlot; if (!ReturnAdjustingThunk) { // No return adjustment needed - just replace the overridden method info // with the current info. MethodInfo MI(VBIndex, OverriddenMethodInfo.VFTableIndex); MethodInfoMap.erase(OverriddenMDIterator); assert(!MethodInfoMap.count(MD) && "Should not have method info for this method yet!"); MethodInfoMap.insert(std::make_pair(MD, MI)); continue; } // In case we need a return adjustment, we'll add a new slot for // the overrider. Mark the overridden method as shadowed by the new slot. OverriddenMethodInfo.Shadowed = true; // Force a special name mangling for a return-adjusting thunk // unless the method is the final overrider without this adjustment. ForceReturnAdjustmentMangling = !(MD == FinalOverriderMD && ThisAdjustmentOffset.isEmpty()); } else if (Base.getBaseOffset() != WhichVFPtr.FullOffsetInMDC || MD->size_overridden_methods()) { // Skip methods that don't belong to the vftable of the current class, // e.g. each method that wasn't seen in any of the visited sub-bases // but overrides multiple methods of other sub-bases. continue; } // If we got here, MD is a method not seen in any of the sub-bases or // it requires return adjustment. Insert the method info for this method. MethodInfo MI(VBIndex, HasRTTIComponent ? Components.size() - 1 : Components.size(), ReturnAdjustingThunk); assert(!MethodInfoMap.count(MD) && "Should not have method info for this method yet!"); MethodInfoMap.insert(std::make_pair(MD, MI)); // Check if this overrider needs a return adjustment. // We don't want to do this for pure virtual member functions. BaseOffset ReturnAdjustmentOffset; ReturnAdjustment ReturnAdjustment; if (!FinalOverriderMD->isPureVirtual()) { ReturnAdjustmentOffset = ComputeReturnAdjustmentBaseOffset(Context, FinalOverriderMD, MD); } if (!ReturnAdjustmentOffset.isEmpty()) { ForceReturnAdjustmentMangling = true; ReturnAdjustment.NonVirtual = ReturnAdjustmentOffset.NonVirtualOffset.getQuantity(); if (ReturnAdjustmentOffset.VirtualBase) { const ASTRecordLayout &DerivedLayout = Context.getASTRecordLayout(ReturnAdjustmentOffset.DerivedClass); ReturnAdjustment.Virtual.Microsoft.VBPtrOffset = DerivedLayout.getVBPtrOffset().getQuantity(); ReturnAdjustment.Virtual.Microsoft.VBIndex = VTables.getVBTableIndex(ReturnAdjustmentOffset.DerivedClass, ReturnAdjustmentOffset.VirtualBase); } } auto ThisType = (OverriddenMD ? OverriddenMD : MD)->getThisType().getTypePtr(); AddMethod(FinalOverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, ThisType, ForceReturnAdjustmentMangling ? MD : nullptr)); } } static void PrintBasePath(const VPtrInfo::BasePath &Path, raw_ostream &Out) { for (const CXXRecordDecl *Elem : llvm::reverse(Path)) { Out << "'"; Elem->printQualifiedName(Out); Out << "' in "; } } static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out, bool ContinueFirstLine) { const ReturnAdjustment &R = TI.Return; bool Multiline = false; const char *LinePrefix = "\n "; if (!R.isEmpty() || TI.Method) { if (!ContinueFirstLine) Out << LinePrefix; Out << "[return adjustment (to type '" << TI.Method->getReturnType().getCanonicalType() << "'): "; if (R.Virtual.Microsoft.VBPtrOffset) Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", "; if (R.Virtual.Microsoft.VBIndex) Out << "vbase #" << R.Virtual.Microsoft.VBIndex << ", "; Out << R.NonVirtual << " non-virtual]"; Multiline = true; } const ThisAdjustment &T = TI.This; if (!T.isEmpty()) { if (Multiline || !ContinueFirstLine) Out << LinePrefix; Out << "[this adjustment: "; if (!TI.This.Virtual.isEmpty()) { assert(T.Virtual.Microsoft.VtordispOffset < 0); Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", "; if (T.Virtual.Microsoft.VBPtrOffset) { Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset << " to the left,"; assert(T.Virtual.Microsoft.VBOffsetOffset > 0); Out << LinePrefix << " vboffset at " << T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, "; } } Out << T.NonVirtual << " non-virtual]"; } } void VFTableBuilder::dumpLayout(raw_ostream &Out) { Out << "VFTable for "; PrintBasePath(WhichVFPtr.PathToIntroducingObject, Out); Out << "'"; MostDerivedClass->printQualifiedName(Out); Out << "' (" << Components.size() << (Components.size() == 1 ? " entry" : " entries") << ").\n"; for (unsigned I = 0, E = Components.size(); I != E; ++I) { Out << llvm::format("%4d | ", I); const VTableComponent &Component = Components[I]; // Dump the component. switch (Component.getKind()) { case VTableComponent::CK_RTTI: Component.getRTTIDecl()->printQualifiedName(Out); Out << " RTTI"; break; case VTableComponent::CK_FunctionPointer: { const CXXMethodDecl *MD = Component.getFunctionDecl(); // FIXME: Figure out how to print the real thunk type, since they can // differ in the return type. std::string Str = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); Out << Str; if (MD->isPureVirtual()) Out << " [pure]"; if (MD->isDeleted()) Out << " [deleted]"; ThunkInfo Thunk = VTableThunks.lookup(I); if (!Thunk.isEmpty()) dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); break; } case VTableComponent::CK_DeletingDtorPointer: { const CXXDestructorDecl *DD = Component.getDestructorDecl(); DD->printQualifiedName(Out); Out << "() [scalar deleting]"; if (DD->isPureVirtual()) Out << " [pure]"; ThunkInfo Thunk = VTableThunks.lookup(I); if (!Thunk.isEmpty()) { assert(Thunk.Return.isEmpty() && "No return adjustment needed for destructors!"); dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); } break; } default: DiagnosticsEngine &Diags = Context.getDiagnostics(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "Unexpected vftable component type %0 for component number %1"); Diags.Report(MostDerivedClass->getLocation(), DiagID) << I << Component.getKind(); } Out << '\n'; } Out << '\n'; if (!Thunks.empty()) { // We store the method names in a map to get a stable order. std::map MethodNamesAndDecls; for (const auto &I : Thunks) { const CXXMethodDecl *MD = I.first; std::string MethodName = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); MethodNamesAndDecls.insert(std::make_pair(MethodName, MD)); } for (const auto &MethodNameAndDecl : MethodNamesAndDecls) { const std::string &MethodName = MethodNameAndDecl.first; const CXXMethodDecl *MD = MethodNameAndDecl.second; ThunkInfoVectorTy ThunksVector = Thunks[MD]; llvm::stable_sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) { // Keep different thunks with the same adjustments in the order they // were put into the vector. return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return); }); Out << "Thunks for '" << MethodName << "' (" << ThunksVector.size(); Out << (ThunksVector.size() == 1 ? " entry" : " entries") << ").\n"; for (unsigned I = 0, E = ThunksVector.size(); I != E; ++I) { const ThunkInfo &Thunk = ThunksVector[I]; Out << llvm::format("%4d | ", I); dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/true); Out << '\n'; } Out << '\n'; } } Out.flush(); } static bool setsIntersect(const llvm::SmallPtrSet &A, ArrayRef B) { for (const CXXRecordDecl *Decl : B) { if (A.count(Decl)) return true; } return false; } static bool rebucketPaths(VPtrInfoVector &Paths); /// Produces MSVC-compatible vbtable data. The symbols produced by this /// algorithm match those produced by MSVC 2012 and newer, which is different /// from MSVC 2010. /// /// MSVC 2012 appears to minimize the vbtable names using the following /// algorithm. First, walk the class hierarchy in the usual order, depth first, /// left to right, to find all of the subobjects which contain a vbptr field. /// Visiting each class node yields a list of inheritance paths to vbptrs. Each /// record with a vbptr creates an initially empty path. /// /// To combine paths from child nodes, the paths are compared to check for /// ambiguity. Paths are "ambiguous" if multiple paths have the same set of /// components in the same order. Each group of ambiguous paths is extended by /// appending the class of the base from which it came. If the current class /// node produced an ambiguous path, its path is extended with the current class. /// After extending paths, MSVC again checks for ambiguity, and extends any /// ambiguous path which wasn't already extended. Because each node yields an /// unambiguous set of paths, MSVC doesn't need to extend any path more than once /// to produce an unambiguous set of paths. /// /// TODO: Presumably vftables use the same algorithm. void MicrosoftVTableContext::computeVTablePaths(bool ForVBTables, const CXXRecordDecl *RD, VPtrInfoVector &Paths) { assert(Paths.empty()); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); // Base case: this subobject has its own vptr. if (ForVBTables ? Layout.hasOwnVBPtr() : Layout.hasOwnVFPtr()) Paths.push_back(std::make_unique(RD)); // Recursive case: get all the vbtables from our bases and remove anything // that shares a virtual base. llvm::SmallPtrSet VBasesSeen; for (const auto &B : RD->bases()) { const CXXRecordDecl *Base = B.getType()->getAsCXXRecordDecl(); if (B.isVirtual() && VBasesSeen.count(Base)) continue; if (!Base->isDynamicClass()) continue; const VPtrInfoVector &BasePaths = ForVBTables ? enumerateVBTables(Base) : getVFPtrOffsets(Base); for (const std::unique_ptr &BaseInfo : BasePaths) { // Don't include the path if it goes through a virtual base that we've // already included. if (setsIntersect(VBasesSeen, BaseInfo->ContainingVBases)) continue; // Copy the path and adjust it as necessary. auto P = std::make_unique(*BaseInfo); // We mangle Base into the path if the path would've been ambiguous and it // wasn't already extended with Base. if (P->MangledPath.empty() || P->MangledPath.back() != Base) P->NextBaseToMangle = Base; // Keep track of which vtable the derived class is going to extend with // new methods or bases. We append to either the vftable of our primary // base, or the first non-virtual base that has a vbtable. if (P->ObjectWithVPtr == Base && Base == (ForVBTables ? Layout.getBaseSharingVBPtr() : Layout.getPrimaryBase())) P->ObjectWithVPtr = RD; // Keep track of the full adjustment from the MDC to this vtable. The // adjustment is captured by an optional vbase and a non-virtual offset. if (B.isVirtual()) P->ContainingVBases.push_back(Base); else if (P->ContainingVBases.empty()) P->NonVirtualOffset += Layout.getBaseClassOffset(Base); // Update the full offset in the MDC. P->FullOffsetInMDC = P->NonVirtualOffset; if (const CXXRecordDecl *VB = P->getVBaseWithVPtr()) P->FullOffsetInMDC += Layout.getVBaseClassOffset(VB); Paths.push_back(std::move(P)); } if (B.isVirtual()) VBasesSeen.insert(Base); // After visiting any direct base, we've transitively visited all of its // morally virtual bases. for (const auto &VB : Base->vbases()) VBasesSeen.insert(VB.getType()->getAsCXXRecordDecl()); } // Sort the paths into buckets, and if any of them are ambiguous, extend all // paths in ambiguous buckets. bool Changed = true; while (Changed) Changed = rebucketPaths(Paths); } static bool extendPath(VPtrInfo &P) { if (P.NextBaseToMangle) { P.MangledPath.push_back(P.NextBaseToMangle); P.NextBaseToMangle = nullptr;// Prevent the path from being extended twice. return true; } return false; } static bool rebucketPaths(VPtrInfoVector &Paths) { // What we're essentially doing here is bucketing together ambiguous paths. // Any bucket with more than one path in it gets extended by NextBase, which // is usually the direct base of the inherited the vbptr. This code uses a // sorted vector to implement a multiset to form the buckets. Note that the // ordering is based on pointers, but it doesn't change our output order. The // current algorithm is designed to match MSVC 2012's names. llvm::SmallVector, 2> PathsSorted( llvm::make_pointee_range(Paths)); llvm::sort(PathsSorted, [](const VPtrInfo &LHS, const VPtrInfo &RHS) { return LHS.MangledPath < RHS.MangledPath; }); bool Changed = false; for (size_t I = 0, E = PathsSorted.size(); I != E;) { // Scan forward to find the end of the bucket. size_t BucketStart = I; do { ++I; } while (I != E && PathsSorted[BucketStart].get().MangledPath == PathsSorted[I].get().MangledPath); // If this bucket has multiple paths, extend them all. if (I - BucketStart > 1) { for (size_t II = BucketStart; II != I; ++II) Changed |= extendPath(PathsSorted[II]); assert(Changed && "no paths were extended to fix ambiguity"); } } return Changed; } MicrosoftVTableContext::~MicrosoftVTableContext() {} namespace { typedef llvm::SetVector, llvm::DenseSet> FullPathTy; } // This recursive function finds all paths from a subobject centered at // (RD, Offset) to the subobject located at IntroducingObject. static void findPathsToSubobject(ASTContext &Context, const ASTRecordLayout &MostDerivedLayout, const CXXRecordDecl *RD, CharUnits Offset, BaseSubobject IntroducingObject, FullPathTy &FullPath, std::list &Paths) { if (BaseSubobject(RD, Offset) == IntroducingObject) { Paths.push_back(FullPath); return; } const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); for (const CXXBaseSpecifier &BS : RD->bases()) { const CXXRecordDecl *Base = BS.getType()->getAsCXXRecordDecl(); CharUnits NewOffset = BS.isVirtual() ? MostDerivedLayout.getVBaseClassOffset(Base) : Offset + Layout.getBaseClassOffset(Base); FullPath.insert(BaseSubobject(Base, NewOffset)); findPathsToSubobject(Context, MostDerivedLayout, Base, NewOffset, IntroducingObject, FullPath, Paths); FullPath.pop_back(); } } // Return the paths which are not subsets of other paths. static void removeRedundantPaths(std::list &FullPaths) { FullPaths.remove_if([&](const FullPathTy &SpecificPath) { for (const FullPathTy &OtherPath : FullPaths) { if (&SpecificPath == &OtherPath) continue; if (llvm::all_of(SpecificPath, [&](const BaseSubobject &BSO) { return OtherPath.contains(BSO); })) { return true; } } return false; }); } static CharUnits getOffsetOfFullPath(ASTContext &Context, const CXXRecordDecl *RD, const FullPathTy &FullPath) { const ASTRecordLayout &MostDerivedLayout = Context.getASTRecordLayout(RD); CharUnits Offset = CharUnits::fromQuantity(-1); for (const BaseSubobject &BSO : FullPath) { const CXXRecordDecl *Base = BSO.getBase(); // The first entry in the path is always the most derived record, skip it. if (Base == RD) { assert(Offset.getQuantity() == -1); Offset = CharUnits::Zero(); continue; } assert(Offset.getQuantity() != -1); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); // While we know which base has to be traversed, we don't know if that base // was a virtual base. const CXXBaseSpecifier *BaseBS = std::find_if( RD->bases_begin(), RD->bases_end(), [&](const CXXBaseSpecifier &BS) { return BS.getType()->getAsCXXRecordDecl() == Base; }); Offset = BaseBS->isVirtual() ? MostDerivedLayout.getVBaseClassOffset(Base) : Offset + Layout.getBaseClassOffset(Base); RD = Base; } return Offset; } // We want to select the path which introduces the most covariant overrides. If // two paths introduce overrides which the other path doesn't contain, issue a // diagnostic. static const FullPathTy *selectBestPath(ASTContext &Context, const CXXRecordDecl *RD, const VPtrInfo &Info, std::list &FullPaths) { // Handle some easy cases first. if (FullPaths.empty()) return nullptr; if (FullPaths.size() == 1) return &FullPaths.front(); const FullPathTy *BestPath = nullptr; typedef std::set OverriderSetTy; OverriderSetTy LastOverrides; for (const FullPathTy &SpecificPath : FullPaths) { assert(!SpecificPath.empty()); OverriderSetTy CurrentOverrides; const CXXRecordDecl *TopLevelRD = SpecificPath.begin()->getBase(); // Find the distance from the start of the path to the subobject with the // VPtr. CharUnits BaseOffset = getOffsetOfFullPath(Context, TopLevelRD, SpecificPath); FinalOverriders Overriders(TopLevelRD, CharUnits::Zero(), TopLevelRD); for (const CXXMethodDecl *MD : Info.IntroducingObject->methods()) { if (!MicrosoftVTableContext::hasVtableSlot(MD)) continue; FinalOverriders::OverriderInfo OI = Overriders.getOverrider(MD->getCanonicalDecl(), BaseOffset); const CXXMethodDecl *OverridingMethod = OI.Method; // Only overriders which have a return adjustment introduce problematic // thunks. if (ComputeReturnAdjustmentBaseOffset(Context, OverridingMethod, MD) .isEmpty()) continue; // It's possible that the overrider isn't in this path. If so, skip it // because this path didn't introduce it. const CXXRecordDecl *OverridingParent = OverridingMethod->getParent(); if (llvm::none_of(SpecificPath, [&](const BaseSubobject &BSO) { return BSO.getBase() == OverridingParent; })) continue; CurrentOverrides.insert(OverridingMethod); } OverriderSetTy NewOverrides = llvm::set_difference(CurrentOverrides, LastOverrides); if (NewOverrides.empty()) continue; OverriderSetTy MissingOverrides = llvm::set_difference(LastOverrides, CurrentOverrides); if (MissingOverrides.empty()) { // This path is a strict improvement over the last path, let's use it. BestPath = &SpecificPath; std::swap(CurrentOverrides, LastOverrides); } else { // This path introduces an overrider with a conflicting covariant thunk. DiagnosticsEngine &Diags = Context.getDiagnostics(); const CXXMethodDecl *CovariantMD = *NewOverrides.begin(); const CXXMethodDecl *ConflictMD = *MissingOverrides.begin(); Diags.Report(RD->getLocation(), diag::err_vftable_ambiguous_component) << RD; Diags.Report(CovariantMD->getLocation(), diag::note_covariant_thunk) << CovariantMD; Diags.Report(ConflictMD->getLocation(), diag::note_covariant_thunk) << ConflictMD; } } // Go with the path that introduced the most covariant overrides. If there is // no such path, pick the first path. return BestPath ? BestPath : &FullPaths.front(); } static void computeFullPathsForVFTables(ASTContext &Context, const CXXRecordDecl *RD, VPtrInfoVector &Paths) { const ASTRecordLayout &MostDerivedLayout = Context.getASTRecordLayout(RD); FullPathTy FullPath; std::list FullPaths; for (const std::unique_ptr& Info : Paths) { findPathsToSubobject( Context, MostDerivedLayout, RD, CharUnits::Zero(), BaseSubobject(Info->IntroducingObject, Info->FullOffsetInMDC), FullPath, FullPaths); FullPath.clear(); removeRedundantPaths(FullPaths); Info->PathToIntroducingObject.clear(); if (const FullPathTy *BestPath = selectBestPath(Context, RD, *Info, FullPaths)) for (const BaseSubobject &BSO : *BestPath) Info->PathToIntroducingObject.push_back(BSO.getBase()); FullPaths.clear(); } } static bool vfptrIsEarlierInMDC(const ASTRecordLayout &Layout, const MethodVFTableLocation &LHS, const MethodVFTableLocation &RHS) { CharUnits L = LHS.VFPtrOffset; CharUnits R = RHS.VFPtrOffset; if (LHS.VBase) L += Layout.getVBaseClassOffset(LHS.VBase); if (RHS.VBase) R += Layout.getVBaseClassOffset(RHS.VBase); return L < R; } void MicrosoftVTableContext::computeVTableRelatedInformation( const CXXRecordDecl *RD) { assert(RD->isDynamicClass()); // Check if we've computed this information before. if (VFPtrLocations.count(RD)) return; const VTableLayout::AddressPointsMapTy EmptyAddressPointsMap; { auto VFPtrs = std::make_unique(); computeVTablePaths(/*ForVBTables=*/false, RD, *VFPtrs); computeFullPathsForVFTables(Context, RD, *VFPtrs); VFPtrLocations[RD] = std::move(VFPtrs); } MethodVFTableLocationsTy NewMethodLocations; for (const std::unique_ptr &VFPtr : *VFPtrLocations[RD]) { VFTableBuilder Builder(*this, RD, *VFPtr); VFTableIdTy id(RD, VFPtr->FullOffsetInMDC); assert(VFTableLayouts.count(id) == 0); SmallVector VTableThunks( Builder.vtable_thunks_begin(), Builder.vtable_thunks_end()); VFTableLayouts[id] = std::make_unique( ArrayRef{0}, Builder.vtable_components(), VTableThunks, EmptyAddressPointsMap); Thunks.insert(Builder.thunks_begin(), Builder.thunks_end()); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); for (const auto &Loc : Builder.vtable_locations()) { auto Insert = NewMethodLocations.insert(Loc); if (!Insert.second) { const MethodVFTableLocation &NewLoc = Loc.second; MethodVFTableLocation &OldLoc = Insert.first->second; if (vfptrIsEarlierInMDC(Layout, NewLoc, OldLoc)) OldLoc = NewLoc; } } } MethodVFTableLocations.insert(NewMethodLocations.begin(), NewMethodLocations.end()); if (Context.getLangOpts().DumpVTableLayouts) dumpMethodLocations(RD, NewMethodLocations, llvm::outs()); } void MicrosoftVTableContext::dumpMethodLocations( const CXXRecordDecl *RD, const MethodVFTableLocationsTy &NewMethods, raw_ostream &Out) { // Compute the vtable indices for all the member functions. // Store them in a map keyed by the location so we'll get a sorted table. std::map IndicesMap; bool HasNonzeroOffset = false; for (const auto &I : NewMethods) { const CXXMethodDecl *MD = cast(I.first.getDecl()); assert(hasVtableSlot(MD)); std::string MethodName = PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunctionNoVirtual, MD); if (isa(MD)) { IndicesMap[I.second] = MethodName + " [scalar deleting]"; } else { IndicesMap[I.second] = MethodName; } if (!I.second.VFPtrOffset.isZero() || I.second.VBTableIndex != 0) HasNonzeroOffset = true; } // Print the vtable indices for all the member functions. if (!IndicesMap.empty()) { Out << "VFTable indices for "; Out << "'"; RD->printQualifiedName(Out); Out << "' (" << IndicesMap.size() << (IndicesMap.size() == 1 ? " entry" : " entries") << ").\n"; CharUnits LastVFPtrOffset = CharUnits::fromQuantity(-1); uint64_t LastVBIndex = 0; for (const auto &I : IndicesMap) { CharUnits VFPtrOffset = I.first.VFPtrOffset; uint64_t VBIndex = I.first.VBTableIndex; if (HasNonzeroOffset && (VFPtrOffset != LastVFPtrOffset || VBIndex != LastVBIndex)) { assert(VBIndex > LastVBIndex || VFPtrOffset > LastVFPtrOffset); Out << " -- accessible via "; if (VBIndex) Out << "vbtable index " << VBIndex << ", "; Out << "vfptr at offset " << VFPtrOffset.getQuantity() << " --\n"; LastVFPtrOffset = VFPtrOffset; LastVBIndex = VBIndex; } uint64_t VTableIndex = I.first.Index; const std::string &MethodName = I.second; Out << llvm::format("%4" PRIu64 " | ", VTableIndex) << MethodName << '\n'; } Out << '\n'; } Out.flush(); } const VirtualBaseInfo &MicrosoftVTableContext::computeVBTableRelatedInformation( const CXXRecordDecl *RD) { VirtualBaseInfo *VBI; { // Get or create a VBI for RD. Don't hold a reference to the DenseMap cell, // as it may be modified and rehashed under us. std::unique_ptr &Entry = VBaseInfo[RD]; if (Entry) return *Entry; Entry = std::make_unique(); VBI = Entry.get(); } computeVTablePaths(/*ForVBTables=*/true, RD, VBI->VBPtrPaths); // First, see if the Derived class shared the vbptr with a non-virtual base. const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); if (const CXXRecordDecl *VBPtrBase = Layout.getBaseSharingVBPtr()) { // If the Derived class shares the vbptr with a non-virtual base, the shared // virtual bases come first so that the layout is the same. const VirtualBaseInfo &BaseInfo = computeVBTableRelatedInformation(VBPtrBase); VBI->VBTableIndices.insert(BaseInfo.VBTableIndices.begin(), BaseInfo.VBTableIndices.end()); } // New vbases are added to the end of the vbtable. // Skip the self entry and vbases visited in the non-virtual base, if any. unsigned VBTableIndex = 1 + VBI->VBTableIndices.size(); for (const auto &VB : RD->vbases()) { const CXXRecordDecl *CurVBase = VB.getType()->getAsCXXRecordDecl(); if (!VBI->VBTableIndices.count(CurVBase)) VBI->VBTableIndices[CurVBase] = VBTableIndex++; } return *VBI; } unsigned MicrosoftVTableContext::getVBTableIndex(const CXXRecordDecl *Derived, const CXXRecordDecl *VBase) { const VirtualBaseInfo &VBInfo = computeVBTableRelatedInformation(Derived); assert(VBInfo.VBTableIndices.count(VBase)); return VBInfo.VBTableIndices.find(VBase)->second; } const VPtrInfoVector & MicrosoftVTableContext::enumerateVBTables(const CXXRecordDecl *RD) { return computeVBTableRelatedInformation(RD).VBPtrPaths; } const VPtrInfoVector & MicrosoftVTableContext::getVFPtrOffsets(const CXXRecordDecl *RD) { computeVTableRelatedInformation(RD); assert(VFPtrLocations.count(RD) && "Couldn't find vfptr locations"); return *VFPtrLocations[RD]; } const VTableLayout & MicrosoftVTableContext::getVFTableLayout(const CXXRecordDecl *RD, CharUnits VFPtrOffset) { computeVTableRelatedInformation(RD); VFTableIdTy id(RD, VFPtrOffset); assert(VFTableLayouts.count(id) && "Couldn't find a VFTable at this offset"); return *VFTableLayouts[id]; } MethodVFTableLocation MicrosoftVTableContext::getMethodVFTableLocation(GlobalDecl GD) { assert(hasVtableSlot(cast(GD.getDecl())) && "Only use this method for virtual methods or dtors"); if (isa(GD.getDecl())) assert(GD.getDtorType() == Dtor_Deleting); GD = GD.getCanonicalDecl(); MethodVFTableLocationsTy::iterator I = MethodVFTableLocations.find(GD); if (I != MethodVFTableLocations.end()) return I->second; const CXXRecordDecl *RD = cast(GD.getDecl())->getParent(); computeVTableRelatedInformation(RD); I = MethodVFTableLocations.find(GD); assert(I != MethodVFTableLocations.end() && "Did not find index!"); return I->second; }