зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
8b30c41219
Коммит
898d508d4c
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче