[llvm-branch-commits] [clang] fd37b4b - [Clang][CodeGen] Fix this argument type for certain destructors

Tom Stellard via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Mar 2 23:17:46 PST 2023


Author: Jacob Young
Date: 2023-03-02T23:16:39-08:00
New Revision: fd37b4b38d77a0b788695d839ee2b8c07732fb5d

URL: https://github.com/llvm/llvm-project/commit/fd37b4b38d77a0b788695d839ee2b8c07732fb5d
DIFF: https://github.com/llvm/llvm-project/commit/fd37b4b38d77a0b788695d839ee2b8c07732fb5d.diff

LOG: [Clang][CodeGen] Fix this argument type for certain destructors

With the Microsoft ABI, some destructors need to offset a parameter to
get the derived this pointer, in which case the type of that parameter
should not be a pointer to the derived type.

Reviewed By: efriedma

Differential Revision: https://reviews.llvm.org/D143233

(cherry picked from commit 67409911353323ca5edf2049ef0df54132fa1ca7)

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/CodeGen/CGCXXABI.h
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CGExprCXX.cpp
    clang/lib/CodeGen/MicrosoftCXXABI.cpp
    clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
    clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp
    clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp
    clang/test/CodeGenCXX/microsoft-abi-structors.cpp
    clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cae9281522be0..e11b7a79a22f0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1015,6 +1015,9 @@ Windows Support
   by Linux distributions such as Fedora. Also improved such setups by
   avoiding to include ``/usr/include`` among the include paths when cross
   compiling with a cross sysroot based in ``/usr``.
+- Fix incorrect alignment attribute on the this parameter of certain
+  non-complete destructors when using the Microsoft ABI.
+  `Issue 60465 <https://github.com/llvm/llvm-project/issues/60465>`_.
 
 LoongArch Support
 ^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index a600768b20746..78646996eac25 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -379,9 +379,8 @@ class CGCXXABI {
   /// zero if no specific type is applicable, e.g. if the ABI expects the "this"
   /// parameter to point to some artificial offset in a complete object due to
   /// vbases being reordered.
-  virtual const CXXRecordDecl *
-  getThisArgumentTypeForMethod(const CXXMethodDecl *MD) {
-    return MD->getParent();
+  virtual const CXXRecordDecl *getThisArgumentTypeForMethod(GlobalDecl GD) {
+    return cast<CXXMethodDecl>(GD.getDecl())->getParent();
   }
 
   /// Perform ABI-specific "this" argument adjustment required prior to

diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index dfa552161d7ca..ee5b76ab21208 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -322,7 +322,9 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) {
 
   SmallVector<CanQualType, 16> argTypes;
   SmallVector<FunctionProtoType::ExtParameterInfo, 16> paramInfos;
-  argTypes.push_back(DeriveThisType(MD->getParent(), MD));
+
+  const CXXRecordDecl *ThisType = TheCXXABI.getThisArgumentTypeForMethod(GD);
+  argTypes.push_back(DeriveThisType(ThisType, MD));
 
   bool PassParams = true;
 

diff  --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index b889a4e05ee15..a9f3434589f2e 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -33,10 +33,12 @@ struct MemberCallInfo {
 }
 
 static MemberCallInfo
-commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
+commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, GlobalDecl GD,
                                   llvm::Value *This, llvm::Value *ImplicitParam,
                                   QualType ImplicitParamTy, const CallExpr *CE,
                                   CallArgList &Args, CallArgList *RtlArgs) {
+  auto *MD = cast<CXXMethodDecl>(GD.getDecl());
+
   assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
          isa<CXXOperatorCallExpr>(CE));
   assert(MD->isInstance() &&
@@ -44,7 +46,7 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
 
   // Push the this ptr.
   const CXXRecordDecl *RD =
-      CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(MD);
+      CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(GD);
   Args.add(RValue::get(This), CGF.getTypes().DeriveThisType(RD, MD));
 
   // If there is an implicit parameter (e.g. VTT), emit it.
@@ -110,7 +112,7 @@ RValue CodeGenFunction::EmitCXXDestructorCall(
   }
 
   CallArgList Args;
-  commonEmitCXXMemberOrOperatorCall(*this, DtorDecl, This, ImplicitParam,
+  commonEmitCXXMemberOrOperatorCall(*this, Dtor, This, ImplicitParam,
                                     ImplicitParamTy, CE, Args, nullptr);
   return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee,
                   ReturnValueSlot(), Args, nullptr, CE && CE == MustTailCall,
@@ -285,7 +287,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
     assert(ReturnValue.isNull() && "Constructor shouldn't have return value");
     CallArgList Args;
     commonEmitCXXMemberOrOperatorCall(
-        *this, Ctor, This.getPointer(*this), /*ImplicitParam=*/nullptr,
+        *this, {Ctor, Ctor_Complete}, This.getPointer(*this),
+        /*ImplicitParam=*/nullptr,
         /*ImplicitParamTy=*/QualType(), CE, Args, nullptr);
 
     EmitCXXConstructorCall(Ctor, Ctor_Complete, /*ForVirtualBase=*/false,

diff  --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index ae785cce09f96..52d442cc587fd 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -235,11 +235,24 @@ class MicrosoftCXXABI : public CGCXXABI {
 
   void EmitCXXDestructors(const CXXDestructorDecl *D) override;
 
-  const CXXRecordDecl *
-  getThisArgumentTypeForMethod(const CXXMethodDecl *MD) override {
-    if (MD->isVirtual() && !isa<CXXDestructorDecl>(MD)) {
+  const CXXRecordDecl *getThisArgumentTypeForMethod(GlobalDecl GD) override {
+    auto *MD = cast<CXXMethodDecl>(GD.getDecl());
+
+    if (MD->isVirtual()) {
+      GlobalDecl LookupGD = GD;
+      if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+        // Complete dtors take a pointer to the complete object,
+        // thus don't need adjustment.
+        if (GD.getDtorType() == Dtor_Complete)
+          return MD->getParent();
+
+        // There's only Dtor_Deleting in vftable but it shares the this
+        // adjustment with the base one, so look up the deleting one instead.
+        LookupGD = GlobalDecl(DD, Dtor_Deleting);
+      }
       MethodVFTableLocation ML =
-          CGM.getMicrosoftVTableContext().getMethodVFTableLocation(MD);
+          CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
+
       // The vbases might be ordered 
diff erently in the final overrider object
       // and the complete object, so the "this" argument may sometimes point to
       // memory that has no particular type (e.g. past the complete object).

diff  --git a/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp b/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
index 3951211eb1974..34f0bf07206c8 100644
--- a/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
+++ b/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp
@@ -153,7 +153,7 @@ D::~D() { }
 // CHECKFUCHSIA-LABEL: define{{.*}} %class.D* @_ZN1DD1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
 
 // CHECKMS-LABEL: define dso_local x86_thiscallcc noundef %class.D* @"??0D@@QAE at XZ"(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i32 noundef %is_most_derived)
-// CHECKMS-LABEL: define dso_local x86_thiscallcc void @"??1D@@UAE at XZ"(%class.D* {{[^,]*}} %this)
+// CHECKMS-LABEL: define dso_local x86_thiscallcc void @"??1D@@UAE at XZ"(i8*{{[^,]*}} %this.coerce)
 
 class E {
 public:

diff  --git a/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp b/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp
index e8cab3b052a7c..9e7ebcef1d02e 100644
--- a/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp
+++ b/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp
@@ -90,14 +90,13 @@ void delete_D(D *d) { delete d; }
 // For MS, we don't add a new vtable slot to the primary vtable for the virtual
 // destructor. Instead we cast to the VDel base class.
 // CHECK-MSABI: bitcast {{.*}} %[[d]]
-// CHECK-MSABI64-NEXT: getelementptr {{.*}}, i64 8
-// CHECK-MSABI32-NEXT: getelementptr {{.*}}, i32 4
-// CHECK-MSABI-NEXT: %[[d:.*]] = bitcast i8*
+// CHECK-MSABI64-NEXT: %[[d:.*]] = getelementptr {{.*}}, i64 8
+// CHECK-MSABI32-NEXT: %[[d:.*]] = getelementptr {{.*}}, i32 4
 //
 // CHECK: %[[VTABLE:.*]] = load
 // CHECK: %[[DTOR:.*]] = load
 //
-// CHECK: call {{void|noundef i8\*|x86_thiscallcc noundef i8\*}} %[[DTOR]](%{{.*}}* {{[^,]*}} %[[d]]
+// CHECK: call {{void|noundef i8\*|x86_thiscallcc noundef i8\*}} %[[DTOR]]({{.*}}*{{[^,]*}} %[[d]]
 // CHECK-MSABI-SAME: , i32 noundef 1)
 // CHECK-NOT: call
 // CHECK: }

diff  --git a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp
index 67daed2e96208..0e50bbf30a762 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp
@@ -216,7 +216,6 @@ C::C() { foo(); }
 // WIN32-NOT:  load
 // WIN32:      getelementptr i8, i8* %{{.*}}, i32 4
 // WIN32-NOT:  load
-// WIN32:      bitcast i8* %{{.*}} to %"struct.crash_on_partial_destroy::B"*
 // WIN32:      call x86_thiscallcc void @"??1B at crash_on_partial_destroy@@UAE at XZ"
 //
 // WIN32-NOT:  load

diff  --git a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
index eb1adc3b5d6af..676d345da1f22 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
@@ -155,8 +155,7 @@ struct C : A, B {
 };
 
 C::~C() {
-// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1C at dtor_in_second_nvbase@@UAE at XZ"
-// CHECK:       (%"struct.dtor_in_second_nvbase::C"* {{[^,]*}} %this)
+// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1C at dtor_in_second_nvbase@@UAE at XZ"(i8*{{[^,]*}} %this.coerce)
 //      No this adjustment!
 // CHECK-NOT: getelementptr
 // CHECK:   load %"struct.dtor_in_second_nvbase::C"*, %"struct.dtor_in_second_nvbase::C"** %{{.*}}
@@ -164,19 +163,16 @@ C::~C() {
 // CHECK:   bitcast %"struct.dtor_in_second_nvbase::C"* %{{.*}} to i8*
 // CHECK:   getelementptr inbounds i8, i8* %{{.*}}, i32 4
 // CHECK:   bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::B"*
-// CHECK:   call x86_thiscallcc void @"??1B at dtor_in_second_nvbase@@UAE at XZ"
-// CHECK:       (%"struct.dtor_in_second_nvbase::B"* {{[^,]*}} %{{.*}})
+// CHECK:   call x86_thiscallcc void @"??1B at dtor_in_second_nvbase@@UAE at XZ"(%"struct.dtor_in_second_nvbase::B"*{{[^,]*}} %{{.*}})
 // CHECK:   ret void
 }
 
 void foo() {
   C c;
 }
-// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc i8* @"??_EC at dtor_in_second_nvbase@@W3AEPAXI at Z"
-// DTORS2:       (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete)
+// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc i8* @"??_EC at dtor_in_second_nvbase@@W3AEPAXI at Z"(i8* %this.coerce, i32 %should_call_delete)
 //      Do an adjustment from B* to C*.
 // DTORS2:   getelementptr i8, i8* %{{.*}}, i32 -4
-// DTORS2:   bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"*
 // DTORS2:   %[[CALL:.*]] = tail call x86_thiscallcc i8* @"??_GC at dtor_in_second_nvbase@@UAEPAXI at Z"
 // DTORS2:   ret i8* %[[CALL]]
 }
@@ -195,7 +191,7 @@ struct E : virtual C { int e; };
 struct F : D, E { ~F(); int f; };
 
 F::~F() {
-// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1F at test2@@UAE at XZ"(%"struct.test2::F"*{{[^,]*}})
+// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1F at test2@@UAE at XZ"(i8*{{[^,]*}})
 //      Do an adjustment from C vbase subobject to F as though F was the
 //      complete type.
 // CHECK:   getelementptr inbounds i8, i8* %{{.*}}, i32 -20
@@ -209,7 +205,6 @@ void foo() {
 // DTORS3-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"??_DF at test2@@QAEXXZ"({{.*}} {{.*}} comdat
 //      Do an adjustment from C* to F*.
 // DTORS3:   getelementptr i8, i8* %{{.*}}, i32 20
-// DTORS3:   bitcast i8* %{{.*}} to %"struct.test2::F"*
 // DTORS3:   call x86_thiscallcc void @"??1F at test2@@UAE at XZ"
 // DTORS3:   ret void
 

diff  --git a/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp b/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
index 9ddaacbed7bf2..f52a6cdb6fdeb 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
@@ -53,6 +53,7 @@ B::B() {
 B::~B() {
   // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B@@UAE at XZ"
   // Store initial this:
+  // CHECK:   %[[THIS:.*]] = alloca %struct.B*
   // CHECK:   %[[THIS_ADDR:.*]] = alloca %struct.B*
   // CHECK:   store %struct.B* %{{.*}}, %struct.B** %[[THIS_ADDR]], align 4
   // Reload and adjust the this parameter:
@@ -90,8 +91,7 @@ B::~B() {
   // CHECK2: %[[THIS:.*]] = load %struct.B*, %struct.B** {{.*}}
   // CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
   // CHECK2: %[[B_i8:.*]] = getelementptr i8, i8* %[[THIS_i8]], i32 8
-  // CHECK2: %[[B:.*]] = bitcast i8* %[[B_i8]] to %struct.B*
-  // CHECK2: call x86_thiscallcc void @"??1B@@UAE at XZ"(%struct.B* {{[^,]*}} %[[B]])
+  // CHECK2: call x86_thiscallcc void @"??1B@@UAE at XZ"(i8*{{[^,]*}} %[[B_i8]])
   // CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
   // CHECK2: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 8
   // CHECK2: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.VBase*
@@ -99,6 +99,7 @@ B::~B() {
   // CHECK2: ret
 
   // CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GB@@UAEPAXI at Z"
+  // CHECK2:   store %struct.B* %{{.*}}, %struct.B** %[[THIS:.*]], align 4
   // CHECK2:   store %struct.B* %{{.*}}, %struct.B** %[[THIS_ADDR:.*]], align 4
   // CHECK2:   %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
   // CHECK2:   %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
@@ -195,8 +196,7 @@ void delete_B(B *obj) {
 // CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
 // CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
 // CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
-// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
-// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.B*
+// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
 //
 // CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
 // CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
@@ -206,12 +206,12 @@ void delete_B(B *obj) {
 // CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
 // CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
 // CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
-// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (%struct.B*, i32)***
-// CHECK: %[[VFTABLE:.*]] = load i8* (%struct.B*, i32)**, i8* (%struct.B*, i32)*** %[[VFPTR]]
-// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFTABLE]], i64 0
-// CHECK: %[[VFUN_VALUE:.*]] = load i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFUN]]
+// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (i8*, i32)***
+// CHECK: %[[VFTABLE:.*]] = load i8* (i8*, i32)**, i8* (i8*, i32)*** %[[VFPTR]]
+// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTABLE]], i64 0
+// CHECK: %[[VFUN_VALUE:.*]] = load i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFUN]]
 //
-// CHECK: call x86_thiscallcc noundef i8* %[[VFUN_VALUE]](%struct.B* {{[^,]*}} %[[VBASE]], i32 noundef 1)
+// CHECK: call x86_thiscallcc noundef i8* %[[VFUN_VALUE]](i8* {{[^,]*}} %[[VBASE]], i32 noundef 1)
 // CHECK: ret void
 }
 
@@ -295,8 +295,9 @@ struct D : virtual Z, B, C {
 } d;
 
 D::~D() {
-  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1D at diamond@@UAE at XZ"(%"struct.diamond::D"*{{.*}})
+  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1D at diamond@@UAE at XZ"(i8*{{.*}})
   // Store initial this:
+  // CHECK: %[[THIS:.*]] = alloca %"struct.diamond::D"*
   // CHECK: %[[THIS_ADDR:.*]] = alloca %"struct.diamond::D"*
   // CHECK: store %"struct.diamond::D"* %{{.*}}, %"struct.diamond::D"** %[[THIS_ADDR]], align 4
   //
@@ -310,16 +311,13 @@ D::~D() {
   // CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, i8* %[[D_i8]], i32 4
   // CHECK: %[[C:.*]] = bitcast i8* %[[C_i8]] to %"struct.diamond::C"*
   // CHECK: %[[C_i8:.*]] = bitcast %"struct.diamond::C"* %[[C]] to i8*
-  // CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %{{.*}}, i32 16
-  // FIXME: We might consider changing the dtor this parameter type to i8*.
-  // CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::C"*
-  // CHECK: call x86_thiscallcc void @"??1C at diamond@@UAE at XZ"(%"struct.diamond::C"* {{[^,]*}} %[[ARG]])
+  // CHECK: %[[ARG:.*]] = getelementptr i8, i8* %{{.*}}, i32 16
+  // CHECK: call x86_thiscallcc void @"??1C at diamond@@UAE at XZ"(i8*{{[^,]*}} %[[ARG]])
 
   // CHECK: %[[B:.*]] = bitcast %"struct.diamond::D"* %[[THIS]] to %"struct.diamond::B"*
   // CHECK: %[[B_i8:.*]] = bitcast %"struct.diamond::B"* %[[B]] to i8*
-  // CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %[[B_i8]], i32 4
-  // CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::B"*
-  // CHECK: call x86_thiscallcc void @"??1B at diamond@@UAE at XZ"(%"struct.diamond::B"* {{[^,]*}} %[[ARG]])
+  // CHECK: %[[ARG:.*]] = getelementptr i8, i8* %[[B_i8]], i32 4
+  // CHECK: call x86_thiscallcc void @"??1B at diamond@@UAE at XZ"(i8*{{[^,]*}} %[[ARG]])
   // CHECK: ret void
 }
 
@@ -443,9 +441,10 @@ struct E : D, B, virtual A {
 };
 
 E::~E() {
-  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1E at test4@@UAE at XZ"(%"struct.test4::E"* {{[^,]*}} %this)
+  // CHECK-LABEL: define dso_local x86_thiscallcc void @"??1E at test4@@UAE at XZ"(i8*{{[^,]*}} %this.coerce)
 
   // In this case "this" points to the most derived class, so no GEPs needed.
+  // CHECK: bitcast i8* %this.coerce
   // CHECK-NOT: getelementptr
   // CHECK-NOT: bitcast
   // CHECK: %[[VFPTR_i8:.*]] = bitcast %"struct.test4::E"* %{{.*}} to i32 (...)***
@@ -458,16 +457,14 @@ void destroy(E *obj) {
 
   // CHECK-NOT: getelementptr
   // CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ]] to i8*
-  // CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
-  // FIXME: in fact, the call should take i8* and the bitcast is redundant.
-  // CHECK: %[[B_as_E:.*]] = bitcast i8* %[[B_i8]] to %"struct.test4::E"*
+  // CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
   // CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ:.*]] to i8*
   // CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
-  // CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (%"struct.test4::E"*, i32)***
-  // CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::E"*, i32)**, i8* (%"struct.test4::E"*, i32)*** %[[VPTR]]
-  // CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
-  // CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
-  // CHECK: call x86_thiscallcc noundef i8* %[[VFUN]](%"struct.test4::E"* {{[^,]*}} %[[B_as_E]], i32 noundef 1)
+  // CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (i8*, i32)***
+  // CHECK: %[[VFTABLE:.*]] = load i8* (i8*, i32)**, i8* (i8*, i32)*** %[[VPTR]]
+  // CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTABLE]], i64 0
+  // CHECK: %[[VFUN:.*]] = load i8* (i8*, i32)*, i8* (i8*, i32)** %[[VFTENTRY]]
+  // CHECK: call x86_thiscallcc noundef i8* %[[VFUN]](i8*{{[^,]*}} %[[THIS_i8]], i32 noundef 1)
   delete obj;
 }
 
@@ -549,9 +546,37 @@ struct B {
 struct C : virtual B {};
 struct D : virtual A, C {};
 D d;
-// CHECK-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GD at pr36921@@UAEPAXI at Z"(
-// CHECK:   %[[THIS:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
-// CHECK:   %[[THIS_UNADJ_i8:.*]] = bitcast %"struct.pr36921::D"* %[[THIS_RELOAD]] to i8*
-// CHECK:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_UNADJ_i8]], i32 -4
-// CHECK:   %[[THIS:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.pr36921::D"*
+// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef i8* @"??_GD at pr36921@@UAEPAXI at Z"(
+// CHECK2:   %[[THIS:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
+// CHECK2:   %[[THIS_RELOAD:.*]] = load %"struct.pr36921::D"*, %"struct.pr36921::D"**
+// CHECK2:   %[[THIS_UNADJ_i8:.*]] = bitcast %"struct.pr36921::D"* %[[THIS_RELOAD]] to i8*
+// CHECK2:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_UNADJ_i8]], i32 -4
+// CHECK2:   %[[THIS:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.pr36921::D"*
+}
+
+namespace issue_60465 {
+// We used to assume the first argument to all destructors was the derived type
+// even when there was a 'this' adjustment.
+struct A {
+  virtual ~A();
+};
+
+struct alignas(2 * sizeof(void *)) B : virtual A {
+  ~B();
+  void *x, *y;
+};
+
+B::~B() {
+// The 'this' parameter should not have a type of %"struct.issue_60465::B"* and
+// must not have 'align 8', since at least B's copy of A is only 'align 4'.
+// CHECK-LABEL: define dso_local x86_thiscallcc void @"??1B at issue_60465@@UAE at XZ"(i8* noundef %this.coerce)
+// CHECK:   %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, i8* %{{.*}}, i32 -12
+// CHECK:   %[[THIS_ADJ:.*]] = bitcast i8* %[[THIS_ADJ_i8]] to %"struct.issue_60465::B"*
+// CHECK:   %[[X:.*]] = getelementptr inbounds %"struct.issue_60465::B", %"struct.issue_60465::B"* %[[THIS_ADJ]], i32 0, i32 1
+// CHECK:   store i8* null, i8** %[[X]], align 4
+// CHECK:   %[[Y:.*]] = getelementptr inbounds %"struct.issue_60465::B", %"struct.issue_60465::B"* %[[THIS_ADJ]], i32 0, i32 2
+// CHECK:   store i8* null, i8** %[[Y]], align 8
+  x = nullptr;
+  y = nullptr;
+}
 }


        


More information about the llvm-branch-commits mailing list