Skip to content

Commit

Permalink
Emit @finally blocks completely lazily instead of forcing their
Browse files Browse the repository at this point in the history
existence by always threading an edge from the catchall.  Not doing
this was previously causing a crash in the very extreme case where
neither the normal cleanup nor the EH catchall was actually reachable:
we would delete the catchall entry block, which would cause us to
delete the entry block of the finally cleanup as well because the
cleanup logic would merge the blocks, which in turn triggered an assert
because later blocks in the finally would still be using values from the
entry.  Laziness turns out to be the most elegant solution to the problem.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133601 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rjmccall committed Jun 22, 2011
1 parent 4dba7b5 commit d768e9d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 72 deletions.
110 changes: 54 additions & 56 deletions lib/CodeGen/CGException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1298,28 +1298,29 @@ namespace {
/// Enters a finally block for an implementation using zero-cost
/// exceptions. This is mostly general, but hard-codes some
/// language/ABI-specific behavior in the catch-all sections.
CodeGenFunction::FinallyInfo
CodeGenFunction::EnterFinallyBlock(const Stmt *Body,
llvm::Constant *BeginCatchFn,
llvm::Constant *EndCatchFn,
llvm::Constant *RethrowFn) {
assert((BeginCatchFn != 0) == (EndCatchFn != 0) &&
void CodeGenFunction::FinallyInfo::enter(CodeGenFunction &CGF,
const Stmt *body,
llvm::Constant *beginCatchFn,
llvm::Constant *endCatchFn,
llvm::Constant *rethrowFn) {
assert((beginCatchFn != 0) == (endCatchFn != 0) &&
"begin/end catch functions not paired");
assert(RethrowFn && "rethrow function is required");
assert(rethrowFn && "rethrow function is required");

BeginCatchFn = beginCatchFn;

// The rethrow function has one of the following two types:
// void (*)()
// void (*)(void*)
// In the latter case we need to pass it the exception object.
// But we can't use the exception slot because the @finally might
// have a landing pad (which would overwrite the exception slot).
const llvm::FunctionType *RethrowFnTy =
const llvm::FunctionType *rethrowFnTy =
cast<llvm::FunctionType>(
cast<llvm::PointerType>(RethrowFn->getType())
->getElementType());
llvm::Value *SavedExnVar = 0;
if (RethrowFnTy->getNumParams())
SavedExnVar = CreateTempAlloca(Builder.getInt8PtrTy(), "finally.exn");
cast<llvm::PointerType>(rethrowFn->getType())->getElementType());
SavedExnVar = 0;
if (rethrowFnTy->getNumParams())
SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn");

// A finally block is a statement which must be executed on any edge
// out of a given scope. Unlike a cleanup, the finally block may
Expand All @@ -1333,67 +1334,64 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body,
// The finally block itself is generated in the context of a cleanup
// which conditionally leaves the catch-all.

FinallyInfo Info;

// Jump destination for performing the finally block on an exception
// edge. We'll never actually reach this block, so unreachable is
// fine.
JumpDest RethrowDest = getJumpDestInCurrentScope(getUnreachableBlock());
RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock());

// Whether the finally block is being executed for EH purposes.
llvm::AllocaInst *ForEHVar = CreateTempAlloca(Builder.getInt1Ty(),
"finally.for-eh");
InitTempAlloca(ForEHVar, llvm::ConstantInt::getFalse(getLLVMContext()));
ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh");
CGF.Builder.CreateStore(CGF.Builder.getFalse(), ForEHVar);

// Enter a normal cleanup which will perform the @finally block.
EHStack.pushCleanup<PerformFinally>(NormalCleanup, Body,
ForEHVar, EndCatchFn,
RethrowFn, SavedExnVar);
CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body,
ForEHVar, endCatchFn,
rethrowFn, SavedExnVar);

// Enter a catch-all scope.
llvm::BasicBlock *CatchAllBB = createBasicBlock("finally.catchall");
CGBuilderTy::InsertPoint SavedIP = Builder.saveIP();
Builder.SetInsertPoint(CatchAllBB);

// If there's a begin-catch function, call it.
if (BeginCatchFn) {
Builder.CreateCall(BeginCatchFn, Builder.CreateLoad(getExceptionSlot()))
->setDoesNotThrow();
}
llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall");
EHCatchScope *catchScope = CGF.EHStack.pushCatch(1);
catchScope->setCatchAllHandler(0, catchBB);
}

// If we need to remember the exception pointer to rethrow later, do so.
if (SavedExnVar) {
llvm::Value *SavedExn = Builder.CreateLoad(getExceptionSlot());
Builder.CreateStore(SavedExn, SavedExnVar);
}
void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) {
// Leave the finally catch-all.
EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin());
llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block;
CGF.EHStack.popCatch();

