diff --git a/clang/lib/CodeGen/CGVtable.cpp b/clang/lib/CodeGen/CGVtable.cpp index a149aed57df4..15b9a924df63 100644 --- a/clang/lib/CodeGen/CGVtable.cpp +++ b/clang/lib/CodeGen/CGVtable.cpp @@ -156,11 +156,22 @@ public: return OverridersMap.lookup(std::make_pair(Base, MD)); } + /// getReturnAdjustmentOffset - Get the return adjustment offset for the + /// method decl in the given base subobject. Returns an empty base offset if + /// no adjustment is needed. BaseOffset getReturnAdjustmentOffset(BaseSubobject Base, const CXXMethodDecl *MD) const { return ReturnAdjustments.lookup(std::make_pair(Base, MD)); } + /// getThisAdjustmentOffset - Get the 'this' pointer adjustment offset for the + /// method decl in the given base subobject. Returns an empty base offset if + /// no adjustment is needed. + BaseOffset getThisAdjustmentOffset(BaseSubobject Base, + const CXXMethodDecl *MD) const { + return ThisAdjustments.lookup(std::make_pair(Base, MD)); + } + /// dump - dump the final overriders. void dump() const { dump(llvm::errs(), BaseSubobject(MostDerivedClass, 0)); @@ -691,7 +702,7 @@ private: /// AddressPoints - Address points for the vtable being built. CGVtableInfo::AddressPointsMapTy AddressPoints; - /// ReturnAdjustment - A return adjustment thunk. + /// ReturnAdjustment - A return adjustment. struct ReturnAdjustment { /// NonVirtual - The non-virtual adjustment from the derived object to its /// nearest virtual base. @@ -710,13 +721,35 @@ private: llvm::SmallVector, 16> ReturnAdjustments; + /// ThisAdjustment - A 'this' pointer adjustment thunk. + struct ThisAdjustment { + /// NonVirtual - The non-virtual adjustment from the derived object to its + /// nearest virtual base. + int64_t NonVirtual; + + /// FIXME: Add VCallOffsetOffset here. + + ThisAdjustment() : NonVirtual(0) { } + + bool isEmpty() const { return !NonVirtual; } + }; + + /// ThisAdjustments - The 'this' pointer adjustments needed in this vtable. + llvm::SmallVector, 16> + ThisAdjustments; + /// ComputeReturnAdjustment - Compute the return adjustment given a return /// adjustment base offset. ReturnAdjustment ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset); + /// ComputeThisAdjustment - Compute the 'this' pointer adjustment given a + /// 'this' pointer adjustment base offset. + ThisAdjustment ComputeThisAdjustment(FinalOverriders::BaseOffset Offset); + /// AddMethod - Add a single virtual member function to the vtable /// components vector. - void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment); + void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment, + ThisAdjustment ThisAdjustment); /// AddMethods - Add the methods of this base subobject and all its /// primary bases to the vtable components vector. @@ -779,12 +812,33 @@ VtableBuilder::ComputeReturnAdjustment(FinalOverriders::BaseOffset Offset) { return Adjustment; } +VtableBuilder::ThisAdjustment +VtableBuilder::ComputeThisAdjustment(FinalOverriders::BaseOffset Offset) { + ThisAdjustment 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) { + ReturnAdjustment ReturnAdjustment, + ThisAdjustment ThisAdjustment) { if (const CXXDestructorDecl *DD = dyn_cast(MD)) { assert(ReturnAdjustment.isEmpty() && "Destructor can't have return adjustment!"); + // Add the 'this' pointer adjustments if necessary. + if (!ThisAdjustment.isEmpty()) { + ThisAdjustments.push_back(std::make_pair(Components.size(), + ThisAdjustment)); + ThisAdjustments.push_back(std::make_pair(Components.size() + 1, + ThisAdjustment)); + } + // Add both the complete destructor and the deleting destructor. Components.push_back(VtableComponent::MakeCompleteDtor(DD)); Components.push_back(VtableComponent::MakeDeletingDtor(DD)); @@ -794,6 +848,11 @@ VtableBuilder::AddMethod(const CXXMethodDecl *MD, ReturnAdjustments.push_back(std::make_pair(Components.size(), ReturnAdjustment)); + // Add the 'this' pointer adjustment if necessary. + if (!ThisAdjustment.isEmpty()) + ThisAdjustments.push_back(std::make_pair(Components.size(), + ThisAdjustment)); + // Add the function. Components.push_back(VtableComponent::MakeFunction(MD)); } @@ -847,7 +906,13 @@ VtableBuilder::AddMethods(BaseSubobject Base, PrimaryBasesSetTy &PrimaryBases) { ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); - AddMethod(Overrider.Method, ReturnAdjustment); + // Check if this overrider needs a 'this' pointer adjustment. + FinalOverriders::BaseOffset ThisAdjustmentOffset = + Overriders.getThisAdjustmentOffset(Base, MD); + + ThisAdjustment ThisAdjustment = ComputeThisAdjustment(ThisAdjustmentOffset); + + AddMethod(Overrider.Method, ReturnAdjustment, ThisAdjustment); } } @@ -927,6 +992,7 @@ void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { } unsigned NextReturnAdjustmentIndex = 0; + unsigned NextThisAdjustmentIndex = 0; for (unsigned I = 0, E = Components.size(); I != E; ++I) { uint64_t Index = I; @@ -990,7 +1056,7 @@ void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { if (MD->isPure()) Out << " [pure]"; - // If this function pointer has a return adjustment thunk, dump it. + // If this function pointer has a return adjustment, dump it. if (NextReturnAdjustmentIndex < ReturnAdjustments.size() && ReturnAdjustments[NextReturnAdjustmentIndex].first == I) { const ReturnAdjustment Adjustment = @@ -1005,6 +1071,20 @@ void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { NextReturnAdjustmentIndex++; } + + // If this function pointer has a 'this' pointer adjustment, dump it. + if (NextThisAdjustmentIndex < ThisAdjustments.size() && + ThisAdjustments[NextThisAdjustmentIndex].first == I) { + const ThisAdjustment Adjustment = + ThisAdjustments[NextThisAdjustmentIndex].second; + + Out << "\n [this adjustment: "; + Out << Adjustment.NonVirtual << " non-virtual"; + + Out << ']'; + + NextThisAdjustmentIndex++; + } break; } diff --git a/clang/test/CodeGenCXX/vtable-layout.cpp b/clang/test/CodeGenCXX/vtable-layout.cpp index d509431a52fe..181ebc5c3e0c 100644 --- a/clang/test/CodeGenCXX/vtable-layout.cpp +++ b/clang/test/CodeGenCXX/vtable-layout.cpp @@ -1,6 +1,16 @@ // RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm-only -fdump-vtable-layouts 2>&1 | FileCheck %s -namespace Test1 { +// For now, just verify this doesn't crash. +namespace test0 { + struct Obj {}; + + struct Base { virtual const Obj *foo() = 0; }; + struct Derived : Base { virtual Obj *foo() { return new Obj(); } }; + + void test(Derived *D) { D->foo(); } +} + +namespace Test1 { // CHECK: Vtable for 'Test1::A' (3 entries). // CHECK-NEXT: 0 | offset_to_top (0) // CHECK-NEXT: 1 | Test1::A RTTI @@ -202,7 +212,7 @@ void F::g() { } namespace Test5 { -// Simple secondary vtables without this-adjustments. +// Simple secondary vtables without 'this' pointer adjustments. struct A { virtual void f(); virtual void g(); @@ -240,12 +250,33 @@ struct C : B1, B2 { void C::h() { } } -// For now, just verify this doesn't crash. -namespace test0 { - struct Obj {}; +namespace Test6 { - struct Base { virtual const Obj *foo() = 0; }; - struct Derived : Base { virtual Obj *foo() { return new Obj(); } }; +// Simple non-virtual 'this' pointer adjustments. +struct A1 { + virtual void f(); + int a; +}; - void test(Derived *D) { D->foo(); } -} +struct A2 { + virtual void f(); + int a; +}; + +// CHECK: Vtable for 'Test6::C' (6 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test6::C RTTI +// CHECK-NEXT: -- (Test6::A1, 0) vtable address -- +// CHECK-NEXT: -- (Test6::C, 0) vtable address -- +// CHECK-NEXT: 2 | void Test6::C::f() +// CHECK-NEXT: 3 | offset_to_top (-16) +// CHECK-NEXT: 4 | Test6::C RTTI +// CHECK-NEXT: -- (Test6::A2, 16) vtable address -- +// CHECK-NEXT: 5 | void Test6::C::f() +// CHECK-NEXT: [this adjustment: -16 non-virtual] +struct C : A1, A2 { + virtual void f(); +}; +void C::f() { } + +} \ No newline at end of file