From c825d6a6065a8e5507d948f4466da6328a7fb575 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 18 Feb 2014 22:51:52 +0000 Subject: [PATCH] MS ABI: Remove nv adjustment from direct vdtor calls and prologues Summary: Generally the vector deleting dtor, which we model as a vtable thunk, takes care of non-virtual adjustment and delegates to the other destructor variants. The other non-complete destructor variants assume that 'this' on entry points to the virtual base subobject that first declared the virtual destructor. We need to change the adjustment in both the prologue and the vdtor call setup. Reviewers: timurrrr CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D2821 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201612 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/MicrosoftCXXABI.cpp | 15 +++++ test/CodeGenCXX/microsoft-abi-structors.cpp | 75 +++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index d8c7d6ee5a..572bb098c9 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -601,6 +601,13 @@ llvm::Value *MicrosoftCXXABI::adjustThisArgumentForVirtualCall( unsigned AS = cast(This->getType())->getAddressSpace(); llvm::Type *charPtrTy = CGF.Int8Ty->getPointerTo(AS); CharUnits StaticOffset = ML.VFPtrOffset; + + // Base destructors expect 'this' to point to the beginning of the base + // subobject, not the first vfptr that happens to contain the virtual dtor. + // However, we still need to apply the virtual base adjustment. + if (isa(MD) && GD.getDtorType() == Dtor_Base) + StaticOffset = CharUnits::Zero(); + if (ML.VBase) { bool AvoidVirtualOffset = false; if (isa(MD) && GD.getDtorType() == Dtor_Base) { @@ -729,6 +736,14 @@ llvm::Value *MicrosoftCXXABI::adjustThisParameterInVirtualFunctionPrologue( MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD); CharUnits Adjustment = ML.VFPtrOffset; + + // Normal virtual instance methods need to adjust from the vfptr that first + // defined the virtual method to the virtual base subobject, but destructors + // do not. The vector deleting destructor thunk applies this adjustment for + // us if necessary. + if (isa(MD)) + Adjustment = CharUnits::Zero(); + if (ML.VBase) { const ASTRecordLayout &DerivedLayout = CGF.getContext().getASTRecordLayout(MD->getParent()); diff --git a/test/CodeGenCXX/microsoft-abi-structors.cpp b/test/CodeGenCXX/microsoft-abi-structors.cpp index 19fff5dd6a..85b9a7df63 100644 --- a/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -3,6 +3,8 @@ // vftables are emitted very late, so do another pass to try to keep the checks // in source order. // RUN: FileCheck --check-prefix DTORS %s < %t +// RUN: FileCheck --check-prefix DTORS2 %s < %t +// RUN: FileCheck --check-prefix DTORS3 %s < %t // // RUN: %clang_cc1 -emit-llvm %s -o - -mconstructor-aliases -triple=x86_64-pc-win32 -fno-rtti | FileCheck --check-prefix DTORS-X64 %s @@ -121,6 +123,79 @@ void use_D() { D c; } } // end namespace basic +namespace dtor_in_second_nvbase { + +struct A { + virtual void f(); // A needs vftable to be primary. +}; +struct B { + virtual ~B(); +}; +struct C : A, B { + virtual ~C(); +}; + +C::~C() { +// CHECK-LABEL: define x86_thiscallcc void @"\01??1C@dtor_in_second_nvbase@@UAE@XZ" +// CHECK: (%"struct.dtor_in_second_nvbase::C"* %this) +// No this adjustment! +// CHECK-NOT: getelementptr +// CHECK: load %"struct.dtor_in_second_nvbase::C"** %{{.*}} +// Now we this-adjust before calling ~B. +// CHECK: bitcast %"struct.dtor_in_second_nvbase::C"* %{{.*}} to i8* +// CHECK: getelementptr inbounds i8* %{{.*}}, i64 4 +// CHECK: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::B"* +// CHECK: call x86_thiscallcc void @"\01??1B@dtor_in_second_nvbase@@UAE@XZ" +// CHECK: (%"struct.dtor_in_second_nvbase::B"* %{{.*}}) +// CHECK: ret void +} + +void foo() { + C c; +} +// DTORS2-LABEL: define weak x86_thiscallcc void @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z" +// DTORS2: (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete) +// Do an adjustment from B* to C*. +// DTORS2: getelementptr i8* %{{.*}}, i32 -4 +// DTORS2: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"* +// DTORS2: call x86_thiscallcc void @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z" +// DTORS2: ret void + +} + +namespace test2 { +// Just like dtor_in_second_nvbase, except put that in a vbase of a diamond. + +// C's dtor is in the non-primary base. +struct A { virtual void f(); }; +struct B { virtual ~B(); }; +struct C : A, B { virtual ~C(); int c; }; + +// Diamond hierarchy, with C as the shared vbase. +struct D : virtual C { int d; }; +struct E : virtual C { int e; }; +struct F : D, E { ~F(); int f; }; + +F::~F() { +// CHECK-LABEL: define x86_thiscallcc void @"\01??1F@test2@@UAE@XZ"(%"struct.test2::F"*) +// Do an adjustment from C vbase subobject to F as though F was the +// complete type. +// CHECK: getelementptr inbounds i8* %{{.*}}, i32 -20 +// CHECK: bitcast i8* %{{.*}} to %"struct.test2::F"* +// CHECK: store %"struct.test2::F"* +} + +void foo() { + F f; +} +// DTORS3-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DF@test2@@UAE@XZ" +// Do an adjustment from C* to F*. +// DTORS3: getelementptr i8* %{{.*}}, i32 20 +// DTORS3: bitcast i8* %{{.*}} to %"struct.test2::F"* +// DTORS3: call x86_thiscallcc void @"\01??1F@test2@@UAE@XZ" +// DTORS3: ret void + +} namespace constructors { -- 2.40.0