From: Anders Carlsson Date: Sat, 13 Feb 2010 20:11:51 +0000 (+0000) Subject: More work on covariant return types. We now handle non-virtual adjustments fine. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7dbf47adb8b8ca84484fae0861ffb2f1eaf44011;p=clang More work on covariant return types. We now handle non-virtual adjustments fine. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96114 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGVtable.cpp b/lib/CodeGen/CGVtable.cpp index 8285a1fdcf..7d3f35f077 100644 --- a/lib/CodeGen/CGVtable.cpp +++ b/lib/CodeGen/CGVtable.cpp @@ -225,14 +225,19 @@ public: /// getOverrider - Get the final overrider for the given method declaration in /// the given base subobject. - const OverriderInfo getOverrider(BaseSubobject Base, - const CXXMethodDecl *MD) const { + OverriderInfo getOverrider(BaseSubobject Base, + const CXXMethodDecl *MD) const { assert(OverridersMap.count(std::make_pair(Base, MD)) && "Did not find overrider!"); return OverridersMap.lookup(std::make_pair(Base, MD)); } + BaseOffset getReturnAdjustmentOffset(BaseSubobject Base, + const CXXMethodDecl *MD) const { + return ReturnAdjustments.lookup(std::make_pair(Base, MD)); + } + /// dump - dump the final overriders. void dump() const { dump(llvm::errs(), BaseSubobject(MostDerivedClass, 0)); @@ -469,7 +474,6 @@ void FinalOverriders::ComputeFinalOverriders(BaseSubobject Base, } void FinalOverriders::dump(llvm::raw_ostream &Out, BaseSubobject Base) const { - const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); @@ -532,9 +536,6 @@ public: CK_DeletingDtorPointer }; - /// dump - Dump the contents of this component to the given stream. - void dump(llvm::raw_ostream &Out); - static VtableComponent MakeOffsetToTop(int64_t Offset) { return VtableComponent(CK_OffsetToTop, Offset); } @@ -670,10 +671,27 @@ private: /// VBaseOffsetIndex - The index relative to the address point of the /// virtual base class offset. int64_t VBaseOffsetIndex; - }; - void layoutVirtualMemberFunctions(BaseSubobject Base, - PrimaryBasesSetTy &PrimaryBases); + ReturnAdjustment() : NonVirtual(0), VBaseOffsetIndex(0) { } + + bool isEmpty() const { return !NonVirtual && !VBaseOffsetIndex; } + }; + + /// ReturnAdjustments - The return adjustments needed in this vtable. + llvm::SmallVector, 16> + ReturnAdjustments; + + /// ComputeReturnAdjustment - Compute the return adjustment given return + /// adjustment base offset. + ReturnAdjustment ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset); + + /// AddMethod - Add a single virtual member function to the vtable + /// components vector. + void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment); + + /// AddMethods - Add the methods of this base subobject and all its + /// primary bases to the vtable components vector. + void AddMethods(BaseSubobject Base, PrimaryBasesSetTy &PrimaryBases); /// layoutSimpleVtable - A test function that will layout very simple vtables /// without any bases. Just used for testing for now. @@ -711,9 +729,41 @@ OverridesMethodInPrimaryBase(const CXXMethodDecl *MD, return 0; } +VtableBuilder::ReturnAdjustment +VtableBuilder::ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset) { + ReturnAdjustment Adjustment; + + if (!Offset.isEmpty()) { + assert(!Offset.VirtualBase && "FIXME: Handle virtual bases!"); + + Adjustment.NonVirtual = Offset.NonVirtualOffset; + } + + return Adjustment; +} + +void +VtableBuilder::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()) + ReturnAdjustments.push_back(std::make_pair(Components.size(), + ReturnAdjustment)); + + // Add the function. + Components.push_back(VtableComponent::MakeFunction(MD)); + } +} + void -VtableBuilder::layoutVirtualMemberFunctions(BaseSubobject Base, - PrimaryBasesSetTy &PrimaryBases) { +VtableBuilder::AddMethods(BaseSubobject Base, PrimaryBasesSetTy &PrimaryBases) { const CXXRecordDecl *RD = Base.getBase(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); @@ -725,7 +775,7 @@ VtableBuilder::layoutVirtualMemberFunctions(BaseSubobject Base, assert(Layout.getBaseClassOffset(PrimaryBase) == 0 && "Primary base should have a zero offset!"); - layoutVirtualMemberFunctions(BaseSubobject(PrimaryBase, 0), PrimaryBases); + AddMethods(BaseSubobject(PrimaryBase, 0), PrimaryBases); if (!PrimaryBases.insert(PrimaryBase)) assert(false && "Found a duplicate primary base!"); @@ -746,22 +796,20 @@ VtableBuilder::layoutVirtualMemberFunctions(BaseSubobject Base, // 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 ATTRIBUTE_UNUSED = - OverridesMethodInPrimaryBase(MD, PrimaryBases)) { - assert(!ReturnTypeConversionRequiresAdjustment(MD, OverriddenMD) - && "FIXME: Handle covariant thunks!"); - - continue; + if (const CXXMethodDecl *OverriddenMD = + OverridesMethodInPrimaryBase(MD, PrimaryBases)) { + if (!ReturnTypeConversionRequiresAdjustment(MD, OverriddenMD)) + continue; } + + // Check if this overrider needs a return adjustment. + FinalOverriders::BaseOffset ReturnAdjustmentOffset = + Overriders.getReturnAdjustmentOffset(Base, MD); + + ReturnAdjustment ReturnAdjustment = + ComputeReturnAdjustment(ReturnAdjustmentOffset); - if (const CXXDestructorDecl *DD = dyn_cast(MD)) { - // Add both the complete destructor and the deleting destructor. - Components.push_back(VtableComponent::MakeCompleteDtor(DD)); - Components.push_back(VtableComponent::MakeDeletingDtor(DD)); - } else { - // Add the function. - Components.push_back(VtableComponent::MakeFunction(MD)); - } + AddMethod(Overrider.Method, ReturnAdjustment); } } @@ -778,7 +826,7 @@ void VtableBuilder::layoutSimpleVtable(BaseSubobject Base) { // Now go through all virtual member functions and add them. PrimaryBasesSetTy PrimaryBases; - layoutVirtualMemberFunctions(Base, PrimaryBases); + AddMethods(Base, PrimaryBases); // Record the address point. AddressPoints.insert(std::make_pair(Base, AddressPoint)); @@ -832,6 +880,7 @@ void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { AddressPointsByIndex.insert(std::make_pair(Index, Base)); } + unsigned NextReturnAdjustmentIndex = 0; for (unsigned I = 0, E = Components.size(); I != E; ++I) { uint64_t Index = I; @@ -895,6 +944,20 @@ void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { if (MD->isPure()) Out << " [pure]"; + // If this function pointer has a return adjustment thunk, dump it. + if (NextReturnAdjustmentIndex < ReturnAdjustments.size() && + ReturnAdjustments[NextReturnAdjustmentIndex].first == I) { + const ReturnAdjustment Adjustment = + ReturnAdjustments[NextReturnAdjustmentIndex].second; + + assert(!Adjustment.VBaseOffsetIndex && "FIXME: Handle virtual bases!"); + + Out << "\n [return adjustment: "; + Out << Adjustment.NonVirtual << " non-virtual]"; + + NextReturnAdjustmentIndex++; + } + break; } @@ -2186,6 +2249,12 @@ int64_t CGVtableInfo::getVirtualBaseOffsetIndex(const CXXRecordDecl *RD, } I = VirtualBaseClassIndicies.find(ClassPair); + // FIXME: The assertion below assertion currently fails with the old vtable + /// layout code if there is a non-virtual thunk adjustment in a vtable. + // Once the new layout is in place, this return should be removed. + if (I == VirtualBaseClassIndicies.end()) + return 0; + assert(I != VirtualBaseClassIndicies.end() && "Did not find index!"); return I->second; diff --git a/test/CodeGenCXX/vtable-layout.cpp b/test/CodeGenCXX/vtable-layout.cpp index b9054410a9..8c839ef6a1 100644 --- a/test/CodeGenCXX/vtable-layout.cpp +++ b/test/CodeGenCXX/vtable-layout.cpp @@ -78,7 +78,7 @@ void A::f() { } // CHECK-NEXT: 1 | Test3::B RTTI // CHECK-NEXT: -- (Test3::A, 0) vtable address -- // CHECK-NEXT: -- (Test3::B, 0) vtable address -- -// CHECK-NEXT: 2 | void Test3::A::f() +// CHECK-NEXT: 2 | void Test3::B::f() // CHECK-NEXT: 3 | void Test3::B::g() struct B : A { virtual void f(); @@ -106,8 +106,8 @@ void C::g() { } // CHECK-NEXT: -- (Test3::A, 0) vtable address -- // CHECK-NEXT: -- (Test3::B, 0) vtable address -- // CHECK-NEXT: -- (Test3::D, 0) vtable address -- -// CHECK-NEXT: 2 | void Test3::A::f() -// CHECK-NEXT: 3 | void Test3::B::g() +// CHECK-NEXT: 2 | void Test3::D::f() +// CHECK-NEXT: 3 | void Test3::D::g() // CHECK-NEXT: 4 | void Test3::D::h() struct D : B { virtual void f(); @@ -118,6 +118,35 @@ struct D : B { void D::f() { } } +namespace Test4 { + +// Test simple non-virtual result adjustments. + +struct R1 { int r1; }; +struct R2 { int r2; }; +struct R3 : R1, R2 { int r3; }; + +struct A { + virtual R2 *f(); +}; + +// CHECK: Vtable for 'Test4::B' (4 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test4::B RTTI +// CHECK-NEXT: -- (Test4::A, 0) vtable address -- +// CHECK-NEXT: -- (Test4::B, 0) vtable address -- +// CHECK-NEXT: 2 | Test4::R3 *Test4::B::f() +// CHECK-NEXT: [return adjustment: 4 non-virtual] +// CHECK-NEXT: 3 | Test4::R3 *Test4::B::f() + +struct B : A { + virtual R3 *f(); +}; + +R3 *B::f() { return 0; } + +} + // For now, just verify this doesn't crash. namespace test0 { struct Obj {};