Add infrastructure for proper @finally support.

- Provides a basic primitive to jump to an arbitrary basic block,
   through the finally code.

 - Only used for return statements and rethrow currently. Still need
   to handle break, continue and goto.

 - Code still needs to be shuffled around to live elsewhere.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56827 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Daniel Dunbar 2008-09-30 01:06:03 +00:00
Родитель 8b30c41219
Коммит 898d508d4c
3 изменённых файлов: 145 добавлений и 60 удалений

Просмотреть файл

@ -1406,12 +1406,14 @@ The basic framework for a @try-catch-finally is as follows:
// fell off end, rethrow.
_rethrow = _caught;
... jump-through-finally to finally_rethrow ...
} else {
// exception in catch block
_rethrow = objc_exception_extract(&d);
goto finally_no_exit;
... jump-through-finally_no_exit to finally_rethrow ...
}
}
... jump-through-finally to finally_end ...
finally:
// match either the initial try_enter or the catch try_enter,
@ -1419,18 +1421,18 @@ finally:
objc_exception_try_exit(&d);
finally_no_exit:
... finally block ....
if (_rethrow)
objc_exception_throw(_rethrow);
... dispatch to finally destination ...
finally_rethrow:
objc_exception_throw(_rethrow);
finally_end:
}
This framework differs slightly from the one gcc uses, in that gcc
uses _rethrow to determine if objc_exception_try_exit should be
called. This breaks in the face of throwing nil and introduces an
unnecessary branch. Note that our framework still does not properly
handle throwing nil, as a nil object will not be rethrown.
FIXME: Determine if _rethrow should be integrated into the other
architecture for selecting paths out of the finally block.
uses _rethrow to determine if objc_exception_try_exit should be called
and if the object should be rethrown. This breaks in the face of
throwing nil and introduces unnecessary branches.
We specialize this framework for a few particular circumstances:
@ -1450,19 +1452,47 @@ Support for implicit rethrows and jumping through the finally block is
handled by storing the current exception-handling context in
ObjCEHStack.
In order to implement proper @finally semantics, we support one basic
mechanism for jumping through the finally block to an arbitrary
destination. Constructs which generate exits from a @try or @catch
block use this mechanism to implement the proper semantics by chaining
jumps, as necessary.
This mechanism works like the one used for indirect goto: we
arbitrarily assign an ID to each destination and store the ID for the
destination in a variable prior to entering the finally block. At the
end of the finally block we simply create a switch to the proper
destination.
*/
void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtTryStmt &S)
{
// Allocate exception data.
const ObjCAtTryStmt &S) {
// Create various blocks we refer to for handling @finally.
llvm::BasicBlock *FinallyBlock = llvm::BasicBlock::Create("finally");
llvm::BasicBlock *FinallyNoExit = llvm::BasicBlock::Create("finally.noexit");
llvm::BasicBlock *FinallyRethrow = llvm::BasicBlock::Create("finally.throw");
llvm::BasicBlock *FinallyEnd = llvm::BasicBlock::Create("finally.end");
llvm::Value *DestCode =
CGF.CreateTempAlloca(llvm::Type::Int32Ty, "finally.dst");
// Generate jump code. Done here so we can directly add things to
// the switch instruction.
llvm::BasicBlock *FinallyJump = llvm::BasicBlock::Create("finally.jump");
llvm::SwitchInst *FinallySwitch =
llvm::SwitchInst::Create(new llvm::LoadInst(DestCode, "", FinallyJump),
FinallyEnd, 10, FinallyJump);
// Push an EH context entry, used for handling rethrows and jumps
// through finally.
CodeGenFunction::ObjCEHEntry EHEntry(FinallyBlock, FinallyNoExit,
FinallySwitch, DestCode);
CGF.ObjCEHStack.push_back(&EHEntry);
// Allocate memory for the exception data and rethrow pointer.
llvm::Value *ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy,
"exceptiondata.ptr");
// Allocate memory for the rethrow pointer.
llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateStore(llvm::Constant::getNullValue(ObjCTypes.ObjectPtrTy),
RethrowPtr);
llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy, "_rethrow");
// Enter a new try block and call setjmp.
CGF.Builder.CreateCall(ObjCTypes.ExceptionTryEnterFn, ExceptionData);
@ -1471,25 +1501,16 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
JmpBufPtr = CGF.Builder.CreateStructGEP(JmpBufPtr, 0, "tmp");
llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.SetJmpFn,
JmpBufPtr, "result");
llvm::BasicBlock *FinallyBlock = llvm::BasicBlock::Create("finally");
llvm::BasicBlock *FinallyNoExit = llvm::BasicBlock::Create("finally.noexit");
llvm::BasicBlock *TryBlock = llvm::BasicBlock::Create("try");
llvm::BasicBlock *TryHandler = llvm::BasicBlock::Create("try.handler");
CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNonNull(SetJmpResult, "threw"),
TryHandler, TryBlock);
// Push an EH context entry for use by rethrow and
// jumps-through-finally.
CGF.ObjCEHStack.push_back(CodeGenFunction::ObjCEHEntry(FinallyBlock));
// Emit the @try block.
CGF.EmitBlock(TryBlock);
CGF.EmitStmt(S.getTryBody());
CGF.Builder.CreateBr(FinallyBlock);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
// Emit the "exception in @try" block.
CGF.EmitBlock(TryHandler);
@ -1499,7 +1520,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
ExceptionData,
"caught");
CGF.ObjCEHStack.back().Exception = Caught;
EHEntry.Exception = Caught;
if (const ObjCAtCatchStmt* CatchStmt = S.getCatchStmts()) {
// Enter a new exception try block (in case a @catch block throws
// an exception).
@ -1520,7 +1541,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
// so.
bool AllMatched = false;
for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) {
llvm::BasicBlock *NextCatchBlock = llvm::BasicBlock::Create("nextcatch");
llvm::BasicBlock *NextCatchBlock = llvm::BasicBlock::Create("catch");
const DeclStmt *CatchParam =
cast_or_null<DeclStmt>(CatchStmt->getCatchParamStmt());
@ -1549,7 +1570,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
}
CGF.EmitStmt(CatchStmt->getCatchBody());
CGF.Builder.CreateBr(FinallyBlock);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
break;
}
@ -1579,7 +1600,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
CGF.EmitStmt(CatchStmt->getCatchBody());
CGF.Builder.CreateBr(FinallyBlock);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
CGF.EmitBlock(NextCatchBlock);
}
@ -1588,7 +1609,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
// None of the handlers caught the exception, so store it to be
// rethrown at the end of the @finally block.
CGF.Builder.CreateStore(Caught, RethrowPtr);
CGF.Builder.CreateBr(FinallyBlock);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow);
}
// Emit the exception handler for the @catch blocks.
@ -1596,41 +1617,37 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
CGF.Builder.CreateStore(CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
ExceptionData),
RethrowPtr);
CGF.Builder.CreateBr(FinallyNoExit);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow, false);
} else {
CGF.Builder.CreateStore(Caught, RethrowPtr);
CGF.Builder.CreateBr(FinallyNoExit);
CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow, false);
}
// Pop the exception-handling stack entry. It is important to do
// this now, because the code in the @finally block is not in this
// context.
CGF.ObjCEHStack.pop_back();
// Emit the @finally block.
CGF.EmitBlock(FinallyBlock);
CGF.Builder.CreateCall(ObjCTypes.ExceptionTryExitFn, ExceptionData);
CGF.EmitBlock(FinallyNoExit);
if (const ObjCAtFinallyStmt* FinallyStmt = S.getFinallyStmt())
CGF.EmitStmt(FinallyStmt->getFinallyBody());
llvm::Value *Rethrow = CGF.Builder.CreateLoad(RethrowPtr);
llvm::BasicBlock *RethrowBlock = llvm::BasicBlock::Create("rethrow");
llvm::BasicBlock *FinallyEndBlock = llvm::BasicBlock::Create("finally.end");
// If necessary, rethrow the exception.
CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNonNull(Rethrow, "rethrow.test"),
RethrowBlock, FinallyEndBlock);
CGF.EmitBlock(RethrowBlock);
CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, Rethrow);
CGF.EmitBlock(FinallyJump);
CGF.EmitBlock(FinallyRethrow);
CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn,
CGF.Builder.CreateLoad(RethrowPtr));
CGF.Builder.CreateUnreachable();
CGF.ObjCEHStack.pop_back();
CGF.EmitBlock(FinallyEndBlock);
CGF.EmitBlock(FinallyEnd);
}
void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S)
{
const ObjCAtThrowStmt &S) {
llvm::Value *ExceptionAsObject;
if (const Expr *ThrowExpr = S.getThrowExpr()) {
@ -1638,9 +1655,9 @@ void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
ExceptionAsObject =
CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp");
} else {
assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back().Exception) &&
assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back()->Exception) &&
"Unexpected rethrow outside @catch block.");
ExceptionAsObject = CGF.ObjCEHStack.back().Exception;
ExceptionAsObject = CGF.ObjCEHStack.back()->Exception;
}
CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, ExceptionAsObject);
@ -1648,6 +1665,35 @@ void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
CGF.EmitBlock(llvm::BasicBlock::Create("bb"));
}
void CodeGenFunction::EmitJumpThroughFinally(ObjCEHEntry *E,
llvm::BasicBlock *Dst,
bool ExecuteTryExit) {
llvm::BasicBlock *Src = Builder.GetInsertBlock();
if (isDummyBlock(Src))
return;
// Find the destination code for this block. We always use 0 for the
// fallthrough block (default destination).
llvm::SwitchInst *SI = E->FinallySwitch;
llvm::ConstantInt *ID;
if (Dst == SI->getDefaultDest()) {
ID = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0);
} else {
ID = SI->findCaseDest(Dst);
if (!ID) {
// No code found, get a new unique one by just using the number
// of switch successors.
ID = llvm::ConstantInt::get(llvm::Type::Int32Ty, SI->getNumSuccessors());
SI->addCase(ID, Dst);
}
}
// Set the destination code and branch.
Builder.CreateStore(ID, E->DestCode);
Builder.CreateBr(ExecuteTryExit ? E->FinallyBlock : E->FinallyNoExit);
}
/* *** Private Interface *** */
/// EmitImageInfo - Emit the image info marker used to encode some module

