diff options
Diffstat (limited to 'clang/lib/AST/VTableBuilder.cpp')
-rw-r--r-- | clang/lib/AST/VTableBuilder.cpp | 154 |
1 files changed, 101 insertions, 53 deletions
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 2b5b74be5961c..f5865ce96b647 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -408,7 +408,7 @@ void FinalOverriders::dump(raw_ostream &Out, BaseSubobject Base, // Now dump the overriders for this base subobject. for (const auto *MD : RD->methods()) { - if (!MD->isVirtual()) + if (!VTableContextBase::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); @@ -486,8 +486,8 @@ static bool HasSameVirtualSignature(const CXXMethodDecl *LHS, bool VCallOffsetMap::MethodsCanShareVCallOffset(const CXXMethodDecl *LHS, const CXXMethodDecl *RHS) { - assert(LHS->isVirtual() && "LHS must be virtual!"); - assert(RHS->isVirtual() && "LHS must be virtual!"); + assert(VTableContextBase::hasVtableSlot(LHS) && "LHS must be virtual!"); + assert(VTableContextBase::hasVtableSlot(RHS) && "LHS must be virtual!"); // A destructor can share a vcall offset with another destructor. if (isa<CXXDestructorDecl>(LHS)) @@ -535,6 +535,8 @@ public: VBaseOffsetOffsetsMapTy; private: + const ItaniumVTableContext &VTables; + /// MostDerivedClass - The most derived class for which we're building vcall /// and vbase offsets. const CXXRecordDecl *MostDerivedClass; @@ -583,13 +585,15 @@ private: CharUnits getCurrentOffsetOffset() const; public: - VCallAndVBaseOffsetBuilder(const CXXRecordDecl *MostDerivedClass, + VCallAndVBaseOffsetBuilder(const ItaniumVTableContext &VTables, + const CXXRecordDecl *MostDerivedClass, const CXXRecordDecl *LayoutClass, const FinalOverriders *Overriders, BaseSubobject Base, bool BaseIsVirtual, CharUnits OffsetInLayoutClass) - : MostDerivedClass(MostDerivedClass), LayoutClass(LayoutClass), - Context(MostDerivedClass->getASTContext()), Overriders(Overriders) { + : VTables(VTables), MostDerivedClass(MostDerivedClass), + LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()), + Overriders(Overriders) { // Add vcall and vbase offsets. AddVCallAndVBaseOffsets(Base, BaseIsVirtual, OffsetInLayoutClass); @@ -662,9 +666,13 @@ CharUnits VCallAndVBaseOffsetBuilder::getCurrentOffsetOffset() const { // vcall offset itself). int64_t OffsetIndex = -(int64_t)(3 + Components.size()); - CharUnits PointerWidth = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - CharUnits OffsetOffset = PointerWidth * OffsetIndex; + // 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(0)); + CharUnits OffsetOffset = OffsetWidth * OffsetIndex; + return OffsetOffset; } @@ -689,7 +697,7 @@ void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base, // Add the vcall offsets. for (const auto *MD : RD->methods()) { - if (!MD->isVirtual()) + if (!VTableContextBase::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); @@ -1077,7 +1085,7 @@ typedef llvm::SmallPtrSet<const CXXMethodDecl *, 8> OverriddenMethodsSetTy; template <class VisitorTy> static void visitAllOverriddenMethods(const CXXMethodDecl *MD, VisitorTy &Visitor) { - assert(MD->isVirtual() && "Method is not virtual!"); + assert(VTableContextBase::hasVtableSlot(MD) && "Method is not virtual!"); for (const CXXMethodDecl *OverriddenMD : MD->overridden_methods()) { if (!Visitor(OverriddenMD)) @@ -1271,13 +1279,13 @@ ThisAdjustment ItaniumVTableBuilder::ComputeThisAdjustment( if (VCallOffsets.empty()) { // We don't have vcall offsets for this virtual base, go ahead and // build them. - VCallAndVBaseOffsetBuilder Builder(MostDerivedClass, MostDerivedClass, - /*Overriders=*/nullptr, - BaseSubobject(Offset.VirtualBase, - CharUnits::Zero()), - /*BaseIsVirtual=*/true, - /*OffsetInLayoutClass=*/ - CharUnits::Zero()); + VCallAndVBaseOffsetBuilder Builder( + VTables, MostDerivedClass, MostDerivedClass, + /*Overriders=*/nullptr, + BaseSubobject(Offset.VirtualBase, CharUnits::Zero()), + /*BaseIsVirtual=*/true, + /*OffsetInLayoutClass=*/ + CharUnits::Zero()); VCallOffsets = Builder.getVCallOffsets(); } @@ -1474,14 +1482,14 @@ void ItaniumVTableBuilder::AddMethods( llvm_unreachable("Found a duplicate primary base!"); } - const CXXDestructorDecl *ImplicitVirtualDtor = nullptr; - typedef llvm::SmallVector<const CXXMethodDecl *, 8> NewVirtualFunctionsTy; NewVirtualFunctionsTy NewVirtualFunctions; + llvm::SmallVector<const CXXMethodDecl*, 4> NewImplicitVirtualFunctions; + // Now go through all virtual member functions and add them. for (const auto *MD : RD->methods()) { - if (!MD->isVirtual()) + if (!ItaniumVTableContext::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); @@ -1542,24 +1550,30 @@ void ItaniumVTableBuilder::AddMethods( } } - if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) { - if (MD->isImplicit()) { - // Itanium C++ ABI 2.5.2: - // If a class has an implicitly-defined virtual destructor, - // its entries come after the declared virtual function pointers. - - assert(!ImplicitVirtualDtor && - "Did already see an implicit virtual dtor!"); - ImplicitVirtualDtor = DD; - continue; - } - } - - NewVirtualFunctions.push_back(MD); - } - - if (ImplicitVirtualDtor) - NewVirtualFunctions.push_back(ImplicitVirtualDtor); + 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->isCopyAssignmentOperator() != B->isCopyAssignmentOperator()) + return A->isCopyAssignmentOperator(); + if (A->isMoveAssignmentOperator() != B->isMoveAssignmentOperator()) + return A->isMoveAssignmentOperator(); + if (isa<CXXDestructorDecl>(A) != isa<CXXDestructorDecl>(B)) + return isa<CXXDestructorDecl>(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. @@ -1629,9 +1643,9 @@ void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables( VTableIndices.push_back(VTableIndex); // Add vcall and vbase offsets for this vtable. - VCallAndVBaseOffsetBuilder Builder(MostDerivedClass, LayoutClass, &Overriders, - Base, BaseIsVirtualInLayoutClass, - OffsetInLayoutClass); + 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. @@ -2155,7 +2169,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { for (const auto *MD : MostDerivedClass->methods()) { // We only want virtual member functions. - if (!MD->isVirtual()) + if (!ItaniumVTableContext::hasVtableSlot(MD)) continue; MD = MD->getCanonicalDecl(); @@ -2194,12 +2208,40 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { } } +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<size_t> VTableIndices, ArrayRef<VTableComponent> VTableComponents, ArrayRef<VTableThunkTy> VTableThunks, const AddressPointsMapTy &AddressPoints) : VTableComponents(VTableComponents), VTableThunks(VTableThunks), - AddressPoints(AddressPoints) { + AddressPoints(AddressPoints), AddressPointIndices(MakeAddressPointIndices( + AddressPoints, VTableIndices.size())) { if (VTableIndices.size() <= 1) assert(VTableIndices.size() == 1 && VTableIndices[0] == 0); else @@ -2215,8 +2257,13 @@ VTableLayout::VTableLayout(ArrayRef<size_t> VTableIndices, VTableLayout::~VTableLayout() { } -ItaniumVTableContext::ItaniumVTableContext(ASTContext &Context) - : VTableContextBase(/*MS=*/false) {} +bool VTableContextBase::hasVtableSlot(const CXXMethodDecl *MD) { + return MD->isVirtual() && !MD->isConsteval(); +} + +ItaniumVTableContext::ItaniumVTableContext( + ASTContext &Context, VTableComponentLayout ComponentLayout) + : VTableContextBase(/*MS=*/false), ComponentLayout(ComponentLayout) {} ItaniumVTableContext::~ItaniumVTableContext() {} @@ -2245,7 +2292,7 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, if (I != VirtualBaseClassOffsetOffsets.end()) return I->second; - VCallAndVBaseOffsetBuilder Builder(RD, RD, /*Overriders=*/nullptr, + VCallAndVBaseOffsetBuilder Builder(*this, RD, RD, /*Overriders=*/nullptr, BaseSubobject(RD, CharUnits::Zero()), /*BaseIsVirtual=*/false, /*OffsetInLayoutClass=*/CharUnits::Zero()); @@ -2494,8 +2541,9 @@ private: BasesSetVectorTy VisitedBases; AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, nullptr, VisitedBases); - assert((HasRTTIComponent ? Components.size() - 1 : Components.size()) && - "vftable can't be empty"); + // 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) { @@ -2874,7 +2922,7 @@ static void GroupNewVirtualOverloads( if (Inserted) Groups.push_back(MethodGroup()); if (const auto *MD = dyn_cast<CXXMethodDecl>(ND)) - if (MD->isVirtual()) + if (MicrosoftVTableContext::hasVtableSlot(MD)) Groups[J->second].push_back(MD->getCanonicalDecl()); } @@ -3470,7 +3518,7 @@ static const FullPathTy *selectBestPath(ASTContext &Context, getOffsetOfFullPath(Context, TopLevelRD, SpecificPath); FinalOverriders Overriders(TopLevelRD, CharUnits::Zero(), TopLevelRD); for (const CXXMethodDecl *MD : Info.IntroducingObject->methods()) { - if (!MD->isVirtual()) + if (!MicrosoftVTableContext::hasVtableSlot(MD)) continue; FinalOverriders::OverriderInfo OI = Overriders.getOverrider(MD->getCanonicalDecl(), BaseOffset); @@ -3609,7 +3657,7 @@ void MicrosoftVTableContext::dumpMethodLocations( for (const auto &I : NewMethods) { const CXXMethodDecl *MD = cast<const CXXMethodDecl>(I.first.getDecl()); - assert(MD->isVirtual()); + assert(hasVtableSlot(MD)); std::string MethodName = PredefinedExpr::ComputeName( PredefinedExpr::PrettyFunctionNoVirtual, MD); @@ -3729,7 +3777,7 @@ MicrosoftVTableContext::getVFTableLayout(const CXXRecordDecl *RD, MethodVFTableLocation MicrosoftVTableContext::getMethodVFTableLocation(GlobalDecl GD) { - assert(cast<CXXMethodDecl>(GD.getDecl())->isVirtual() && + assert(hasVtableSlot(cast<CXXMethodDecl>(GD.getDecl())) && "Only use this method for virtual methods or dtors"); if (isa<CXXDestructorDecl>(GD.getDecl())) assert(GD.getDtorType() == Dtor_Deleting); |