diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index d8c7d6ee5ab20c87c0fcf7a8fd18ab29993d35df..572bb098c93bd644ebfda72e4b2121b5a3595709 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -601,6 +601,13 @@ llvm::Value *MicrosoftCXXABI::adjustThisArgumentForVirtualCall( unsigned AS = cast<llvm::PointerType>(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<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base) + StaticOffset = CharUnits::Zero(); + if (ML.VBase) { bool AvoidVirtualOffset = false; if (isa<CXXDestructorDecl>(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<CXXDestructorDecl>(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 19fff5dd6a3cd178256e03c899ddd0da5cac764f..85b9a7df63cf27d8afa0bd17a2b6e390189416ac 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 {