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

llvm-svn: 201612

clang/lib/CodeGen/MicrosoftCXXABI.cpp
clang/test/CodeGenCXX/microsoft-abi-structors.cpp

index d8c7d6e..572bb09 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 19fff5d..85b9a7d 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 {