From 57071e29bdb65d21f72e221f2a16f24f6bea72f3 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Fri, 12 Feb 2010 05:25:12 +0000 Subject: [PATCH] More work on vtable layout. We can now layout vtables with primary bases. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95965 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGVtable.cpp | 190 ++++++++++++++++++++++-------- test/CodeGenCXX/vtable-layout.cpp | 61 +++++++++- 2 files changed, 196 insertions(+), 55 deletions(-) diff --git a/lib/CodeGen/CGVtable.cpp b/lib/CodeGen/CGVtable.cpp index 09d6067f46..1c5384767e 100644 --- a/lib/CodeGen/CGVtable.cpp +++ b/lib/CodeGen/CGVtable.cpp @@ -99,6 +99,17 @@ TypeConversionRequiresAdjustment(ASTContext &Ctx, return TypeConversionRequiresAdjustment(Ctx, DerivedDecl, BaseDecl); } +static bool +ReturnTypeConversionRequiresAdjustment(ASTContext &Context, + const CXXMethodDecl *DerivedMD, + const CXXMethodDecl *BaseMD) { + const FunctionType *BaseFT = BaseMD->getType()->getAs(); + const FunctionType *DerivedFT = DerivedMD->getType()->getAs(); + + return TypeConversionRequiresAdjustment(Context, DerivedFT->getResultType(), + BaseFT->getResultType()); +} + namespace { /// FinalOverriders - Contains the final overrider member functions for all @@ -248,6 +259,9 @@ void FinalOverriders::PropagateOverrider(const CXXMethodDecl *OldMD, OverriddenMD)]; assert(Overrider && "Did not find existing overrider!"); + assert(!ReturnTypeConversionRequiresAdjustment(Context, NewMD, + OverriddenMD) && + "FIXME: Covariant return types not handled yet!"); // Set the new overrider. Overrider = NewMD; @@ -470,12 +484,17 @@ private: /// VtableBuilder - Class for building vtable layout information. class VtableBuilder { +public: + /// PrimaryBasesSetTy - A set of direct and indirect primary bases. + typedef llvm::SmallPtrSet PrimaryBasesSetTy; + +private: /// MostDerivedClass - The most derived class for which we're building this /// vtable. const CXXRecordDecl *MostDerivedClass; /// Context - The ASTContext which we will use for layout information. - const ASTContext &Context; + ASTContext &Context; /// FinalOverriders - The final overriders of the most derived class. FinalOverriders Overriders; @@ -486,42 +505,85 @@ class VtableBuilder { /// AddressPoints - Address points for the vtable being built. CGVtableInfo::AddressPointsMapTy AddressPoints; + void layoutVirtualMemberFunctions(BaseSubobject Base, + PrimaryBasesSetTy &PrimaryBases); + /// layoutSimpleVtable - A test function that will layout very simple vtables /// without any bases. Just used for testing for now. - void layoutSimpleVtable(const CXXRecordDecl *RD); + void layoutSimpleVtable(BaseSubobject Base); public: VtableBuilder(const CXXRecordDecl *MostDerivedClass) : MostDerivedClass(MostDerivedClass), Context(MostDerivedClass->getASTContext()), Overriders(MostDerivedClass) { - layoutSimpleVtable(MostDerivedClass); + layoutSimpleVtable(BaseSubobject(MostDerivedClass, 0)); } /// dumpLayout - Dump the vtable layout. void dumpLayout(llvm::raw_ostream&); }; -void VtableBuilder::layoutSimpleVtable(const CXXRecordDecl *RD) { - assert(!RD->getNumBases() && - "We don't support layout for vtables with bases right now!"); - - // First, add the offset to top. - Components.push_back(VtableComponent::MakeOffsetToTop(0)); - - // Next, add the RTTI. - Components.push_back(VtableComponent::MakeRTTI(RD)); - - // Record the address point. - AddressPoints.insert(std::make_pair(BaseSubobject(RD, 0), Components.size())); +/// OverridesMethodInPrimaryBase - Checks whether whether this virtual member +/// function overrides a member function in a direct or indirect primary base. +/// Returns the overridden member function, or null if none was found. +static const CXXMethodDecl * +OverridesMethodInPrimaryBase(const CXXMethodDecl *MD, + VtableBuilder::PrimaryBasesSetTy &PrimaryBases) { + for (CXXMethodDecl::method_iterator I = MD->begin_overridden_methods(), + E = MD->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *OverriddenMD = *I; + const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent(); + assert(OverriddenMD->isCanonicalDecl() && + "Should have the canonical decl of the overridden RD!"); + + if (PrimaryBases.count(OverriddenRD)) + return OverriddenMD; + } + + return 0; +} + +void +VtableBuilder::layoutVirtualMemberFunctions(BaseSubobject Base, + PrimaryBasesSetTy &PrimaryBases) { + const CXXRecordDecl *RD = Base.getBase(); + + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + + if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) { + if (Layout.getPrimaryBaseWasVirtual()) + assert(false && "FIXME: Handle vbases here."); + else + assert(Layout.getBaseClassOffset(PrimaryBase) == 0 && + "Primary base should have a zero offset!"); + + layoutVirtualMemberFunctions(BaseSubobject(PrimaryBase, 0), PrimaryBases); + + if (!PrimaryBases.insert(PrimaryBase)) + assert(false && "Found a duplicate primary base!"); + } // Now go through all virtual member functions and add them. for (CXXRecordDecl::method_iterator I = RD->method_begin(), E = RD->method_end(); I != E; ++I) { const CXXMethodDecl *MD = *I; - + if (!MD->isVirtual()) continue; + + // Get the final overrider. + const CXXMethodDecl *Overrider = Overriders.getOverrider(Base, MD); + + // 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 (OverridesMethodInPrimaryBase(MD, PrimaryBases)) { + assert(!ReturnTypeConversionRequiresAdjustment(Context, Overrider, MD) && + "FIXME: Handle covariant thunks!"); + + continue; + } if (const CXXDestructorDecl *DD = dyn_cast(MD)) { // Add both the complete destructor and the deleting destructor. @@ -534,6 +596,42 @@ void VtableBuilder::layoutSimpleVtable(const CXXRecordDecl *RD) { } } +void VtableBuilder::layoutSimpleVtable(BaseSubobject Base) { + const CXXRecordDecl *RD = Base.getBase(); + + // First, add the offset to top. + Components.push_back(VtableComponent::MakeOffsetToTop(0)); + + // Next, add the RTTI. + Components.push_back(VtableComponent::MakeRTTI(RD)); + + // Record the address point. + // FIXME: Record the address point for all primary bases. + AddressPoints.insert(std::make_pair(Base, Components.size())); + + // Now go through all virtual member functions and add them. + PrimaryBasesSetTy PrimaryBases; + layoutVirtualMemberFunctions(Base, PrimaryBases); + + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); + + // Traverse bases. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *BaseDecl = + cast(I->getType()->getAs()->getDecl()); + + // Ignore the primary base. + if (BaseDecl == PrimaryBase) + continue; + + assert(!I->isVirtual() && "FIXME: Handle virtual bases"); + + assert(false && "FIXME: Handle secondary virtual tables!"); + } +} + /// dumpLayout - Dump the vtable layout. void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { @@ -1726,7 +1824,7 @@ void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { // Collect all the primary bases, so we can check whether methods override // a method from the base. - llvm::SmallPtrSet PrimaryBases; + VtableBuilder::PrimaryBasesSetTy PrimaryBases; for (ASTRecordLayout::primary_base_info_iterator I = Layout.primary_base_begin(), E = Layout.primary_base_end(); I != E; ++I) @@ -1745,42 +1843,30 @@ void CGVtableInfo::ComputeMethodVtableIndices(const CXXRecordDecl *RD) { bool ShouldAddEntryForMethod = true; // Check if this method overrides a method in the primary base. - for (CXXMethodDecl::method_iterator i = MD->begin_overridden_methods(), - e = MD->end_overridden_methods(); i != e; ++i) { - const CXXMethodDecl *OverriddenMD = *i; - const CXXRecordDecl *OverriddenRD = OverriddenMD->getParent(); - assert(OverriddenMD->isCanonicalDecl() && - "Should have the canonical decl of the overridden RD!"); - - if (PrimaryBases.count(OverriddenRD)) { - // Check if converting from the return type of the method to the - // return type of the overridden method requires conversion. - QualType ReturnType = - MD->getType()->getAs()->getResultType(); - QualType OverriddenReturnType = - OverriddenMD->getType()->getAs()->getResultType(); - - if (!TypeConversionRequiresAdjustment(CGM.getContext(), - ReturnType, OverriddenReturnType)) { - // This index is shared between the index in the vtable of the primary - // base class. - if (const CXXDestructorDecl *DD = dyn_cast(MD)) { - const CXXDestructorDecl *OverriddenDD = - cast(OverriddenMD); - - // Add both the complete and deleting entries. - MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = - getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Complete)); - MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = - getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); - } else { - MethodVtableIndices[MD] = getMethodVtableIndex(OverriddenMD); - } + if (const CXXMethodDecl *OverriddenMD = + OverridesMethodInPrimaryBase(MD, PrimaryBases)) { + // Check if converting from the return type of the method to the + // return type of the overridden method requires conversion. + if (!ReturnTypeConversionRequiresAdjustment(CGM.getContext(), + MD, OverriddenMD)) { + // This index is shared between the index in the vtable of the primary + // base class. + if (const CXXDestructorDecl *DD = dyn_cast(MD)) { + const CXXDestructorDecl *OverriddenDD = + cast(OverriddenMD); - // We don't need to add an entry for this method. - ShouldAddEntryForMethod = false; - break; - } + // Add both the complete and deleting entries. + MethodVtableIndices[GlobalDecl(DD, Dtor_Complete)] = + getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Complete)); + MethodVtableIndices[GlobalDecl(DD, Dtor_Deleting)] = + getMethodVtableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); + } else { + MethodVtableIndices[MD] = getMethodVtableIndex(OverriddenMD); + } + + // We don't need to add an entry for this method. + ShouldAddEntryForMethod = false; + break; } } diff --git a/test/CodeGenCXX/vtable-layout.cpp b/test/CodeGenCXX/vtable-layout.cpp index 3f7e18a095..f7db2135d2 100644 --- a/test/CodeGenCXX/vtable-layout.cpp +++ b/test/CodeGenCXX/vtable-layout.cpp @@ -9,8 +9,8 @@ namespace Test1 { struct A { virtual void f(); }; - void A::f() { } + } namespace Test2 { @@ -36,10 +36,10 @@ struct A { virtual void h(); virtual A& operator=(const A&); }; - void A::f() { } // Another simple vtable dumper test. + // CHECK: Vtable for 'Test2::B' (6 entries). // CHECK-NEXT: 0 | offset_to_top (0) // CHECK-NEXT: 1 | Test2::B RTTI @@ -48,13 +48,68 @@ void A::f() { } // CHECK-NEXT: 3 | void Test2::B::g() [pure] // CHECK-NEXT: 4 | Test2::B::~B() [complete] [pure] // CHECK-NEXT: 5 | Test2::B::~B() [deleting] [pure] - struct B { virtual void f(); virtual void g() = 0; virtual ~B() = 0; }; +void B::f() { } + +} + +namespace Test3 { +// If a function in a derived class overrides a function in a primary base, +// then the function should not have an entry in the derived class (unless the return +// value requires adjusting). + +// CHECK: Vtable for 'Test3::A' (3 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test3::A RTTI +// CHECK-NEXT: -- (Test3::A, 0) vtable address -- +// CHECK-NEXT: 2 | void Test3::A::f() +struct A { + virtual void f(); +}; +void A::f() { } + +// CHECK: Vtable for 'Test3::B' (4 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test3::B RTTI +// CHECK-NEXT: -- (Test3::B, 0) vtable address -- +// CHECK-NEXT: 2 | void Test3::A::f() +// CHECK-NEXT: 3 | void Test3::B::g() +struct B : A { + virtual void f(); + virtual void g(); +}; void B::f() { } +// CHECK: Vtable for 'Test3::C' (5 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test3::C RTTI +// CHECK-NEXT: -- (Test3::C, 0) vtable address -- +// CHECK-NEXT: 2 | void Test3::A::f() +// CHECK-NEXT: 3 | void Test3::C::g() +// CHECK-NEXT: 4 | void Test3::C::h() +struct C : A { + virtual void g(); + virtual void h(); +}; +void C::g() { } + +// CHECK: Vtable for 'Test3::D' (5 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test3::D RTTI +// CHECK-NEXT: -- (Test3::D, 0) vtable address -- +// CHECK-NEXT: 2 | void Test3::A::f() +// CHECK-NEXT: 3 | void Test3::B::g() +// CHECK-NEXT: 4 | void Test3::D::h() +struct D : B { + virtual void f(); + virtual void g(); + virtual void h(); +}; + +void D::f() { } } -- 2.40.0