[WinEH] Push cleanupendpad scopes around exceptional cleanups

We were only doing this for SEH as a special case. Generalize it to all
cleanups.

llvm-svn: 249748
This commit is contained in:
Reid Kleckner 2015-10-08 21:14:56 +00:00
parent 5beec213e2
commit 5539152404
3 changed files with 61 additions and 47 deletions

View File

@ -521,15 +521,6 @@ static void EmitCleanup(CodeGenFunction &CGF,
EHScopeStack::Cleanup *Fn,
EHScopeStack::Cleanup::Flags flags,
Address ActiveFlag) {
// Itanium EH cleanups occur within a terminate scope. Microsoft SEH doesn't
// have this behavior, and the Microsoft C++ runtime will call terminate for
// us if the cleanup throws.
bool PushedTerminate = false;
if (flags.isForEHCleanup() && !CGF.getTarget().getCXXABI().isMicrosoft()) {
CGF.EHStack.pushTerminate();
PushedTerminate = true;
}
// If there's an active flag, load it and skip the cleanup if it's
// false.
llvm::BasicBlock *ContBB = nullptr;
@ -549,10 +540,6 @@ static void EmitCleanup(CodeGenFunction &CGF,
// Emit the continuation block if there was an active flag.
if (ActiveFlag.isValid())
CGF.EmitBlock(ContBB);
// Leave the terminate scope.
if (PushedTerminate)
CGF.EHStack.popTerminate();
}
static void ForwardPrebranchedFallthrough(llvm::BasicBlock *Exit,
@ -931,11 +918,29 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
EmitBlock(EHEntry);
// Push terminate scopes around the potentially throwing destructor calls.
// We don't emit these when using funclets, because the runtime does it for
// us as part of unwinding out of a cleanuppad.
bool PushedTerminate = false;
if (!EHPersonality::get(*this).usesFuncletPads()) {
EHStack.pushTerminate();
PushedTerminate = true;
}
llvm::CleanupPadInst *CPI = nullptr;
llvm::BasicBlock *CleanupEndBB = nullptr;
llvm::BasicBlock *NextAction = getEHDispatchBlock(EHParent);
if (EHPersonality::get(*this).usesFuncletPads())
if (EHPersonality::get(*this).usesFuncletPads()) {
CPI = Builder.CreateCleanupPad({});
// Build a cleanupendpad to unwind through. Our insertion point should be
// in the cleanuppad block.
CleanupEndBB = createBasicBlock("ehcleanup.end");
CGBuilderTy(*this, CleanupEndBB).CreateCleanupEndPad(CPI, NextAction);
EHStack.pushPadEnd(CleanupEndBB);
}
// We only actually emit the cleanup code if the cleanup is either
// active or was used before it was deactivated.
if (EHActiveFlag.isValid() || IsActive) {
@ -948,6 +953,21 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
else
Builder.CreateBr(NextAction);
// Insert the cleanupendpad block here, if it has any uses.
if (CleanupEndBB) {
EHStack.popPadEnd();
if (CleanupEndBB->hasNUsesOrMore(1)) {
CurFn->getBasicBlockList().insertAfter(Builder.GetInsertBlock(),
CleanupEndBB);
} else {
delete CleanupEndBB;
}
}
// Leave the terminate scope.
if (PushedTerminate)
EHStack.popTerminate();
Builder.restoreIP(SavedIP);
SimplifyCleanupEntry(*this, EHEntry);

View File

@ -1410,10 +1410,8 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
namespace {
struct PerformSEHFinally final : EHScopeStack::Cleanup {
llvm::Function *OutlinedFinally;
EHScopeStack::stable_iterator EnclosingScope;
PerformSEHFinally(llvm::Function *OutlinedFinally,
EHScopeStack::stable_iterator EnclosingScope)
: OutlinedFinally(OutlinedFinally), EnclosingScope(EnclosingScope) {}
PerformSEHFinally(llvm::Function *OutlinedFinally)
: OutlinedFinally(OutlinedFinally) {}
void Emit(CodeGenFunction &CGF, Flags F) override {
ASTContext &Context = CGF.getContext();
@ -1438,28 +1436,7 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup {
CGM.getTypes().arrangeFreeFunctionCall(Args, FPT,
/*chainCall=*/false);
// If this is the normal cleanup, just emit the call.
if (!F.isForEHCleanup()) {
CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args);
return;
}
// Build a cleanupendpad to unwind through.
llvm::BasicBlock *CleanupBB = CGF.Builder.GetInsertBlock();
llvm::BasicBlock *CleanupEndBB = CGF.createBasicBlock("ehcleanup.end");
llvm::Instruction *PadInst = CleanupBB->getFirstNonPHI();
auto *CPI = cast<llvm::CleanupPadInst>(PadInst);
CGBuilderTy(CGF, CleanupEndBB)
.CreateCleanupEndPad(CPI, CGF.getEHDispatchBlock(EnclosingScope));
// Push and pop the cleanupendpad around the call.
CGF.EHStack.pushPadEnd(CleanupEndBB);
CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args);
CGF.EHStack.popPadEnd();
// Insert the catchendpad block here.
CGF.CurFn->getBasicBlockList().insertAfter(CGF.Builder.GetInsertBlock(),
CleanupEndBB);
}
};
} // end anonymous namespace
@ -1815,8 +1792,7 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
HelperCGF.GenerateSEHFinallyFunction(*this, *Finally);
// Push a cleanup for __finally blocks.
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc,
EHStack.getInnermostEHScope());
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc);
return;
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -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 %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 %s
struct A {
A();
@ -54,14 +54,14 @@ int HasDeactivatedCleanups() {
//
// WIN32: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z"([[argmem_ty]]* inalloca %[[argmem]])
// Destroy the two const ref temporaries.
// WIN32: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: ret i32
//
// Conditionally destroy arg1.
// WIN32: %[[cond:.*]] = load i1, i1* %[[isactive]]
// WIN32: br i1 %[[cond]]
// WIN32: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg1]])
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %[[arg1]])
// WIN32: }
// Test putting the cleanups inside a conditional.
@ -118,14 +118,14 @@ int HasConditionalDeactivatedCleanups(bool cond) {
// False condition.
// WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"()
// Two normal cleanups for TakeRef args.
// WIN32: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: ret i32
//
// Somewhere in the landing pad soup, we conditionally destroy arg1.
// WIN32: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]]
// WIN32: br i1 %[[isactive]]
// WIN32: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"
// WIN32: }
namespace crash_on_partial_destroy {
@ -155,7 +155,7 @@ C::C() { foo(); }
// WIN32: getelementptr i8, i8* %{{.*}}, i32 4
// WIN32-NOT: load
// WIN32: bitcast i8* %{{.*}} to %"struct.crash_on_partial_destroy::B"*
// WIN32: invoke x86_thiscallcc void @"\01??1B@crash_on_partial_destroy@@UAE@XZ"
// WIN32: call x86_thiscallcc void @"\01??1B@crash_on_partial_destroy@@UAE@XZ"
//
// WIN32-NOT: load
// WIN32: bitcast %"struct.crash_on_partial_destroy::C"* %{{.*}} to i8*
@ -188,3 +188,21 @@ void f() {
// WIN32-NEXT: cleanuppad
// WIN32: call x86_thiscallcc void @"\01??1C@dont_call_terminate@@QAE@XZ"({{.*}})
}
namespace noexcept_false_dtor {
struct D {
~D() noexcept(false);
};
void f() {
D d;
CouldThrow();
}
}
// WIN32-LABEL: define void @"\01?f@noexcept_false_dtor@@YAXXZ"()
// WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"()
// WIN32: call x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}})
// WIN32: cleanuppad
// WIN32: invoke x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}})
// WIN32: cleanupret
// WIN32: cleanupendpad