// Tell the finally block that we're in EH.
Builder.CreateStore(llvm::ConstantInt::getTrue(getLLVMContext()), ForEHVar);
// If there are any references to the catch-all block, emit it.
if (catchBB->use_empty()) {
delete catchBB;
} else {
CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP();
CGF.EmitBlock(catchBB);

// Thread a jump through the finally cleanup.
EmitBranchThroughCleanup(RethrowDest);
llvm::Value *exn = 0;

Builder.restoreIP(SavedIP);
// If there's a begin-catch function, call it.
if (BeginCatchFn) {
exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot());
CGF.Builder.CreateCall(BeginCatchFn, exn)->setDoesNotThrow();
}

EHCatchScope *CatchScope = EHStack.pushCatch(1);
CatchScope->setCatchAllHandler(0, CatchAllBB);
// If we need to remember the exception pointer to rethrow later, do so.
if (SavedExnVar) {
if (!exn) exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot());
CGF.Builder.CreateStore(exn, SavedExnVar);
}

return Info;
}
// Tell the cleanups in the finally block that we're do this for EH.
CGF.Builder.CreateStore(CGF.Builder.getTrue(), ForEHVar);

void CodeGenFunction::ExitFinallyBlock(FinallyInfo &Info) {
// Leave the finally catch-all.
EHCatchScope &Catch = cast<EHCatchScope>(*EHStack.begin());
llvm::BasicBlock *CatchAllBB = Catch.getHandler(0).Block;
EHStack.popCatch();
// Thread a jump through the finally cleanup.
CGF.EmitBranchThroughCleanup(RethrowDest);

// And leave the normal cleanup.
PopCleanupBlock();

CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
EmitBlock(CatchAllBB, true);
CGF.Builder.restoreIP(savedIP);
}

Builder.restoreIP(SavedIP);
// Finally, leave the @finally cleanup.
CGF.PopCleanupBlock();
}

llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() {
Expand Down
10 changes: 4 additions & 6 deletions lib/CodeGen/CGObjCRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,

CodeGenFunction::FinallyInfo FinallyInfo;
if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt())
FinallyInfo = CGF.EnterFinallyBlock(Finally->getFinallyBody(),
beginCatchFn,
endCatchFn,
exceptionRethrowFn);
FinallyInfo.enter(CGF, Finally->getFinallyBody(),
beginCatchFn, endCatchFn, exceptionRethrowFn);

llvm::SmallVector<CatchHandler, 8> Handlers;

Expand Down Expand Up @@ -266,9 +264,9 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
// Go back to the try-statement fallthrough.
CGF.Builder.restoreIP(SavedIP);

// Pop out of the normal cleanup on the finally.
// Pop out of the finally.
if (S.getFinallyStmt())
CGF.ExitFinallyBlock(FinallyInfo);
FinallyInfo.exit(CGF);

if (Cont.isValid())
CGF.EmitBlock(Cont.getBlock());
Expand Down
30 changes: 21 additions & 9 deletions lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,16 +635,28 @@ class CodeGenFunction : public CodeGenTypeCache {
/// rethrows.
llvm::SmallVector<llvm::Value*, 8> ObjCEHValueStack;

// A struct holding information about a finally block's IR
// generation. For now, doesn't actually hold anything.
struct FinallyInfo {
};
/// A class controlling the emission of a finally block.
class FinallyInfo {
/// Where the catchall's edge through the cleanup should go.
JumpDest RethrowDest;

/// A function to call to enter the catch.
llvm::Constant *BeginCatchFn;

/// An i1 variable indicating whether or not the @finally is
/// running for an exception.
llvm::AllocaInst *ForEHVar;

FinallyInfo EnterFinallyBlock(const Stmt *Stmt,
llvm::Constant *BeginCatchFn,
llvm::Constant *EndCatchFn,
llvm::Constant *RethrowFn);
void ExitFinallyBlock(FinallyInfo &FinallyInfo);
/// An i8* variable into which the exception pointer to rethrow
/// has been saved.
llvm::AllocaInst *SavedExnVar;

public:
void enter(CodeGenFunction &CGF, const Stmt *Finally,
llvm::Constant *beginCatchFn, llvm::Constant *endCatchFn,
llvm::Constant *rethrowFn);
void exit(CodeGenFunction &CGF);
};

/// pushFullExprCleanup - Push a cleanup to be run at the end of the
/// current full-expression. Safe against the possibility that
Expand Down
16 changes: 15 additions & 1 deletion test/CodeGenObjC/exceptions-nonfragile.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fexceptions -fobjc-exceptions -o - %s | FileCheck %s

// rdar://problem/8535238
// CHECK: declare void @objc_exception_rethrow()
Expand All @@ -15,3 +15,17 @@ void protos() {
void throwing() {
@throw(@"error!");
}

// rdar://problem/9431547
void die(void) __attribute__((nothrow, noreturn));
void test2(void) {
@try {
die();
} @finally {
extern void test2_helper(void);
test2_helper();
}

// CHECK: define void @test2()
// CHECK-NOT: call void @test2_helper()
}

0 comments on commit d768e9d

Please sign in to comment.