forked from OSchip/llvm-project
[MS] Always use base dtors in place of complete/vbase dtors when possible
Summary: Previously we tried too hard to uphold the fiction that destructor variants work like they do on Itanium throughout the ABI-neutral parts of clang. This lead to MS C++ ABI incompatiblities and other bugs. Now, -mconstructor-aliases will no longer control this ABI detail, and clang -cc1's LLVM IR output will be this much closer to the clang driver's. Based on a patch by Zahira Ammarguellat: https://reviews.llvm.org/D39063 I've tried to move the logic that Zahira added into MicrosoftCXXABI.cpp. There is only one ABI-specific detail sticking out, and that is in CodeGenModule::getAddrOfCXXStructor, where we collapse complete dtors to base dtors in the MS ABI. This fixes PR32990. Reviewers: erichkeane, zahiraam, majnemer, rjmccall Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D44505 llvm-svn: 327732
This commit is contained in:
parent
5ba2fe3720
commit
ae9b070111
|
@ -242,6 +242,11 @@ llvm::Constant *CodeGenModule::getAddrOfCXXStructor(
|
|||
if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
|
||||
GD = GlobalDecl(CD, toCXXCtorType(Type));
|
||||
} else {
|
||||
// Always alias equivalent complete destructors to base destructors in the
|
||||
// MS ABI.
|
||||
if (getTarget().getCXXABI().isMicrosoft() &&
|
||||
Type == StructorType::Complete && MD->getParent()->getNumVBases() == 0)
|
||||
Type = StructorType::Base;
|
||||
GD = GlobalDecl(cast<CXXDestructorDecl>(MD), toCXXDtorType(Type));
|
||||
}
|
||||
|
||||
|
|
|
@ -287,6 +287,20 @@ CGCXXABI::EmitCtorCompleteObjectHandler(CodeGenFunction &CGF,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void CGCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const {
|
||||
// Assume the base C++ ABI has no special rules for destructor variants.
|
||||
CGM.setDLLImportDLLExport(GV, Dtor);
|
||||
}
|
||||
|
||||
llvm::GlobalValue::LinkageTypes CGCXXABI::getCXXDestructorLinkage(
|
||||
GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
|
||||
// Delegate back to CGM by default.
|
||||
return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
|
||||
/*isConstantVariable=*/false);
|
||||
}
|
||||
|
||||
bool CGCXXABI::NeedsVTTParameter(GlobalDecl GD) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -319,6 +319,14 @@ public:
|
|||
virtual bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const = 0;
|
||||
|
||||
virtual void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const;
|
||||
|
||||
virtual llvm::GlobalValue::LinkageTypes
|
||||
getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const;
|
||||
|
||||
/// Emit destructor variants required by this ABI.
|
||||
virtual void EmitCXXDestructors(const CXXDestructorDecl *D) = 0;
|
||||
|
||||
|
|
|
@ -786,12 +786,10 @@ void CodeGenModule::setDSOLocal(llvm::GlobalValue *GV) const {
|
|||
void CodeGenModule::setDLLImportDLLExport(llvm::GlobalValue *GV,
|
||||
GlobalDecl GD) const {
|
||||
const auto *D = dyn_cast<NamedDecl>(GD.getDecl());
|
||||
// C++ destructors have a few C++ ABI specific special cases.
|
||||
if (const auto *Dtor = dyn_cast_or_null<CXXDestructorDecl>(D)) {
|
||||
if (getCXXABI().useThunkForDtorVariant(Dtor, GD.getDtorType())) {
|
||||
// Don't dllexport/import destructor thunks.
|
||||
GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
|
||||
return;
|
||||
}
|
||||
getCXXABI().setCXXDestructorDLLStorage(GV, Dtor, GD.getDtorType());
|
||||
return;
|
||||
}
|
||||
setDLLImportDLLExport(GV, D);
|
||||
}
|
||||
|
@ -1074,14 +1072,8 @@ CodeGenModule::getFunctionLinkage(GlobalDecl GD) {
|
|||
|
||||
GVALinkage Linkage = getContext().GetGVALinkageForFunction(D);
|
||||
|
||||
if (isa<CXXDestructorDecl>(D) &&
|
||||
getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D),
|
||||
GD.getDtorType())) {
|
||||
// Destructor variants in the Microsoft C++ ABI are always internal or
|
||||
// linkonce_odr thunks emitted on an as-needed basis.
|
||||
return Linkage == GVA_Internal ? llvm::GlobalValue::InternalLinkage
|
||||
: llvm::GlobalValue::LinkOnceODRLinkage;
|
||||
}
|
||||
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(D))
|
||||
return getCXXABI().getCXXDestructorLinkage(Linkage, Dtor, GD.getDtorType());
|
||||
|
||||
if (isa<CXXConstructorDecl>(D) &&
|
||||
cast<CXXConstructorDecl>(D)->isInheritingConstructor() &&
|
||||
|
|
|
@ -216,6 +216,14 @@ public:
|
|||
return DT != Dtor_Base;
|
||||
}
|
||||
|
||||
void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const override;
|
||||
|
||||
llvm::GlobalValue::LinkageTypes
|
||||
getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const override;
|
||||
|
||||
void EmitCXXDestructors(const CXXDestructorDecl *D) override;
|
||||
|
||||
const CXXRecordDecl *
|
||||
|
@ -1310,6 +1318,52 @@ MicrosoftCXXABI::buildStructorSignature(const CXXMethodDecl *MD, StructorType T,
|
|||
return Added;
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DT) const {
|
||||
// Deleting destructor variants are never imported or exported. Give them the
|
||||
// default storage class.
|
||||
if (DT == Dtor_Deleting) {
|
||||
GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
|
||||
} else {
|
||||
const NamedDecl *ND = Dtor;
|
||||
CGM.setDLLImportDLLExport(GV, ND);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
|
||||
GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
|
||||
// Internal things are always internal, regardless of attributes. After this,
|
||||
// we know the thunk is externally visible.
|
||||
if (Linkage == GVA_Internal)
|
||||
return llvm::GlobalValue::InternalLinkage;
|
||||
|
||||
switch (DT) {
|
||||
case Dtor_Base:
|
||||
// The base destructor most closely tracks the user-declared constructor, so
|
||||
// we delegate back to the normal declarator case.
|
||||
return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
|
||||
/*isConstantVariable=*/false);
|
||||
case Dtor_Complete:
|
||||
// The complete destructor is like an inline function, but it may be
|
||||
// imported and therefore must be exported as well. This requires changing
|
||||
// the linkage if a DLL attribute is present.
|
||||
if (Dtor->hasAttr<DLLExportAttr>())
|
||||
return llvm::GlobalValue::WeakODRLinkage;
|
||||
if (Dtor->hasAttr<DLLImportAttr>())
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
return llvm::GlobalValue::LinkOnceODRLinkage;
|
||||
case Dtor_Deleting:
|
||||
// Deleting destructors are like inline functions. They have vague linkage
|
||||
// and are emitted everywhere they are used. They are internal if the class
|
||||
// is internal.
|
||||
return llvm::GlobalValue::LinkOnceODRLinkage;
|
||||
case Dtor_Comdat:
|
||||
llvm_unreachable("MS C++ ABI does not support comdat dtors");
|
||||
}
|
||||
llvm_unreachable("invalid dtor type");
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) {
|
||||
// The TU defining a dtor is only guaranteed to emit a base destructor. All
|
||||
// other destructor variants are delegating thunks.
|
||||
|
@ -1549,6 +1603,12 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
|
|||
const CXXDestructorDecl *DD,
|
||||
CXXDtorType Type, bool ForVirtualBase,
|
||||
bool Delegating, Address This) {
|
||||
// Use the base destructor variant in place of the complete destructor variant
|
||||
// if the class has no virtual bases. This effectively implements some of the
|
||||
// -mconstructor-aliases optimization, but as part of the MS C++ ABI.
|
||||
if (Type == Dtor_Complete && DD->getParent()->getNumVBases() == 0)
|
||||
Type = Dtor_Base;
|
||||
|
||||
CGCallee Callee = CGCallee::forDirect(
|
||||
CGM.getAddrOfCXXStructor(DD, getFromDtorType(Type)),
|
||||
DD);
|
||||
|
@ -3821,19 +3881,12 @@ static void emitCXXConstructor(CodeGenModule &CGM,
|
|||
|
||||
static void emitCXXDestructor(CodeGenModule &CGM, const CXXDestructorDecl *dtor,
|
||||
StructorType dtorType) {
|
||||
// The complete destructor is equivalent to the base destructor for
|
||||
// classes with no virtual bases, so try to emit it as an alias.
|
||||
if (!dtor->getParent()->getNumVBases() &&
|
||||
(dtorType == StructorType::Complete || dtorType == StructorType::Base)) {
|
||||
bool ProducedAlias = !CGM.TryEmitDefinitionAsAlias(
|
||||
GlobalDecl(dtor, Dtor_Complete), GlobalDecl(dtor, Dtor_Base));
|
||||
if (ProducedAlias) {
|
||||
if (dtorType == StructorType::Complete)
|
||||
return;
|
||||
if (dtor->isVirtual())
|
||||
CGM.getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete));
|
||||
}
|
||||
}
|
||||
// Emit the base destructor if the base and complete (vbase) destructors are
|
||||
// equivalent. This effectively implements -mconstructor-aliases as part of
|
||||
// the ABI.
|
||||
if (dtorType == StructorType::Complete &&
|
||||
dtor->getParent()->getNumVBases() == 0)
|
||||
dtorType = StructorType::Base;
|
||||
|
||||
// The base destructor is equivalent to the base destructor of its
|
||||
// base class if there is exactly one non-virtual base class with a
|
||||
|
|
|
@ -128,7 +128,7 @@ H::~H() { call_in_dtor(); }
|
|||
// CHECK-MSABI: call void @"\01??3F@@SAXPEAU0@Udestroying_delete_t@std@@_KW4align_val_t@2@@Z"({{.*}}, i64 48, i64 16)
|
||||
// CHECK-MSABI: br label %[[RETURN:.*]]
|
||||
//
|
||||
// CHECK-MSABI: call void @"\01??_DH@@QEAAXXZ"(
|
||||
// CHECK-MSABI: call void @"\01??1H@@UEAA@XZ"(
|
||||
// CHECK-MSABI: br label %[[RETURN]]
|
||||
//
|
||||
// CHECK-MSABI: }
|
||||
|
@ -155,7 +155,7 @@ I::~I() { call_in_dtor(); }
|
|||
// CHECK-MSABI: call void @"\01??3F@@SAXPEAU0@Udestroying_delete_t@std@@_KW4align_val_t@2@@Z"({{.*}}, i64 96, i64 32)
|
||||
// CHECK-MSABI: br label %[[RETURN:.*]]
|
||||
//
|
||||
// CHECK-MSABI: call void @"\01??_DI@@QEAAXXZ"(
|
||||
// CHECK-MSABI: call void @"\01??1I@@UEAA@XZ"(
|
||||
// CHECK-MSABI: br label %[[RETURN]]
|
||||
//
|
||||
// CHECK-MSABI: }
|
||||
|
|
|
@ -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@@QAEXXZ"(%struct.S* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
// CHECK: call x86_thiscallcc void @"\01??1S@@UAE@XZ"(%struct.S* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"\01??_GT@@UAEPAXI@Z"
|
||||
// CHECK: call x86_thiscallcc void @"\01??_DT@@QAEXXZ"(%struct.T* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
// CHECK: call x86_thiscallcc void @"\01??1T@@UAE@XZ"(%struct.T* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"\01??_GU@@UAEPAXI@Z"
|
||||
// CHECK: call x86_thiscallcc void @"\01??_DU@@QAEXXZ"(%struct.U* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
// CHECK: call x86_thiscallcc void @"\01??1U@@UAE@XZ"(%struct.U* %this1){{.*}}!dbg !{{[0-9]+}}
|
||||
|
|
|
@ -5,6 +5,6 @@ struct __declspec(dllexport) B { virtual ~B(); };
|
|||
struct __declspec(dllexport) C : A, B { virtual ~C(); };
|
||||
C::~C() {}
|
||||
|
||||
// CHECK: define dso_local dllexport void @"\01??1C@@UEAA@XZ"
|
||||
// This thunk should *not* be dllexport.
|
||||
// CHECK: define linkonce_odr dso_local i8* @"\01??_EC@@W7EAAPEAXI@Z"
|
||||
// CHECK: define dso_local dllexport void @"\01??1C@@UEAA@XZ"
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
// RUN: %clang_cc1 -mconstructor-aliases %s -triple x86_64-windows-msvc -fms-extensions -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// FIXME: We should really consider removing -mconstructor-aliases for MS C++
|
||||
// ABI. The risk of bugs introducing ABI incompatibility under
|
||||
// -mno-constructor-aliases is too high.
|
||||
|
||||
// PR32990
|
||||
|
||||
// Introduces the virtual destructor. We should use the base destructor
|
||||
|
@ -44,6 +40,6 @@ extern "C" void testit() {
|
|||
// CHECK: call void @"\01??1ImportOverrideVDtor@@UEAA@XZ"(%struct.ImportOverrideVDtor* %{{.*}})
|
||||
// CHECK: call void @"\01??1ImportIntroVDtor@@UEAA@XZ"(%struct.ImportIntroVDtor* %{{.*}})
|
||||
|
||||
// CHECK-LABEL: define linkonce_odr dso_local void @"\01??_DImportVBaseOverrideVDtor@@QEAAXXZ"
|
||||
// CHECK-LABEL: declare dllimport void @"\01??_DImportVBaseOverrideVDtor@@QEAAXXZ"
|
||||
// CHECK-LABEL: declare dllimport void @"\01??1ImportOverrideVDtor@@UEAA@XZ"
|
||||
// CHECK-LABEL: declare dllimport void @"\01??1ImportIntroVDtor@@UEAA@XZ"
|
||||
|
|
|
@ -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@@QAEXXZ"(
|
||||
// CHECK: call x86_thiscallcc void @"\01??1Cleanup@@QAE@XZ"(
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK: [[CLEANUP]]
|
||||
// CHECK: %[[CLEANUPPAD:.*]] = cleanuppad within none []
|
||||
// CHECK: call x86_thiscallcc void @"\01??_DCleanup@@QAEXXZ"(
|
||||
// CHECK: call x86_thiscallcc void @"\01??1Cleanup@@QAE@XZ"(
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD]] unwind to caller
|
||||
|
||||
|
||||
|
|
|
@ -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@@QAEXXZ"(
|
||||
// WIN32: call {{.*}} @"\01??1Q@@QAE@XZ"(
|
||||
|
||||
// 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@@QEAAXXZ"({{.*}}* %[[TMP]])
|
||||
// WIN64: call void @"\01??1Q@@QEAA@XZ"({{.*}}* %[[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@@QAEXXZ"(
|
||||
// WIN32: call {{.*}} @"\01??1Q@@QAE@XZ"(
|
||||
|
||||
// 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@@QEAAXXZ"({{.*}}* %[[TMP]])
|
||||
// WIN64: call void @"\01??1Q@@QEAA@XZ"({{.*}}* %[[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@@QAEXXZ"(
|
||||
// WIN32: call {{.*}} @"\01??1Q@@QAE@XZ"(
|
||||
|
||||
// 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@@QEAAXXZ"({{.*}}* %[[TMP]])
|
||||
// WIN64: call void @"\01??1Q@@QEAA@XZ"({{.*}}* %[[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@@QAEXXZ"(
|
||||
// WIN32: call {{.*}} @"\01??1Q@@QAE@XZ"(
|
||||
|
||||
// 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@@QEAAXXZ"({{.*}}* %[[TMP]])
|
||||
// WIN64: call void @"\01??1Q@@QEAA@XZ"({{.*}}* %[[TMP]])
|
||||
}
|
||||
|
||||
namespace inline_nonvirt {
|
||||
|
|
|
@ -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@@QAEXXZ"(
|
||||
// CHECK: call x86_thiscallcc void @"\01??1Cleanup@PR25606@@QAE@XZ"(
|
||||
// CHECK: ret %"struct.PR25606::S3"* [[BC]]
|
||||
}
|
||||
|
|
|
@ -75,15 +75,15 @@ void constructors() {
|
|||
// CHECK: %{{[.0-9A-Z_a-z]+}} = call x86_thiscallcc %class.Base* @"\01??0Base@@QAE@XZ"
|
||||
// CHECK: ret
|
||||
|
||||
// Make sure that the Base constructor definition uses the right CC:
|
||||
// CHECK: define linkonce_odr dso_local x86_thiscallcc %class.Base* @"\01??0Base@@QAE@XZ"
|
||||
|
||||
// Make sure that the Base destructor call in the Child denstructor uses
|
||||
// the right calling convention:
|
||||
// CHECK: define linkonce_odr dso_local x86_thiscallcc void @"\01??1Child@@QAE@XZ"
|
||||
// CHECK: call x86_thiscallcc void @"\01??1Base@@QAE@XZ"
|
||||
// CHECK: ret
|
||||
|
||||
// Make sure that the Base constructor definition uses the right CC:
|
||||
// CHECK: define linkonce_odr dso_local x86_thiscallcc %class.Base* @"\01??0Base@@QAE@XZ"
|
||||
|
||||
// Make sure that the Base destructor definition uses the right CC:
|
||||
// CHECK: define linkonce_odr dso_local x86_thiscallcc void @"\01??1Base@@QAE@XZ"
|
||||
}
|
||||
|
|
|
@ -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 dso_local void @"\01??_Dtest_class@@QEAAXXZ"
|
||||
// CHECK-WIN32-DAG: define linkonce_odr dso_local x86_thiscallcc void @"\01??_Dtest_class@@QAEXXZ"
|
||||
// CHECK-WIN64-DAG: define linkonce_odr dso_local void @"\01??1test_class@@QEAA@XZ"
|
||||
// CHECK-WIN32-DAG: define linkonce_odr dso_local x86_thiscallcc void @"\01??1test_class@@QAE@XZ"
|
||||
|
||||
test_class& __regcall operator+=(const test_class&){
|
||||
return *this;
|
||||
|
|
|
@ -50,7 +50,7 @@ coro_t f() {
|
|||
// CHECK: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]]
|
||||
// CHECK: [[EHCLEANUP]]:
|
||||
// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
|
||||
// CHECK: call void @"\01??_DCleanup@@QEAAXXZ"(
|
||||
// CHECK: call void @"\01??1Cleanup@@QEAA@XZ"(
|
||||
// CHECK: cleanupret from %{{.+}} unwind label %[[CATCHDISPATCH:.+]]
|
||||
|
||||
// CHECK: [[CATCHDISPATCH]]:
|
||||
|
|
|
@ -44,4 +44,4 @@ coro_t f() {
|
|||
// CHECK: br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]]
|
||||
|
||||
// CHECK: [[DTOR]]:
|
||||
// CHECK: call void @"\01??_Dcoro_t@@QEAAXXZ"(
|
||||
// CHECK: call void @"\01??1coro_t@@QEAA@XZ"(
|
||||
|
|
|
@ -37,7 +37,7 @@ coro_t f() {
|
|||
// CHECK: to label %{{.+}} unwind label %[[EHCLEANUP:.+]]
|
||||
// CHECK: [[EHCLEANUP]]:
|
||||
// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
|
||||
// CHECK: call void @"\01??_DCleanup@@QEAAXXZ"(
|
||||
// CHECK: call void @"\01??1Cleanup@@QEAA@XZ"(
|
||||
// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[CATCHSW:.+]]
|
||||
// CHECK: [[CATCHSW]]:
|
||||
// CHECK: %[[CATCHSWTOK:.+]] = catchswitch within none [label %[[CATCH:.+]]] unwind label
|
||||
|
|
Loading…
Reference in New Issue