]> granicus.if.org Git - clang/commitdiff
[MS ABI] Correctly mangling vbase destructors
authorDavid Majnemer <david.majnemer@gmail.com>
Tue, 14 Feb 2017 00:54:11 +0000 (00:54 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Tue, 14 Feb 2017 00:54:11 +0000 (00:54 +0000)
They are a little bit of a special case in the mangling. They are always
mangled without taking into account their virtual-ness of the
destructor. They are also mangled to return void, unlike the actual
destructor.

This fixes PR31931.

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295010 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/MicrosoftMangle.cpp
test/CodeGenCXX/debug-info-ms-dtor-thunks.cpp
test/CodeGenCXX/exceptions-cxx-new.cpp
test/CodeGenCXX/inheriting-constructor.cpp
test/CodeGenCXX/microsoft-abi-dynamic-cast.cpp
test/CodeGenCXX/microsoft-abi-structors.cpp
test/CodeGenCXX/microsoft-abi-throw.cpp
test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
test/CodeGenCXX/regcall.cpp

index 07e38eb7bbacd237b58041b5610a3cbf77a2c0e9..ef0512fd444a11813ced4b836983cea8b2a173ed 100644 (file)
@@ -1890,14 +1890,18 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
   // <return-type> ::= <type>
   //               ::= @ # structors (they have no declared return type)
   if (IsStructor) {
-    if (isa<CXXDestructorDecl>(D) && isStructorDecl(D) &&
-        StructorType == Dtor_Deleting) {
-      // The scalar deleting destructor takes an extra int argument.
-      // However, the FunctionType generated has 0 arguments.
-      // FIXME: This is a temporary hack.
-      // Maybe should fix the FunctionType creation instead?
-      Out << (PointersAre64Bit ? "PEAXI@Z" : "PAXI@Z");
-      return;
+    if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
+      // The scalar deleting destructor takes an extra int argument which is not
+      // reflected in the AST.
+      if (StructorType == Dtor_Deleting) {
+        Out << (PointersAre64Bit ? "PEAXI@Z" : "PAXI@Z");
+        return;
+      }
+      // The vbase destructor returns void which is not reflected in the AST.
+      if (StructorType == Dtor_Complete) {
+        Out << "XXZ";
+        return;
+      }
     }
     if (IsCtorClosure) {
       // Default constructor closure and copy constructor closure both return
@@ -2005,13 +2009,20 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
   // <global-function> ::= Y # global near
   //                   ::= Z # global far
   if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
+    bool IsVirtual = MD->isVirtual();
+    // When mangling vbase destructor variants, ignore whether or not the
+    // underlying destructor was defined to be virtual.
+    if (isa<CXXDestructorDecl>(MD) && isStructorDecl(MD) &&
+        StructorType == Dtor_Complete) {
+      IsVirtual = false;
+    }
     switch (MD->getAccess()) {
       case AS_none:
         llvm_unreachable("Unsupported access specifier");
       case AS_private:
         if (MD->isStatic())
           Out << 'C';
-        else if (MD->isVirtual())
+        else if (IsVirtual)
           Out << 'E';
         else
           Out << 'A';
@@ -2019,7 +2030,7 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
       case AS_protected:
         if (MD->isStatic())
           Out << 'K';
-        else if (MD->isVirtual())
+        else if (IsVirtual)
           Out << 'M';
         else
           Out << 'I';
@@ -2027,7 +2038,7 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
       case AS_public:
         if (MD->isStatic())
           Out << 'S';
-        else if (MD->isVirtual())
+        else if (IsVirtual)
           Out << 'U';
         else
           Out << 'Q';
index 096d2d603b34a3fb8f5ea628438848f1266c0276..f876267eb5d69a3a66b67ad4b062bf94020614f4 100644 (file)
@@ -5,10 +5,10 @@ struct __declspec(dllexport) T { virtual ~T(); };
 struct __declspec(dllexport) U : S, T { virtual ~U(); };
 
 // CHECK-LABEL: define {{.*}} @"\01??_GS@@UAEPAXI@Z"
-// CHECK: call x86_thiscallcc void @"\01??_DS@@UAE@XZ"(%struct.S* %this1){{.*}}!dbg !{{[0-9]+}}
+// CHECK: call x86_thiscallcc void @"\01??_DS@@QAEXXZ"(%struct.S* %this1){{.*}}!dbg !{{[0-9]+}}
 
 // CHECK-LABEL: define {{.*}} @"\01??_GT@@UAEPAXI@Z"
-// CHECK: call x86_thiscallcc void @"\01??_DT@@UAE@XZ"(%struct.T* %this1){{.*}}!dbg !{{[0-9]+}}
+// CHECK: call x86_thiscallcc void @"\01??_DT@@QAEXXZ"(%struct.T* %this1){{.*}}!dbg !{{[0-9]+}}
 
 // CHECK-LABEL: define {{.*}} @"\01??_GU@@UAEPAXI@Z"
-// CHECK: call x86_thiscallcc void @"\01??_DU@@UAE@XZ"(%struct.U* %this1){{.*}}!dbg !{{[0-9]+}}
+// CHECK: call x86_thiscallcc void @"\01??_DU@@QAEXXZ"(%struct.U* %this1){{.*}}!dbg !{{[0-9]+}}
index 3329aea32ef20161dd2207521ffca38e100a8cae..9836da8e540b701cd22d8668553a83933b9f0196 100644 (file)
@@ -55,12 +55,12 @@ void test_cleanup() {
 // CHECK:           to label %[[LEAVE_FUNC:.*]] unwind label %[[CLEANUP:.*]]
 
 // CHECK: [[LEAVE_FUNC]]
-// CHECK:   call x86_thiscallcc void @"\01??_DCleanup@@QAE@XZ"(
+// CHECK:   call x86_thiscallcc void @"\01??_DCleanup@@QAEXXZ"(
 // CHECK:   ret void
 
 // CHECK: [[CLEANUP]]
 // CHECK:   %[[CLEANUPPAD:.*]] = cleanuppad within none []
-// CHECK:   call x86_thiscallcc void @"\01??_DCleanup@@QAE@XZ"(
+// CHECK:   call x86_thiscallcc void @"\01??_DCleanup@@QAEXXZ"(
 // CHECK:   cleanupret from %[[CLEANUPPAD]] unwind to caller
 
 
index a3adf70ebb188393e9342b959363bb1f02e9e027..1be59b9305d338dbcc3f1bb6e0aa0364b5711178 100644 (file)
@@ -137,7 +137,7 @@ namespace inalloca_nonvirt {
   // WIN32: call {{.*}} @"\01??0A@inalloca_nonvirt@@QAE@UQ@@H0$$QAU2@@Z"(%{{[^,]*}}, <{{.*}}>* inalloca %[[ARGMEM]])
   // WIN32: call void @llvm.stackrestore(
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
-  // WIN32: call {{.*}} @"\01??_DQ@@QAE@XZ"(
+  // WIN32: call {{.*}} @"\01??_DQ@@QAEXXZ"(
 
   // On Win64, the Q arguments would be destroyed in the callee. We don't yet
   // support that in the non-inlined case, so we force inlining.
@@ -150,7 +150,7 @@ namespace inalloca_nonvirt {
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
   // WIN64: call {{.*}} @"\01??0A@inalloca_nonvirt@@QEAA@UQ@@H0$$QEAU2@@Z"(%{{.*}}, %{{.*}}* %[[ARG1]], i32 2, %{{.*}}* %[[ARG3]], %{{.*}} %[[TMP]])
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
-  // WIN64: call void @"\01??_DQ@@QEAA@XZ"({{.*}}* %[[TMP]])
+  // WIN64: call void @"\01??_DQ@@QEAAXXZ"({{.*}}* %[[TMP]])
 
   struct C : B { using B::B; };
   C c(1, 2, 3, 4);
@@ -173,7 +173,7 @@ namespace inalloca_nonvirt {
   // WIN32: call {{.*}} @"\01??0A@inalloca_nonvirt@@QAE@UQ@@H0$$QAU2@@Z"(%{{[^,]*}}, <{{.*}}>* inalloca %[[ARGMEM]])
   // WIN32: call void @llvm.stackrestore(
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
-  // WIN32: call {{.*}} @"\01??_DQ@@QAE@XZ"(
+  // WIN32: call {{.*}} @"\01??_DQ@@QAEXXZ"(
 
   // On Win64, the Q arguments would be destroyed in the callee. We don't yet
   // support that in the non-inlined case, so we force inlining.
@@ -186,7 +186,7 @@ namespace inalloca_nonvirt {
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
   // WIN64: call {{.*}} @"\01??0A@inalloca_nonvirt@@QEAA@UQ@@H0$$QEAU2@@Z"(%{{.*}}, %{{.*}}* %[[ARG1]], i32 2, %{{.*}}* %[[ARG3]], %{{.*}} %[[TMP]])
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
-  // WIN64: call void @"\01??_DQ@@QEAA@XZ"({{.*}}* %[[TMP]])
+  // WIN64: call void @"\01??_DQ@@QEAAXXZ"({{.*}}* %[[TMP]])
 }
 
 namespace inalloca_virt {
@@ -224,7 +224,7 @@ namespace inalloca_virt {
   // destroy the parameters, but that's not actually possible.
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
-  // WIN32: call {{.*}} @"\01??_DQ@@QAE@XZ"(
+  // WIN32: call {{.*}} @"\01??_DQ@@QAEXXZ"(
 
   // On Win64, the Q arguments would be destroyed in the callee. We don't yet
   // support that in the non-inlined case, so we force inlining.
@@ -239,7 +239,7 @@ namespace inalloca_virt {
   // WIN64: br
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
-  // WIN64: call void @"\01??_DQ@@QEAA@XZ"({{.*}}* %[[TMP]])
+  // WIN64: call void @"\01??_DQ@@QEAAXXZ"({{.*}}* %[[TMP]])
 
   struct C : B { using B::B; };
   C c(1, 2, 3, 4);
@@ -281,7 +281,7 @@ namespace inalloca_virt {
   //
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
   // WIN32: call {{.*}} @"\01??0Z@@QAE@XZ"(
-  // WIN32: call {{.*}} @"\01??_DQ@@QAE@XZ"(
+  // WIN32: call {{.*}} @"\01??_DQ@@QAEXXZ"(
 
   // On Win64, the Q arguments would be destroyed in the callee. We don't yet
   // support that in the non-inlined case, so we force inlining.
@@ -301,7 +301,7 @@ namespace inalloca_virt {
   // WIN64: br
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
   // WIN64: call {{.*}} @"\01??0Z@@QEAA@XZ"(
-  // WIN64: call void @"\01??_DQ@@QEAA@XZ"({{.*}}* %[[TMP]])
+  // WIN64: call void @"\01??_DQ@@QEAAXXZ"({{.*}}* %[[TMP]])
 }
 
 namespace inline_nonvirt {
index f03cd6c5632b3f8f2e8eaa1146c5489f6155609f..f3e43079c3ebbd900c3a45f5ec8f41ea17c3b958 100644 (file)
@@ -138,6 +138,6 @@ S3 *f(S2 &s) {
 // CHECK:    [[CALL:%.*]] = invoke i8* @__RTDynamicCast
 
 // CHECK:    [[BC:%.*]] = bitcast i8* [[CALL]] to %"struct.PR25606::S3"*
-// CHECK:    call x86_thiscallcc void @"\01??_DCleanup@PR25606@@QAE@XZ"(
+// CHECK:    call x86_thiscallcc void @"\01??_DCleanup@PR25606@@QAEXXZ"(
 // CHECK:    ret %"struct.PR25606::S3"* [[BC]]
 }
index 6397eeb7313f40c07b5823af40071a172f1793f0..4de6c3346611892c852868fd3044c46a2cc862ad 100644 (file)
@@ -207,7 +207,7 @@ F::~F() {
 void foo() {
   F f;
 }
-// DTORS3-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DF@test2@@UAE@XZ"({{.*}} {{.*}} comdat
+// DTORS3-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DF@test2@@QAEXXZ"({{.*}} {{.*}} comdat
 //      Do an adjustment from C* to F*.
 // DTORS3:   getelementptr i8, i8* %{{.*}}, i32 20
 // DTORS3:   bitcast i8* %{{.*}} to %"struct.test2::F"*
@@ -361,12 +361,12 @@ struct D : B, C { ~D(); };
 void call_vbase_complete(D *d) {
   d->~D();
 // CHECK: define void @"\01?call_vbase_complete@dtors@@YAXPAUD@1@@Z"
-// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAEXXZ"(%"struct.dtors::D"* %{{[^,]+}})
 // CHECK: ret
 }
 
 // The complete dtor should call the base dtors for D and the vbase A (once).
-// CHECK: define linkonce_odr x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"({{.*}}) {{.*}} comdat
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??_DD@dtors@@QAEXXZ"({{.*}}) {{.*}} comdat
 // CHECK-NOT: call
 // CHECK: call x86_thiscallcc void @"\01??1D@dtors@@QAE@XZ"
 // CHECK-NOT: call
@@ -377,7 +377,7 @@ void call_vbase_complete(D *d) {
 void destroy_d_complete() {
   D d;
 // CHECK: define void @"\01?destroy_d_complete@dtors@@YAXXZ"
-// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAEXXZ"(%"struct.dtors::D"* %{{[^,]+}})
 // CHECK: ret
 }
 
@@ -387,7 +387,7 @@ void destroy_d_complete() {
 void call_nv_deleting_dtor(D *d) {
   delete d;
 // CHECK: define void @"\01?call_nv_deleting_dtor@dtors@@YAXPAUD@1@@Z"
-// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAEXXZ"(%"struct.dtors::D"* %{{[^,]+}})
 // CHECK: call void @"\01??3@YAXPAX@Z"
 // CHECK: ret
 }
index 7c2e2a8f901e1c7fc347b5bee28c5493653f8498..51076f841611e0d8de29dd918e8e2a3c8f0610ec 100644 (file)
@@ -12,7 +12,7 @@
 // CHECK-DAG: @"\01??_R0?AUV@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUV@@\00" }, comdat
 // CHECK-DAG: @"_CT??_R0?AUV@@@81044" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUV@@@8" to i8*), i32 0, i32 4, i32 4, i32 1, i8* null }, section ".xdata", comdat
 // CHECK-DAG: @"_CTA5?AUY@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.5 { i32 5, [5 x %eh.CatchableType*] [%eh.CatchableType* @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8", %eh.CatchableType* @"_CT??_R0?AUZ@@@81", %eh.CatchableType* @"_CT??_R0?AUW@@@8??0W@@QAE@ABU0@@Z44", %eh.CatchableType* @"_CT??_R0?AUM@@@818", %eh.CatchableType* @"_CT??_R0?AUV@@@81044"] }, section ".xdata", comdat
-// CHECK-DAG: @"_TI5?AUY@@" = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i8* bitcast (void (%struct.Y*)* @"\01??_DY@@QAE@XZ" to i8*), i8* null, i8* bitcast (%eh.CatchableTypeArray.5* @"_CTA5?AUY@@" to i8*) }, section ".xdata", comdat
+// CHECK-DAG: @"_TI5?AUY@@" = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i8* bitcast (void (%struct.Y*)* @"\01??_DY@@QAEXXZ" to i8*), i8* null, i8* bitcast (%eh.CatchableTypeArray.5* @"_CTA5?AUY@@" to i8*) }, section ".xdata", comdat
 // CHECK-DAG: @"_CT??_R0?AUDefault@@@8??_ODefault@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor13* @"\01??_R0?AUDefault@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.Default*, %struct.Default*)* @"\01??_ODefault@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat
 // CHECK-DAG: @"_CT??_R0?AUVariadic@@@8??_OVariadic@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor14* @"\01??_R0?AUVariadic@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.Variadic*, %struct.Variadic*)* @"\01??_OVariadic@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat
 // CHECK-DAG: @"_CT??_R0?AUTemplateWithDefault@@@8??$?_OH@TemplateWithDefault@@QAEXAAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor25* @"\01??_R0?AUTemplateWithDefault@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* bitcast (void (%struct.TemplateWithDefault*, %struct.TemplateWithDefault*)* @"\01??$?_OH@TemplateWithDefault@@QAEXAAU0@@Z" to i8*) }, section ".xdata", comdat
index 416aefa6b7155c7df22bda506c4a326d0f3bc3c5..20ecaf4316503761829c956ffffaef7018aae781 100644 (file)
@@ -84,7 +84,7 @@ B::~B() {
 
   // CHECK: ret
 
-  // CHECK2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B*
+  // CHECK2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DB@@QAEXXZ"(%struct.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
@@ -102,7 +102,7 @@ B::~B() {
   // CHECK2:   %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
   // CHECK2:   store %struct.B* %[[THIS]], %struct.B** %[[THIS_ADDR:.*]], align 4
   // CHECK2:   %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
-  // CHECK2:   call x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B* %[[THIS]])
+  // CHECK2:   call x86_thiscallcc void @"\01??_DB@@QAEXXZ"(%struct.B* %[[THIS]])
   // ...
   // CHECK2: ret
 }
@@ -208,7 +208,7 @@ void call_complete_dtor() {
   B b;
   // CHECK: call x86_thiscallcc %struct.B* @"\01??0B@@QAE@XZ"(%struct.B* %[[B:.*]], i32 1)
   // CHECK-NOT: getelementptr
-  // CHECK: call x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B* %[[B]])
+  // CHECK: call x86_thiscallcc void @"\01??_DB@@QAEXXZ"(%struct.B* %[[B]])
   // CHECK: ret
 }
 
index 0cc6ad009324b2a039fddd32a08efd16bdaef220..0ff6fdf12a58344970f6aea5093804d83a6475a7 100644 (file)
@@ -47,8 +47,8 @@ public:
   // CHECK-LIN-DAG: define linkonce_odr x86_regcallcc void @_ZN10test_classD2Ev
   // CHECK-LIN-DAG: define linkonce_odr x86_regcallcc void @_ZN10test_classD1Ev
   // Windows ignores calling convention on constructor/destructors.
-  // CHECK-WIN64-DAG: define linkonce_odr void @"\01??_Dtest_class@@QEAA@XZ"
-  // CHECK-WIN32-DAG: define linkonce_odr x86_thiscallcc void @"\01??_Dtest_class@@QAE@XZ"
+  // CHECK-WIN64-DAG: define linkonce_odr void @"\01??_Dtest_class@@QEAAXXZ"
+  // CHECK-WIN32-DAG: define linkonce_odr x86_thiscallcc void @"\01??_Dtest_class@@QAEXXZ"
   
   test_class& __regcall operator+=(const test_class&){
     return *this;