Просмотреть файл

@ -437,6 +437,15 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
EmitAggExpr(RV, ReturnValue, false);
}
if (!ObjCEHStack.empty()) {
for (ObjCEHStackType::reverse_iterator i = ObjCEHStack.rbegin(),
e = ObjCEHStack.rend(); i != e; ++i) {
llvm::BasicBlock *ReturnPad = llvm::BasicBlock::Create("return.pad");
EmitJumpThroughFinally(*i, ReturnPad);
EmitBlock(ReturnPad);
}
}
Builder.CreateBr(ReturnBlock);
// Emit a block after the branch so that dead code after a return has some

Просмотреть файл

@ -31,6 +31,7 @@
namespace llvm {
class BasicBlock;
class Module;
class SwitchInst;
}
namespace clang {
@ -88,13 +89,42 @@ public:
// inside @catch blocks and which @finally block exits from an EH
// scope should be chained through.
struct ObjCEHEntry {
ObjCEHEntry(llvm::BasicBlock *fb)
: Exception(0), FinallyBlock(fb) {}
ObjCEHEntry(llvm::BasicBlock *fb, llvm::BasicBlock *fne,
llvm::SwitchInst *fs, llvm::Value *dc)
: FinallyBlock(fb), FinallyNoExit(fne), FinallySwitch(fs),
DestCode(dc), Exception(0) {}
llvm::Value *Exception;
llvm::BasicBlock *FinallyBlock;
/// Entry point to the finally block.
llvm::BasicBlock *FinallyBlock;
/// Entry point to the finally block which skips execution of the
/// try_exit runtime function.
llvm::BasicBlock *FinallyNoExit;
/// Switch instruction which runs at the end of the finally block
/// to forward jumps through the finally block.
llvm::SwitchInst *FinallySwitch;
/// Variable holding the code for the destination of a jump
/// through the @finally block.
llvm::Value *DestCode;
/// The exception object being handled, during IR generation for a
/// @catch block.
llvm::Value *Exception;
};
llvm::SmallVector<ObjCEHEntry, 8> ObjCEHStack;
typedef llvm::SmallVector<ObjCEHEntry*, 8> ObjCEHStackType;
ObjCEHStackType ObjCEHStack;
/// EmitJumpThroughFinally - Emit a branch from the current insert
/// point through the finally handling code for \arg Entry and then
/// on to \arg Dest.
///
/// \param ExecuteTryExit - When true, the try_exit runtime function
/// should be called prior to executing the finally code.
void EmitJumpThroughFinally(ObjCEHEntry *Entry, llvm::BasicBlock *Dest,
bool ExecuteTryExit=true);
private:
/// LabelIDs - Track arbitrary ids assigned to labels for use in