From bf8b80b623db9a735e7cde0587506e2a321a7729 Mon Sep 17 00:00:00 2001 From: Timur Iskhodzhanov Date: Fri, 7 Mar 2014 09:34:59 +0000 Subject: [PATCH] Fix PR18967 -- Bad this adjustment for virtual methods in a diamond virtual inheritance hierarchy git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@203222 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/VTableBuilder.cpp | 128 +++++------------- .../microsoft-abi-virtual-inheritance.cpp | 7 +- ...tables-multiple-nonvirtual-inheritance.cpp | 53 ++++++-- .../microsoft-abi-vtables-return-thunks.cpp | 42 +++--- ...-vtables-virtual-inheritance-vtordisps.cpp | 62 +++++++++ ...rosoft-abi-vtables-virtual-inheritance.cpp | 51 +++++-- 6 files changed, 207 insertions(+), 136 deletions(-) diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index db305ead62..d975dbe363 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -2520,13 +2520,15 @@ private: /// 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 { - if (!TI.isEmpty()) - VTableThunks[Components.size()] = TI; Components.push_back(VTableComponent::MakeFunction(MD)); } } @@ -2539,47 +2541,10 @@ private: const CXXRecordDecl *LastVBase, BasesSetVectorTy &VisitedBases); - void CheckBadVirtualInheritanceHierarchy() { - // We fail at this-adjustment for virtual methods inherited from - // non-virtual bases that overrides a method in a virtual base. - if (Context.getLangOpts().DumpVTableLayouts) - return; - for (CXXRecordDecl::base_class_const_iterator BI = - MostDerivedClass->bases_begin(), BE = MostDerivedClass->bases_end(); - BI != BE; ++BI) { - const CXXRecordDecl *Base = BI->getType()->getAsCXXRecordDecl(); - if (BI->isVirtual()) - continue; - for (CXXRecordDecl::method_iterator I = Base->method_begin(), - E = Base->method_end(); I != E; ++I) { - const CXXMethodDecl *Method = *I; - if (!Method->isVirtual()) - continue; - if (isa(Method)) - continue; - OverriddenMethodsSetTy OverriddenMethods; - ComputeAllOverriddenMethods(Method, OverriddenMethods); - for (OverriddenMethodsSetTy::const_iterator I = - OverriddenMethods.begin(), - E = OverriddenMethods.end(); I != E; ++I) { - const CXXMethodDecl *Overridden = *I; - if (Base->isVirtuallyDerivedFrom(Overridden->getParent())) { - ErrorUnsupported("classes with non-virtual base " - "classes that override methods in virtual bases", - BI->getLocStart()); - return; - } - } - } - } - } - void LayoutVFTable() { // FIXME: add support for RTTI when we have proper LLVM support for symbols // pointing to the middle of a section. - CheckBadVirtualInheritanceHierarchy(); - BasesSetVectorTy VisitedBases; AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, 0, VisitedBases); @@ -2687,6 +2652,11 @@ VFTableBuilder::ComputeThisOffset(FinalOverriders::OverriderInfo Overrider) { InitialOverriddenDefinitionCollector Collector; visitAllOverriddenMethods(Overrider.Method, Collector); + // If there are no overrides then 'this' is located + // in the base that defines the method. + if (Collector.Bases.size() == 0) + return Overrider.Offset; + CXXBasePaths Paths; Overrider.Method->getParent()->lookupInBases(BaseInSet, &Collector.Bases, Paths); @@ -2899,13 +2869,21 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, Base.getBaseOffset()); - ThisAdjustment ThisAdjustmentOffset; - bool ForceThunk = false; + const CXXMethodDecl *OverriderMD = Overrider.Method; + const CXXMethodDecl *OverriddenMD = + FindNearestOverriddenMethod(MD, VisitedBases); - // Check if this virtual member function overrides - // a method in one of the visited bases. - if (const CXXMethodDecl *OverriddenMD = - FindNearestOverriddenMethod(MD, VisitedBases)) { + ThisAdjustment ThisAdjustmentOffset; + bool ReturnAdjustingThunk = false; + CharUnits ThisOffset = ComputeThisOffset(Overrider); + ThisAdjustmentOffset.NonVirtual = + (ThisOffset - WhichVFPtr.FullOffsetInMDC).getQuantity(); + if ((OverriddenMD || OverriderMD != MD) && + WhichVFPtr.getVBaseWithVPtr()) + CalculateVtordispAdjustment(Overrider, ThisOffset, ThisAdjustmentOffset); + + if (OverriddenMD) { + // If MD overrides anything in this vftable, we need to update the entries. MethodInfoMapTy::iterator OverriddenMDIterator = MethodInfoMap.find(OverriddenMD); @@ -2915,22 +2893,6 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second; - // Create a this-adjusting thunk if needed. - CharUnits TI = ComputeThisOffset(Overrider); - if (TI != WhichVFPtr.FullOffsetInMDC) { - ThisAdjustmentOffset.NonVirtual = - (TI - WhichVFPtr.FullOffsetInMDC).getQuantity(); - } - - if (WhichVFPtr.getVBaseWithVPtr()) - CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset); - - if (!ThisAdjustmentOffset.isEmpty()) { - VTableThunks[OverriddenMethodInfo.VFTableIndex].This = - ThisAdjustmentOffset; - AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]); - } - if (!NeedsReturnAdjustingThunk(MD)) { // No return adjustment needed - just replace the overridden method info // with the current info. @@ -2945,29 +2907,13 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, } // In case we need a return adjustment, we'll add a new slot for - // the overrider and put a return-adjusting thunk where the overridden - // method was in the vftable. - // For now, just mark the overriden method as shadowed by a new slot. + // the overrider. Mark the overriden method as shadowed by the new slot. OverriddenMethodInfo.Shadowed = true; - ForceThunk = true; - - // Also apply this adjustment to the shadowed slots. - if (!ThisAdjustmentOffset.isEmpty()) { - // FIXME: this is O(N^2), can be O(N). - const CXXMethodDecl *SubOverride = OverriddenMD; - while ((SubOverride = - FindNearestOverriddenMethod(SubOverride, VisitedBases))) { - MethodInfoMapTy::iterator SubOverrideIterator = - MethodInfoMap.find(SubOverride); - if (SubOverrideIterator == MethodInfoMap.end()) - break; - MethodInfo &SubOverrideMI = SubOverrideIterator->second; - assert(SubOverrideMI.Shadowed); - VTableThunks[SubOverrideMI.VFTableIndex].This = - ThisAdjustmentOffset; - AddThunk(MD, VTableThunks[SubOverrideMI.VFTableIndex]); - } - } + + // Force a special name mangling for a return-adjusting thunk + // unless the method is the final overrider without this adjustment. + ReturnAdjustingThunk = + !(MD == OverriderMD && 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, @@ -2986,8 +2932,6 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, "Should not have method info for this method yet!"); MethodInfoMap.insert(std::make_pair(MD, MI)); - const CXXMethodDecl *OverriderMD = Overrider.Method; - // Check if this overrider needs a return adjustment. // We don't want to do this for pure virtual member functions. BaseOffset ReturnAdjustmentOffset; @@ -2997,7 +2941,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD); } if (!ReturnAdjustmentOffset.isEmpty()) { - ForceThunk = true; + ReturnAdjustingThunk = true; ReturnAdjustment.NonVirtual = ReturnAdjustmentOffset.NonVirtualOffset.getQuantity(); if (ReturnAdjustmentOffset.VirtualBase) { @@ -3012,7 +2956,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, } AddMethod(OverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, - ForceThunk ? MD : 0)); + ReturnAdjustingThunk ? MD : 0)); } } @@ -3029,11 +2973,13 @@ 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()) { + const char *LinePrefix = "\n "; + if (!R.isEmpty() || TI.Method) { if (!ContinueFirstLine) Out << LinePrefix; - Out << "[return adjustment: "; + Out << "[return adjustment (to type '" + << TI.Method->getReturnType().getCanonicalType().getAsString() + << "'): "; if (R.Virtual.Microsoft.VBPtrOffset) Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", "; if (R.Virtual.Microsoft.VBIndex) @@ -3052,7 +2998,7 @@ static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out, Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", "; if (T.Virtual.Microsoft.VBPtrOffset) { Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset - << " to the left, "; + << " to the left,"; assert(T.Virtual.Microsoft.VBOffsetOffset > 0); Out << LinePrefix << " vboffset at " << T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, "; diff --git a/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp index 0f53f73919..80efdd0490 100644 --- a/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp @@ -217,12 +217,7 @@ void call_complete_dtor() { // CHECK: ret } -struct X : virtual VBase { - int x; -}; - - -struct C : X { +struct C : B { C(); // has an implicit vdtor. }; diff --git a/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp index 387190125b..4f5051b45e 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp @@ -458,10 +458,13 @@ struct Ret1 { struct Test1 : Ret1 { // RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries). // RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo() - // RET-THUNKS-Test1-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test1-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual] // RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo() + // RET-THUNKS-Test1: Thunks for 'this_adjustment::Test1 *return_adjustment::Test1::foo()' (1 entry). + // RET-THUNKS-Test1-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual] + // RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entry). // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo() @@ -477,12 +480,16 @@ struct Ret2 : B, this_adjustment::Test1 { }; struct Test2 : Test1 { // RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries). // RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo() - // RET-THUNKS-Test2-NEXT: [return adjustment: 8 non-virtual] + // RET-THUNKS-Test2-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual] // RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo() - // RET-THUNKS-Test2-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test2-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo() + // RET-THUNKS-Test2: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test2::foo()' (2 entries). + // RET-THUNKS-Test2-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] + // RET-THUNKS-Test2-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual] + // RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entry). // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo() @@ -498,10 +505,13 @@ struct Test3: B, Ret1 { // RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries). // RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo() - // RET-THUNKS-Test3-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test3-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual] // RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo() + // RET-THUNKS-Test3: Thunks for 'this_adjustment::Test1 *return_adjustment::Test3::foo()' (1 entry). + // RET-THUNKS-Test3-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual] + // RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entry). // RET-THUNKS-Test3-NEXT: via vfptr at offset 4 // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo() @@ -518,15 +528,19 @@ struct Test4 : Test3 { // RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries). // RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo() - // RET-THUNKS-Test4-NEXT: [return adjustment: 8 non-virtual] + // RET-THUNKS-Test4-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual] // RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo() - // RET-THUNKS-Test4-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test4-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo() + // RET-THUNKS-Test4: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test4::foo()' (2 entries). + // RET-THUNKS-Test4-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] + // RET-THUNKS-Test4-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual] + // RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entry). // RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 -- - // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo() + // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo() virtual Ret2* foo(); }; @@ -536,19 +550,31 @@ Test4 t4; struct Test5 : Ret1, Test1 { // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries). // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo() - // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual] + // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual] // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo() + // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (1 entry). + // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct C *'): 8 non-virtual] + // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries). // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo() - // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual] + // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual] // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo() - // RET-THUNKS-Test5-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] // RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo() + // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual] + // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] + + // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (3 entries). + // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual] + // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] + // RET-THUNKS-Test5-NEXT: 1 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual] + // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] + // RET-THUNKS-Test5-NEXT: 2 | [return adjustment (to type 'struct C *'): 8 non-virtual] // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] // RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entry). @@ -565,11 +591,16 @@ struct Test6 : Test1 { virtual Ret3* foo(); // RET-THUNKS-Test6: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test6' (4 entries). // RET-THUNKS-Test6-NEXT: 0 | return_adjustment::Ret3 *return_adjustment::Test6::foo() - // RET-THUNKS-Test6-NEXT: [return adjustment: 4 non-virtual] + // RET-THUNKS-Test6-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual] // RET-THUNKS-Test6-NEXT: 1 | void return_adjustment::Ret1::z() // RET-THUNKS-Test6-NEXT: 2 | return_adjustment::Ret3 *return_adjustment::Test6::foo() + // RET-THUNKS-Test6-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual] // RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo() + // RET-THUNKS-Test6: Thunks for 'return_adjustment::Ret3 *return_adjustment::Test6::foo()' (2 entries). + // RET-THUNKS-Test6-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual] + // RET-THUNKS-Test6-NEXT: 1 | [return adjustment (to type 'struct C *'): 4 non-virtual] + // RET-THUNKS-Test6: VFTable indices for 'return_adjustment::Test6' (1 entry). // RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo() }; diff --git a/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp b/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp index 7812b58bde..a4a21106c6 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp @@ -20,25 +20,27 @@ struct K : J { virtual E *foo(); K(); }; J::J() {} // VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' (3 entries). -// VFTABLES: 0 | test1::D *test1::J::foo() -// VFTABLES: [return adjustment: 4 non-virtual] -// VFTABLES: 1 | test1::D *test1::J::foo() -// VFTABLES: 2 | test1::D *test1::J::foo() +// VFTABLES-NEXT: 0 | test1::D *test1::J::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual] +// VFTABLES-NEXT: 1 | test1::D *test1::J::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual] +// VFTABLES-NEXT: 2 | test1::D *test1::J::foo() // GLOBALS-LABEL: @"\01??_7J@test1@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*] // GLOBALS: @"\01?foo@J@test1@@QAEPAUB@2@XZ" // GLOBALS: @"\01?foo@J@test1@@QAEPAUC@2@XZ" -// GLOBALS: @"\01?foo@J@test1@@QAEPAUD@2@XZ" -// FIXME: Should be UAEPAUD. +// GLOBALS: @"\01?foo@J@test1@@UAEPAUD@2@XZ" K::K() {} // VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' in 'test1::K' (4 entries). -// VFTABLES: 0 | test1::E *test1::K::foo() -// VFTABLES: [return adjustment: 4 non-virtual] -// VFTABLES: 1 | test1::E *test1::K::foo() -// VFTABLES: 2 | test1::E *test1::K::foo() -// VFTABLES: 3 | test1::E *test1::K::foo() +// VFTABLES-NEXT: 0 | test1::E *test1::K::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual] +// VFTABLES-NEXT: 1 | test1::E *test1::K::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual] +// VFTABLES-NEXT: 2 | test1::E *test1::K::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test1::D *'): 0 non-virtual] +// VFTABLES-NEXT: 3 | test1::E *test1::K::foo() // Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of // which are trivial. @@ -46,8 +48,7 @@ K::K() {} // GLOBALS: @"\01?foo@K@test1@@QAEPAUB@2@XZ" // GLOBALS: @"\01?foo@K@test1@@QAEPAUC@2@XZ" // GLOBALS: @"\01?foo@K@test1@@QAEPAUD@2@XZ" -// GLOBALS: @"\01?foo@K@test1@@QAEPAUE@2@XZ" -// FIXME: Should be UAEPAUE. +// GLOBALS: @"\01?foo@K@test1@@UAEPAUE@2@XZ" // This thunk has a return adjustment. // CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUB@2@XZ" @@ -85,19 +86,20 @@ struct K : J { virtual E *foo(); K(); }; J::J() {} // VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' (2 entries). -// VFTABLES: 0 | test2::D *test2::J::foo() -// VFTABLES: [return adjustment: 4 non-virtual] -// VFTABLES: 1 | test2::D *test2::J::foo() +// VFTABLES-NEXT: 0 | test2::D *test2::J::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual] +// VFTABLES-NEXT: 1 | test2::D *test2::J::foo() // GLOBALS-LABEL: @"\01??_7J@test2@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*] K::K() {} // VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' in 'test2::K' (3 entries). -// VFTABLES: 0 | test2::E *test2::K::foo() -// VFTABLES: [return adjustment: 4 non-virtual] -// VFTABLES: 1 | test2::E *test2::K::foo() -// VFTABLES: 2 | test2::E *test2::K::foo() +// VFTABLES-NEXT: 0 | test2::E *test2::K::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual] +// VFTABLES-NEXT: 1 | test2::E *test2::K::foo() +// VFTABLES-NEXT: [return adjustment (to type 'struct test2::D *'): 0 non-virtual] +// VFTABLES-NEXT: 2 | test2::E *test2::K::foo() // GLOBALS-LABEL: @"\01??_7K@test2@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*] diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp index f0cfa1fed4..bae9ea61fd 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp @@ -70,6 +70,12 @@ struct A : virtual V1 { // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting] // VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-A: Thunks for 'simple::A::~A()' (1 entry). + // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] + + // VTABLE-SIMPLE-A: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] + virtual void f(); // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ" @@ -85,12 +91,21 @@ struct B : virtual V3 { // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting] // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry). + // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries). // VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f() // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting] // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] + // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry). + // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] + + // VTABLE-SIMPLE-B: Thunks for 'void simple::B::f()' (1 entry). + // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual] + // FIXME: The vtordisp thunk should only get emitted for a constructor // if "this" leaves scope. B() { use_somewhere_else(this); } @@ -111,18 +126,33 @@ struct C : virtual V4 { // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry). + // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries). // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f() // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] + // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry). + // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] + + // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry). + // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual] + // VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries). // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f() // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual] // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual] + // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry). + // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual] + + // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry). + // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual] + int x; virtual void f(); // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ" @@ -159,6 +189,10 @@ struct A : virtual simple::A { // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting] // VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-EXTENDED-A: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-EXTENDED-A-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // `vtordispex{8,8,4294967292,8}' // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" @@ -179,6 +213,10 @@ struct B : virtual simple::A { // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting] // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-EXTENDED-B: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-EXTENDED-B-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-B-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // vtordisp{4294967292,0} // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z" }; @@ -191,6 +229,10 @@ struct C : virtual simple::A { // VTABLE-EXTENDED-C-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left, // VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // VTABLE-EXTENDED-C: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-EXTENDED-C-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left, + // VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // `vtordispex{12,8,4294967292,8}' // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ" int x; @@ -212,6 +254,10 @@ struct E : virtual D { // VTABLE-EXTENDED-E-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, // VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual] + // VTABLE-EXTENDED-E: Thunks for 'void extended::D::f()' (1 entry). + // VTABLE-EXTENDED-E-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual] + // `vtordispex{8,8,4294967292,12}' // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ" @@ -227,6 +273,10 @@ struct F : virtual Z, virtual D { // VTABLE-EXTENDED-F-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left, // VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual] + // VTABLE-EXTENDED-F: Thunks for 'void extended::D::f()' (1 entry). + // VTABLE-EXTENDED-F-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left, + // VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual] + // `vtordispex{20,12,4294967292,12}' // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ" int x; @@ -247,6 +297,10 @@ struct G : virtual simple::A { // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting] // VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-EXTENDED-G: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-EXTENDED-G-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // Emits a G's own vfptr, thus moving the vbptr in the layout. virtual void g(); @@ -267,6 +321,10 @@ struct H : Z, A { // VTABLE-EXTENDED-H-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, // VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // VTABLE-EXTENDED-H: Thunks for 'void simple::A::f()' (1 entry). + // VTABLE-EXTENDED-H-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z" }; @@ -284,6 +342,10 @@ struct A : virtual simple::B { // VTABLE-PR17738-A-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left, // VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual] + // VTABLE-PR17738-A: Thunks for 'void simple::B::f()' (1 entry). + // VTABLE-PR17738-A-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left, + // VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual] + // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ" int a; virtual ~A(); diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp index d6f8bc8408..82eac8a047 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp @@ -11,6 +11,7 @@ // RUN: FileCheck --check-prefix=TEST7 %s < %t // RUN: FileCheck --check-prefix=TEST8-X %s < %t // RUN: FileCheck --check-prefix=TEST8-Z %s < %t +// RUN: FileCheck --check-prefix=TEST8-T %s < %t // RUN: FileCheck --check-prefix=TEST9-Y %s < %t // RUN: FileCheck --check-prefix=TEST9-Z %s < %t // RUN: FileCheck --check-prefix=TEST9-W %s < %t @@ -48,7 +49,7 @@ struct C: virtual A { // MANGLING-DAG: @"\01??_7C@@6B@" - virtual void f(); + virtual void f() {} }; C c; @@ -156,6 +157,9 @@ struct X: virtual C { // TEST4-NEXT: [this adjustment: 8 non-virtual] // TEST4-NEXT: 1 | void A::z() + // TEST4: Thunks for 'void C::f()' (1 entry). + // TEST4-NEXT: 0 | [this adjustment: 8 non-virtual] + // TEST4-NOT: VFTable indices for 'Test4::X' // MANGLING-DAG: @"\01??_7X@Test4@@6B@" @@ -260,7 +264,7 @@ X x; // Another diamond inheritance which led to AST crashes. struct Y : virtual A {}; -class Z : Y, C { +struct Z : Y, C { // TEST8-Z: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries). // TEST8-Z-NEXT: 0 | void Test8::Z::f() // TEST8-Z-NEXT: 1 | void A::z() @@ -271,6 +275,27 @@ class Z : Y, C { virtual void f(); }; Z z; + +// Another diamond inheritance which we miscompiled (PR18967). +struct W : virtual A { + virtual void bar(); +}; + +struct T : W, C { + // TEST8-T: VFTable for 'Test8::W' in 'Test8::T' (1 entry) + // TEST8-T-NEXT: 0 | void Test8::T::bar() + + // TEST8-T: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries) + // TEST8-T-NEXT: 0 | void C::f() + // TEST8-T-NEXT: [this adjustment: -4 non-virtual] + // TEST8-T-NEXT: 1 | void A::z() + + // TEST8-T: Thunks for 'void C::f()' (1 entry). + // TEST8-T-NEXT: 0 | [this adjustment: -4 non-virtual] + virtual void bar(); + int field; +}; +T t; } namespace Test9 { @@ -473,7 +498,7 @@ struct U : virtual W { // VDTORS-U-NEXT: [this adjustment: -4 non-virtual] // VDTORS-U-NEXT: 1 | void vdtors::X::zzz() - // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry). + // VDTORS-U: Thunks for 'vdtors::U::~U()' (1 entry). // VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTORS-U: VFTable indices for 'vdtors::U' (1 entry). @@ -493,7 +518,7 @@ struct V : virtual W { // VDTORS-V-NEXT: [this adjustment: -4 non-virtual] // VDTORS-V-NEXT: 1 | void vdtors::X::zzz() - // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry). + // VDTORS-V: Thunks for 'vdtors::V::~V()' (1 entry). // VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTORS-V: VFTable indices for 'vdtors::V' (1 entry). @@ -537,9 +562,12 @@ struct Z { struct W : Z { // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries). // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo() - // RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual] + // RET-W-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() + // RET-W: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry). + // RET-W-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] + // RET-W: VFTable indices for 'return_adjustment::W' (1 entry). // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() @@ -551,11 +579,15 @@ W y; struct T : W { // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries). // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo() - // RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual] + // RET-T-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo() - // RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual] + // RET-T-NEXT: [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual] // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() + // RET-T: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries). + // RET-T-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] + // RET-T-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual] + // RET-T: VFTable indices for 'return_adjustment::T' (1 entry). // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() @@ -571,9 +603,12 @@ struct U : virtual A { struct V : Z { // RET-V: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries). // RET-V-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo() - // RET-V-NEXT: [return adjustment: vbptr at offset 4, vbase #1, 0 non-virtual] + // RET-V-NEXT: [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual] // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() + // RET-V: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry). + // RET-V-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual] + // RET-V: VFTable indices for 'return_adjustment::V' (1 entry). // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() -- 2.40.0