forked from OSchip/llvm-project
[Temporary, Lifetime] Add lifetime marks for temporaries
With all MaterializeTemporaryExprs coming with a ExprWithCleanups, it's easy to add correct lifetime.end marks into the right RunCleanupsScope. Differential Revision: http://reviews.llvm.org/D20499 llvm-svn: 274385
This commit is contained in:
parent
1bba89612b
commit
421119fd89
|
@ -188,6 +188,7 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
|
|||
bool IsNormalCleanup = Kind & NormalCleanup;
|
||||
bool IsEHCleanup = Kind & EHCleanup;
|
||||
bool IsActive = !(Kind & InactiveCleanup);
|
||||
bool IsLifetimeMarker = Kind & LifetimeMarker;
|
||||
EHCleanupScope *Scope =
|
||||
new (Buffer) EHCleanupScope(IsNormalCleanup,
|
||||
IsEHCleanup,
|
||||
|
@ -200,6 +201,8 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) {
|
|||
InnermostNormalCleanup = stable_begin();
|
||||
if (IsEHCleanup)
|
||||
InnermostEHScope = stable_begin();
|
||||
if (IsLifetimeMarker)
|
||||
Scope->setLifetimeMarker();
|
||||
|
||||
return Scope->getCleanupBuffer();
|
||||
}
|
||||
|
|
|
@ -529,19 +529,6 @@ namespace {
|
|||
CGF.EmitCall(FnInfo, CleanupFn, ReturnValueSlot(), Args);
|
||||
}
|
||||
};
|
||||
|
||||
/// A cleanup to call @llvm.lifetime.end.
|
||||
class CallLifetimeEnd final : public EHScopeStack::Cleanup {
|
||||
llvm::Value *Addr;
|
||||
llvm::Value *Size;
|
||||
public:
|
||||
CallLifetimeEnd(Address addr, llvm::Value *size)
|
||||
: Addr(addr.getPointer()), Size(size) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
CGF.EmitLifetimeEnd(Size, Addr);
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
/// EmitAutoVarWithLifetime - Does the setup required for an automatic
|
||||
|
@ -1406,13 +1393,10 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) {
|
|||
|
||||
// Make sure we call @llvm.lifetime.end. This needs to happen
|
||||
// *last*, so the cleanup needs to be pushed *first*.
|
||||
if (emission.useLifetimeMarkers()) {
|
||||
EHStack.pushCleanup<CallLifetimeEnd>(NormalAndEHCleanup,
|
||||
if (emission.useLifetimeMarkers())
|
||||
EHStack.pushCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
|
||||
emission.getAllocatedAddress(),
|
||||
emission.getSizeForLifetimeMarkers());
|
||||
EHCleanupScope &cleanup = cast<EHCleanupScope>(*EHStack.begin());
|
||||
cleanup.setLifetimeMarker();
|
||||
}
|
||||
|
||||
// Check the type for a cleanup.
|
||||
if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeGenFunction.h"
|
||||
#include "CGCXXABI.h"
|
||||
#include "CGCall.h"
|
||||
#include "CGCleanup.h"
|
||||
#include "CGDebugInfo.h"
|
||||
#include "CGObjCRuntime.h"
|
||||
#include "CGOpenMPRuntime.h"
|
||||
#include "CGRecordLayout.h"
|
||||
#include "CodeGenFunction.h"
|
||||
#include "CodeGenModule.h"
|
||||
#include "TargetInfo.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
@ -423,6 +424,23 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
|
|||
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
|
||||
}
|
||||
} else {
|
||||
switch (M->getStorageDuration()) {
|
||||
case SD_Automatic:
|
||||
case SD_FullExpression:
|
||||
if (auto *Size = EmitLifetimeStart(
|
||||
CGM.getDataLayout().getTypeAllocSize(Object.getElementType()),
|
||||
Object.getPointer())) {
|
||||
if (M->getStorageDuration() == SD_Automatic)
|
||||
pushCleanupAfterFullExpr<CallLifetimeEnd>(NormalEHLifetimeMarker,
|
||||
Object, Size);
|
||||
else
|
||||
pushFullExprCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker, Object,
|
||||
Size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
|
||||
}
|
||||
pushTemporaryCleanup(*this, M, E, Object);
|
||||
|
|
|
@ -302,6 +302,19 @@ public:
|
|||
|
||||
llvm::Instruction *CurrentFuncletPad = nullptr;
|
||||
|
||||
class CallLifetimeEnd final : public EHScopeStack::Cleanup {
|
||||
llvm::Value *Addr;
|
||||
llvm::Value *Size;
|
||||
|
||||
public:
|
||||
CallLifetimeEnd(Address addr, llvm::Value *size)
|
||||
: Addr(addr.getPointer()), Size(size) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
CGF.EmitLifetimeEnd(Size, Addr);
|
||||
}
|
||||
};
|
||||
|
||||
/// Header for data within LifetimeExtendedCleanupStack.
|
||||
struct LifetimeExtendedCleanupHeader {
|
||||
/// The size of the following cleanup object.
|
||||
|
|
|
@ -89,7 +89,10 @@ enum CleanupKind : unsigned {
|
|||
InactiveCleanup = 0x4,
|
||||
InactiveEHCleanup = EHCleanup | InactiveCleanup,
|
||||
InactiveNormalCleanup = NormalCleanup | InactiveCleanup,
|
||||
InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup
|
||||
InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup,
|
||||
|
||||
LifetimeMarker = 0x8,
|
||||
NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
|
||||
};
|
||||
|
||||
/// A stack of scopes which respond to exceptions, including cleanups
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: %clang_cc1 %s -fexceptions -fcxx-exceptions -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// lifetime.end should be invoked even if the destructor doesn't run due to an
|
||||
// exception thrown from previous ctor call.
|
||||
|
||||
struct A { A(); ~A(); };
|
||||
A Baz(const A&);
|
||||
|
||||
void Test1() {
|
||||
// CHECK-LABEL: @_Z5Test1v(
|
||||
// CHECK: getelementptr
|
||||
// CHECK-NEXT: call void @llvm.lifetime.start(i64 1, i8* [[TMP:[^ ]+]])
|
||||
// CHECK-NEXT: getelementptr
|
||||
// CHECK-NEXT: call void @llvm.lifetime.start(i64 1, i8* [[TMP1:[^ ]+]])
|
||||
|
||||
// Normal exit
|
||||
// CHECK: call void @llvm.lifetime.end(i64 1, i8* [[TMP1]])
|
||||
// CHECK-NEXT: call void @llvm.lifetime.end(i64 1, i8* [[TMP]])
|
||||
|
||||
// Exception exit
|
||||
// CHECK: call void @llvm.lifetime.end(i64 1, i8* [[TMP1]])
|
||||
// CHECK-NEXT: call void @llvm.lifetime.end(i64 1, i8* [[TMP]])
|
||||
Baz(Baz(A()));
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
// RUN: %clang_cc1 %s -std=c++11 -O1 -DWITH_DTOR -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-DTOR %s
|
||||
// RUN: %clang_cc1 %s -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-NO-DTOR %s
|
||||
|
||||
struct A {
|
||||
A();
|
||||
#ifdef WITH_DTOR
|
||||
~A();
|
||||
#endif
|
||||
char a[1024];
|
||||
operator bool() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void Foo(T &&);
|
||||
|
||||
template <typename T>
|
||||
void Bar(T &&);
|
||||
|
||||
template <typename T>
|
||||
T Baz();
|
||||
|
||||
void Test1() {
|
||||
// CHECK-DTOR-LABEL: Test1
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: }
|
||||
|
||||
// CHECK-NO-DTOR-LABEL: Test1
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
|
||||
// CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]])
|
||||
// CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-NO-DTOR: }
|
||||
{
|
||||
const A &a = A{};
|
||||
Foo(a);
|
||||
}
|
||||
{
|
||||
const A &a = A{};
|
||||
Foo(a);
|
||||
}
|
||||
}
|
||||
|
||||
void Test2() {
|
||||
// CHECK-DTOR-LABEL: Test2
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR2]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]])
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR1]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]])
|
||||
// CHECK-DTOR: }
|
||||
|
||||
// CHECK-NO-DTOR-LABEL: Test2
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]])
|
||||
// CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]])
|
||||
// CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]])
|
||||
// CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]])
|
||||
// CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]])
|
||||
// CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]])
|
||||
// CHECK-NO-DTOR: }
|
||||
const A &a = A{};
|
||||
Foo(a);
|
||||
const A &b = A{};
|
||||
Foo(b);
|
||||
}
|
||||
|
||||
void Test3() {
|
||||
// CHECK-DTOR-LABEL: Test3
|
||||
// CHECK-DTOR: entry:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start
|
||||
// CHECK-DTOR: if.then:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end
|
||||
// CHECK-DTOR: cleanup{{.*}}:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end
|
||||
// CHECK-DTOR: cleanup{{.*}}:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end
|
||||
// CHECK-DTOR: }
|
||||
const A &a = A{};
|
||||
if (const A &b = A(a)) {
|
||||
Foo(b);
|
||||
return;
|
||||
}
|
||||
Bar(a);
|
||||
}
|
||||
|
||||
void Test4() {
|
||||
// CHECK-DTOR-LABEL: Test4
|
||||
// CHECK-DTOR: entry:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start
|
||||
// CHECK-DTOR: for.cond.cleanup:
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end
|
||||
// CHECK-DTOR: for.body:
|
||||
// CHECK-DTOR: }
|
||||
for (const A &a = A{}; a;) {
|
||||
Foo(a);
|
||||
}
|
||||
}
|
||||
|
||||
int Test5() {
|
||||
// CHECK-DTOR-LABEL: Test5
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start
|
||||
// CHECK-DTOR: call i32 @_Z3BazIiET_v()
|
||||
// CHECK-DTOR: store
|
||||
// CHECK-DTOR: call void @_Z3FooIRKiEvOT_
|
||||
// CHECK-DTOR: load
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end
|
||||
// CHECK-DTOR: }
|
||||
const int &a = Baz<int>();
|
||||
Foo(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void Test6() {
|
||||
// CHECK-DTOR-LABEL: Test6
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call i32 @_Z3BazIiET_v()
|
||||
// CHECK-DTOR: store
|
||||
// CHECK-DTOR: call void @_Z3FooIiEvOT_
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call i32 @_Z3BazIiET_v()
|
||||
// CHECK-DTOR: store
|
||||
// CHECK-DTOR: call void @_Z3FooIiEvOT_
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: }
|
||||
Foo(Baz<int>());
|
||||
Foo(Baz<int>());
|
||||
}
|
||||
|
||||
void Test7() {
|
||||
// CHECK-DTOR-LABEL: Test7
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]])
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]])
|
||||
// CHECK-DTOR: call void @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]])
|
||||
// CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]])
|
||||
// CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]])
|
||||
// CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]])
|
||||
// CHECK-DTOR: }
|
||||
Foo(Baz<A>());
|
||||
Foo(Baz<A>());
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 %s
|
||||
// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-LIFETIME %s
|
||||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O0 %s
|
||||
// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O3 -check-prefix WIN32-LIFETIME %s
|
||||
|
||||
struct A {
|
||||
A();
|
||||
|
@ -95,40 +95,78 @@ int HasConditionalDeactivatedCleanups(bool cond) {
|
|||
return (cond ? TakesTwo((TakeRef(A()), A()), (TakeRef(A()), A())) : CouldThrow());
|
||||
}
|
||||
|
||||
// WIN32-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} {
|
||||
// WIN32: alloca i1
|
||||
// WIN32: %[[arg1_cond:.*]] = alloca i1
|
||||
// WIN32-O0-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} {
|
||||
// WIN32-O0: alloca i1
|
||||
// WIN32-O0: %[[arg1_cond:.*]] = alloca i1
|
||||
// Start all four cleanups as deactivated.
|
||||
// WIN32: store i1 false
|
||||
// WIN32: store i1 false
|
||||
// WIN32: store i1 false
|
||||
// WIN32: store i1 false
|
||||
// WIN32: br i1
|
||||
// WIN32-O0: store i1 false
|
||||
// WIN32-O0: store i1 false
|
||||
// WIN32-O0: store i1 false
|
||||
// WIN32-O0: store i1 false
|
||||
// WIN32-O0: br i1
|
||||
// True condition.
|
||||
// WIN32: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32: store i1 true
|
||||
// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32: store i1 true, i1* %[[arg1_cond]]
|
||||
// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32: store i1 true
|
||||
// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32: store i1 true
|
||||
// WIN32: store i1 false, i1* %[[arg1_cond]]
|
||||
// WIN32: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z"
|
||||
// WIN32-O0: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O0: store i1 true
|
||||
// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O0: store i1 true, i1* %[[arg1_cond]]
|
||||
// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O0: store i1 true
|
||||
// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O0: store i1 true
|
||||
// WIN32-O0: store i1 false, i1* %[[arg1_cond]]
|
||||
// WIN32-O0: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z"
|
||||
// False condition.
|
||||
// WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"()
|
||||
// WIN32-O0: invoke i32 @"\01?CouldThrow@@YAHXZ"()
|
||||
// Two normal cleanups for TakeRef args.
|
||||
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
|
||||
// WIN32: ret i32
|
||||
// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32-O0-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
|
||||
// WIN32-O0: ret i32
|
||||
//
|
||||
// Somewhere in the landing pad soup, we conditionally destroy arg1.
|
||||
// WIN32: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]]
|
||||
// WIN32: br i1 %[[isactive]]
|
||||
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32: }
|
||||
// WIN32-O0: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]]
|
||||
// WIN32-O0: br i1 %[[isactive]]
|
||||
// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32-O0: }
|
||||
|
||||
// WIN32-O3-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} {
|
||||
// WIN32-O3: alloca i1
|
||||
// WIN32-O3: alloca i1
|
||||
// WIN32-O3: %[[arg1_cond:.*]] = alloca i1
|
||||
// Start all four cleanups as deactivated.
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: store i1 false
|
||||
// WIN32-O3: br i1
|
||||
// True condition.
|
||||
// WIN32-O3: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O3: store i1 true
|
||||
// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O3: store i1 true, i1* %[[arg1_cond]]
|
||||
// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O3: store i1 true
|
||||
// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z"
|
||||
// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ"
|
||||
// WIN32-O3: store i1 true
|
||||
// WIN32-O3: store i1 false, i1* %[[arg1_cond]]
|
||||
// WIN32-O3: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z"
|
||||
// False condition.
|
||||
// WIN32-O3: invoke i32 @"\01?CouldThrow@@YAHXZ"()
|
||||
// Two normal cleanups for TakeRef args.
|
||||
// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32-O3-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
|
||||
// WIN32-O3: ret i32
|
||||
//
|
||||
// Somewhere in the landing pad soup, we conditionally destroy arg1.
|
||||
// WIN32-O3: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]]
|
||||
// WIN32-O3: br i1 %[[isactive]]
|
||||
// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}})
|
||||
// WIN32-O3: }
|
||||
|
||||
namespace crash_on_partial_destroy {
|
||||
struct A {
|
||||
|
|
Loading…
Reference in New Issue