Hi timurrrr,
Previously it was thought that the Microsoft ABI used '?1' for
complete dtors. In fact, '?1' is for base dtors, and '?_D' is for
complete dtors. (Some confusion arose from documentation referring
to complete dtors as 'vbase dtors'.) The dtor forms other than the
base dtor are emitted by a TU when required by that TU, even when
the body is not present.
Also, fix the implicit parameter belonging to deleting destructors.
That parameter is an integer, not a boolean.
http://llvm-reviews.chandlerc.com/D823
Files:
include/clang/Basic/TargetCXXABI.h
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/microsoft-abi-static-initializers.cpp
test/CodeGenCXX/microsoft-abi-structors.cpp
Index: include/clang/Basic/TargetCXXABI.h
===================================================================
--- include/clang/Basic/TargetCXXABI.h
+++ include/clang/Basic/TargetCXXABI.h
@@ -141,12 +141,6 @@
return isItaniumFamily();
}
- /// \brief Does this ABI have different entrypoints for complete-object
- /// and base-subobject destructors?
- bool hasDestructorVariants() const {
- return isItaniumFamily();
- }
-
/// \brief Does this ABI allow virtual bases to be primary base classes?
bool hasPrimaryVBases() const {
return isItaniumFamily();
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -504,9 +504,9 @@
// use the type we were given.
mangleCXXDtorType(static_cast<CXXDtorType>(StructorType));
else
- // Otherwise, use the complete destructor name. This is relevant if a
+ // Otherwise, use the base destructor name. This is relevant if a
// class with a destructor is declared within a destructor.
- mangleCXXDtorType(Dtor_Complete);
+ mangleCXXDtorType(Dtor_Base);
break;
case DeclarationName::CXXConversionFunctionName:
@@ -574,13 +574,11 @@
Out << "?_G";
return;
case Dtor_Base:
- // FIXME: We should be asked to mangle base dtors.
- // However, fixing this would require larger changes to the CodeGenModule.
- // Please put llvm_unreachable here when CGM is changed.
- // For now, just mangle a base dtor the same way as a complete dtor...
- case Dtor_Complete:
Out << "?1";
return;
+ case Dtor_Complete:
+ Out << "?_D";
+ return;
}
llvm_unreachable("Unsupported dtor type?");
}
Index: lib/CodeGen/CGCXX.cpp
===================================================================
--- lib/CodeGen/CGCXX.cpp
+++ lib/CodeGen/CGCXX.cpp
@@ -229,15 +229,17 @@
}
void CodeGenModule::EmitCXXDestructors(const CXXDestructorDecl *D) {
- // The destructor in a virtual table is always a 'deleting'
- // destructor, which calls the complete destructor and then uses the
- // appropriate operator delete.
- if (D->isVirtual())
- EmitGlobal(GlobalDecl(D, Dtor_Deleting));
-
- // The destructor used for destructing this as a most-derived class;
- // call the base destructor and then destructs any virtual bases.
- EmitGlobal(GlobalDecl(D, Dtor_Complete));
+ if (getTarget().getCXXABI().isItaniumFamily()) {
+ // The destructor in a virtual table is always a 'deleting'
+ // destructor, which calls the complete destructor and then uses the
+ // appropriate operator delete.
+ if (D->isVirtual())
+ EmitGlobal(GlobalDecl(D, Dtor_Deleting));
+
+ // The destructor used for destructing this as a most-derived class;
+ // call the base destructor and then destructs any virtual bases.
+ EmitGlobal(GlobalDecl(D, Dtor_Complete));
+ }
// The destructor used for destructing this as a base class; ignores
// virtual bases.
Index: lib/CodeGen/CGClass.cpp
===================================================================
--- lib/CodeGen/CGClass.cpp
+++ lib/CodeGen/CGClass.cpp
@@ -1282,8 +1282,7 @@
// Enter the cleanup scopes for virtual bases.
EnterDtorCleanups(Dtor, Dtor_Complete);
- if (!isTryBody &&
- CGM.getTarget().getCXXABI().hasDestructorVariants()) {
+ if (!isTryBody) {
EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
/*Delegating=*/false, LoadCXXThis());
break;
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -527,6 +527,14 @@
if (Linkage == GVA_C99Inline)
return llvm::Function::AvailableExternallyLinkage;
+ // In the Microsoft ABI, most destructor types are not emitted with the
+ // definition, and must be emitted on demand. Give these linkonce_odr
+ // linkage.
+ if (getTarget().getCXXABI().isMicrosoft() && isa<CXXDestructorDecl>(D)) {
+ if (GD.getDtorType() != Dtor_Base)
+ return llvm::Function::LinkOnceODRLinkage;
+ }
+
// Note that Apple's kernel linker doesn't support symbol
// coalescing, so we need to avoid linkonce and weak linkages there.
// Normally, this means we just map to internal, but for explicit
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -201,6 +201,8 @@
llvm::Value *MemPtr,
const MemberPointerType *MPT);
+ virtual bool NeedsBodylessEmission(GlobalDecl GD);
+
};
}
@@ -262,8 +264,8 @@
// TODO: 'for base' flag
if (Type == Dtor_Deleting) {
- // The scalar deleting destructor takes an implicit bool parameter.
- ArgTys.push_back(CGM.getContext().BoolTy);
+ // The deleting destructors take an implicit int parameter.
+ ArgTys.push_back(CGM.getContext().IntTy);
}
}
@@ -294,13 +296,17 @@
Params.push_back(IsMostDerived);
getStructorImplicitParamDecl(CGF) = IsMostDerived;
} else if (IsDeletingDtor(CGF.CurGD)) {
- ImplicitParamDecl *ShouldDelete
+ // Deleting dtors decide their behavior based on the second parameter,
+ // an integer in which the 2 lowest bits are significant. The lower bit
+ // decides whether to deallocate memory, and the higher bit decides whether
+ // to treat the operand like an array allocated with 'new []'.
+ ImplicitParamDecl *Flags
= ImplicitParamDecl::Create(Context, 0,
CGF.CurGD.getDecl()->getLocation(),
- &Context.Idents.get("should_call_delete"),
- Context.BoolTy);
- Params.push_back(ShouldDelete);
- getStructorImplicitParamDecl(CGF) = ShouldDelete;
+ &Context.Idents.get("flags"),
+ Context.IntTy);
+ Params.push_back(Flags);
+ getStructorImplicitParamDecl(CGF) = Flags;
}
}
@@ -326,7 +332,7 @@
getStructorImplicitParamValue(CGF)
= CGF.Builder.CreateLoad(
CGF.GetAddrOfLocalVar(getStructorImplicitParamDecl(CGF)),
- "should_call_delete");
+ "flags");
}
}
@@ -371,11 +377,11 @@
ASTContext &Context = CGF.getContext();
llvm::Value *ImplicitParam
- = llvm::ConstantInt::get(llvm::IntegerType::getInt1Ty(CGF.getLLVMContext()),
- DtorType == Dtor_Deleting);
+ = llvm::ConstantInt::get(llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
+ DtorType == Dtor_Deleting);
return CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValue, This,
- ImplicitParam, Context.BoolTy, 0, 0);
+ ImplicitParam, Context.IntTy, 0, 0);
}
bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
@@ -1118,6 +1124,14 @@
return Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo());
}
+bool MicrosoftCXXABI::NeedsBodylessEmission(GlobalDecl GD) {
+ // In the Microsoft ABI, all destructor types except the base destructor are
+ // not emitted with the definition, and must be emitted on demand.
+ if (isa<CXXDestructorDecl>(GD.getDecl()))
+ return GD.getDtorType() != Dtor_Base;
+ return false;
+}
+
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}
Index: test/CodeGenCXX/microsoft-abi-static-initializers.cpp
===================================================================
--- test/CodeGenCXX/microsoft-abi-static-initializers.cpp
+++ test/CodeGenCXX/microsoft-abi-static-initializers.cpp
@@ -1,19 +1,23 @@
// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
struct S {
- S() {}
- ~S() {}
+ S();
+ ~S();
} s;
// CHECK: define internal void [[INIT_s:@.*global_var.*]] [[NUW:#[0-9]+]]
// CHECK: %{{[.0-9A-Z_a-z]+}} = call x86_thiscallcc %struct.S* @"\01??0S@@QAE@XZ"
// CHECK: call i32 @atexit(void ()* @"__dtor_\01?s@@3US@@A")
// CHECK: ret void
-// CHECK: define internal void @"__dtor_\01?s@@3US@@A"() [[NUW]] {
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??_DS@@QAE@XZ"
// CHECK: call x86_thiscallcc void @"\01??1S@@QAE@XZ"
// CHECK: ret void
+// CHECK: define internal void @"__dtor_\01?s@@3US@@A"() [[NUW]] {
+// CHECK: call x86_thiscallcc void @"\01??_DS@@QAE@XZ"
+// CHECK: ret void
+
// Force WeakODRLinkage by using templates
class A {
public:
@@ -40,12 +44,12 @@
// CHECK: define linkonce_odr x86_thiscallcc %class.A* @"\01??0A@@QAE@XZ"
-// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1A@@QAE@XZ"
-
// CHECK: define internal void [[FOO_DTOR]]
-// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"{{.*}}foo
+// CHECK: call x86_thiscallcc void @"\01??_DA@@QAE@XZ"{{.*}}foo
// CHECK: ret void
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1A@@QAE@XZ"
+
// CHECK: define internal void @_GLOBAL__I_a() [[NUW]] {
// CHECK: call void [[INIT_s]]
// CHECK: call void [[INIT_foo]]
Index: test/CodeGenCXX/microsoft-abi-structors.cpp
===================================================================
--- test/CodeGenCXX/microsoft-abi-structors.cpp
+++ test/CodeGenCXX/microsoft-abi-structors.cpp
@@ -1,8 +1,14 @@
// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 -fno-rtti > %t 2>&1
// RUN: FileCheck %s < %t
-// Using a different check prefix as the inline destructors might be placed
+// Using a different check prefix as the inline con/destructors might be placed
// anywhere in the output.
-// RUN: FileCheck --check-prefix=DTORS %s < %t
+// RUN: FileCheck --check-prefix=CTOR1 %s < %t
+// RUN: FileCheck --check-prefix=DTOR1 %s < %t
+// RUN: FileCheck --check-prefix=DTOR2 %s < %t
+// RUN: FileCheck --check-prefix=DTOR3 %s < %t
+// RUN: FileCheck --check-prefix=DTOR4 %s < %t
+// RUN: FileCheck --check-prefix=DTOR5 %s < %t
+// RUN: FileCheck --check-prefix=DTOR6 %s < %t
namespace basic {
@@ -23,44 +29,55 @@
// CHECK-NEXT: }
// Make sure that the destructor doesn't call itself:
-// CHECK: define {{.*}} @"\01??1A@basic@@QAE@XZ"
-// CHECK-NOT: call void @"\01??1A@basic@@QAE@XZ"
-// CHECK: ret
+// DTOR1: define {{.*}} @"\01??1A@basic@@QAE@XZ"
+// DTOR1-NOT: call void @"\01??1A@basic@@QAE@XZ"
+// DTOR1: ret
}
struct B {
B();
+ ~B();
};
// Tests that we can define constructors outside the class (PR12784).
B::B() {
// CHECK: define x86_thiscallcc %"struct.basic::B"* @"\01??0B@basic@@QAE@XZ"(%"struct.basic::B"* %this)
// CHECK: ret
}
+// Tests that we can define destructors outside the class.
+B::~B() {
+ // CHECK: define x86_thiscallcc void @"\01??1B@basic@@QAE@XZ"(%"struct.basic::B"* %this)
+ // CHECK: ret
+}
+
+// We shouldn't emit the other destructor forms as they aren't needed by the TU.
+// CHECK-NOT: @"\01??_DB@basic@@UAE@XZ"
+// CHECK-NOT: @"\01??_GB@basic@@UAEPAXI@Z"
+// CHECK-NOT: @"\01??_EB@basic@@UAEPAXI@Z"
+
struct C {
virtual ~C() {
// Complete destructor first:
-// DTORS: define {{.*}} x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %this)
+// DTOR2: define {{.*}} x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %this)
// Then, the scalar deleting destructor (used in the vtable):
// FIXME: add a test that verifies that the out-of-line scalar deleting
// destructor is linkonce_odr too.
-// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i1 zeroext %should_call_delete)
-// DTORS: %[[FROMBOOL:[0-9a-z]+]] = zext i1 %should_call_delete to i8
-// DTORS-NEXT: store i8 %[[FROMBOOL]], i8* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 1
-// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i8* %[[SHOULD_DELETE_VAR]]
-// DTORS: call x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]])
-// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i8 %[[SHOULD_DELETE_VALUE]], 0
-// DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]]
+// DTOR3: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %flags)
+// DTOR3: store i32 %flags, i32* %[[FLAGS_VAR:[0-9a-z._]+]], align 4
+// DTOR3: %[[FLAGS_VALUE:[0-9a-z._]+]] = load i32* %[[FLAGS_VAR]]
+// DTOR3: call x86_thiscallcc void @"\01??_DC@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]])
+// DTOR3-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[FLAGS_VALUE]], 0
+// DTOR3-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]]
//
-// DTORS: [[CALL_DELETE_LABEL]]
-// DTORS-NEXT: %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8*
-// DTORS-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[THIS_AS_VOID]]) [[NUW:#[0-9]+]]
-// DTORS-NEXT: br label %[[CONTINUE_LABEL]]
+// DTOR3: [[CALL_DELETE_LABEL]]
+// DTOR3-NEXT: %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8*
+// DTOR3-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[THIS_AS_VOID]]) [[NUW:#[0-9]+]]
+// DTOR3-NEXT: br label %[[CONTINUE_LABEL]]
//
-// DTORS: [[CONTINUE_LABEL]]
-// DTORS-NEXT: ret void
+// DTOR3: [[CONTINUE_LABEL]]
+// DTOR3-NEXT: ret void
}
virtual void foo();
};
@@ -79,11 +96,11 @@
// CHECK: define void @"\01?call_complete_dtor@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
obj_ptr->~C();
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
-// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i1)***
-// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i1)*** %[[PVTABLE]]
-// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i1)** %[[VTABLE]], i64 0
-// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i1)** %[[PVDTOR]]
-// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i1 zeroext false)
+// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
+// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
+// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
+// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
+// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
// CHECK-NEXT: ret void
}
@@ -94,11 +111,11 @@
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
// CHECK: [[DELETE_NOTNULL]]
-// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i1)***
-// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i1)*** %[[PVTABLE]]
-// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i1)** %[[VTABLE]], i64 0
-// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i1)** %[[PVDTOR]]
-// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i1 zeroext true)
+// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
+// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
+// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
+// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
+// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
// CHECK: ret void
}
@@ -108,18 +125,18 @@
D() {
static int ctor_static = foo();
// CHECK that the static in the ctor gets mangled correctly:
- // CHECK: @"\01?ctor_static@?1???0D@basic@@QAE@XZ@4HA"
+ // CTOR1: @"\01?ctor_static@?1???0D@basic@@QAE@XZ@4HA"
}
~D() {
static int dtor_static = foo();
// CHECK that the static in the dtor gets mangled correctly:
- // CHECK: @"\01?dtor_static@?1???1D@basic@@QAE@XZ@4HA"
+ // DTOR4: @"\01?dtor_static@?1???1D@basic@@QAE@XZ@4HA"
}
};
void use_D() { D c; }
-// DTORS: attributes [[NUW]] = { nounwind{{.*}} }
+// DTOR3: attributes [[NUW]] = { nounwind{{.*}} }
} // end namespace basic
@@ -213,3 +230,43 @@
}
} // end namespace constructors
+
+namespace bodyless_dtors {
+
+struct A {
+ A();
+ virtual void f(); // TODO: remove this function once we disable the key
+ // function logic for the MS ABI.
+ virtual ~A();
+};
+
+A::A() {}
+void A::f() {}
+
+// The emission of the ctor requires emission of the vtable and therefore
+// of the other dtor forms, despite not being defined in this TU.
+
+// DTOR5: define linkonce_odr x86_thiscallcc void @"\01??_GA@bodyless_dtors@@UAEPAXI@Z"(%"struct.bodyless_dtors::A"* %this, i32 %flags)
+// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_DA@bodyless_dtors@@UAE@XZ"(%"struct.bodyless_dtors::A"* %this)
+
+}
+
+namespace virtual_bases {
+
+struct A {
+ ~A();
+};
+
+struct B : virtual A {
+ ~B();
+};
+
+// DTOR6: define linkonce_odr x86_thiscallcc void @"\01??_DB@virtual_bases@@QAE@XZ"
+// DTOR6: call x86_thiscallcc void @"\01??1B@virtual_bases@@QAE@XZ"
+// DTOR6: call x86_thiscallcc void @"\01??1A@virtual_bases@@QAE@XZ"
+
+void f(B *b) {
+ b->~B();
+}
+
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits