]> granicus.if.org Git - clang/commitdiff
MS ABI: Remove nv adjustment from direct vdtor calls and prologues
authorReid Kleckner <reid@kleckner.net>
Tue, 18 Feb 2014 22:51:52 +0000 (22:51 +0000)
committerReid Kleckner <reid@kleckner.net>
Tue, 18 Feb 2014 22:51:52 +0000 (22:51 +0000)
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
test/CodeGenCXX/microsoft-abi-structors.cpp

index d8c7d6ee5ab20c87c0fcf7a8fd18ab29993d35df..572bb098c93bd644ebfda72e4b2121b5a3595709 100644 (file)
@@ -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());
index 19fff5dd6a3cd178256e03c899ddd0da5cac764f..85b9a7df63cf27d8afa0bd17a2b6e390189416ac 100644 (file)
@@ -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 {