Add support for implicit rethrows in @catch blocks.

Comment exception-handling code generation strategy.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56763 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Daniel Dunbar 2008-09-28 01:03:14 +00:00
Родитель ad12b6d643
Коммит 18ccc7776a
2 изменённых файлов: 102 добавлений и 10 удалений

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

@ -1383,6 +1383,75 @@ llvm::Function *CGObjCMac::EnumerationMutationFunction()
return ObjCTypes.EnumerationMutationFn;
}
/*
Objective-C setjmp-longjmp (sjlj) Exception Handling
--
The basic framework for a @try-catch-finally is as follows:
{
objc_exception_data d;
id _rethrow = null;
objc_exception_try_enter(&d);
if (!setjmp(d.jmp_buf)) {
... try body ...
} else {
// exception path
id _caught = objc_exception_extract(&d);
// enter new try scope for handlers
if (!setjmp(d.jmp_buf)) {
... match exception and execute catch blocks ...
// fell off end, rethrow.
_rethrow = _caught;
} else {
// exception in catch block
_rethrow = objc_exception_extract(&d);
goto finally_no_exit;
}
}
finally:
// match either the initial try_enter or the catch try_enter,
// depending on the path followed.
objc_exception_try_exit(&d);
finally_no_exit:
... finally block ....
if (_rethrow)
objc_exception_throw(_rethrow);
}
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.
We specialize this framework for a few particular circumstances:
- If there are no catch blocks, then we avoid emitting the second
exception handling context.
- If there is a catch-all catch block (i.e. @catch(...) or @catch(id
e)) we avoid emitting the code to rethrow an uncaught exception.
- FIXME: If there is no @finally block we can do a few more
simplifications.
Rethrows and Jumps-Through-Finally
--
Support for implicit rethrows and jumping through the finally block is
handled by storing the current exception-handling context in
ObjCEHStack.
*/
void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtTryStmt &S)
{
@ -1413,6 +1482,10 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
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());
@ -1426,6 +1499,7 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
ExceptionData,
"caught");
CGF.ObjCEHStack.back().Exception = Caught;
if (const ObjCAtCatchStmt* CatchStmt = S.getCatchStmts()) {
// Enter a new exception try block (in case a @catch block throws
// an exception).
@ -1497,14 +1571,12 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
// Emit the @catch block.
CGF.EmitBlock(MatchedBlock);
if (CatchParam) {
CGF.EmitStmt(CatchParam);
llvm::Value *Tmp =
CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(VD->getType()),
"tmp");
CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
}
CGF.EmitStmt(CatchParam);
llvm::Value *Tmp =
CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(VD->getType()),
"tmp");
CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
CGF.EmitStmt(CatchStmt->getCatchBody());
CGF.Builder.CreateBr(FinallyBlock);
@ -1551,6 +1623,8 @@ void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, Rethrow);
CGF.Builder.CreateUnreachable();
CGF.ObjCEHStack.pop_back();
CGF.EmitBlock(FinallyEndBlock);
}
@ -1564,7 +1638,9 @@ void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
ExceptionAsObject =
CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp");
} else {
assert(0 && "FIXME: rethrows not supported!");
assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back().Exception) &&
"Unexpected rethrow outside @catch block.");
ExceptionAsObject = CGF.ObjCEHStack.back().Exception;
}
CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, ExceptionAsObject);

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

@ -79,6 +79,22 @@ public:
const llvm::Type *LLVMIntTy;
uint32_t LLVMPointerWidth;
public:
// FIXME: The following should be private once EH code is moved out
// of NeXT runtime.
// ObjCEHStack - This keeps track of which object to rethrow from
// 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) {}
llvm::Value *Exception;
llvm::BasicBlock *FinallyBlock;
};
llvm::SmallVector<ObjCEHEntry, 8> ObjCEHStack;
private:
/// LabelIDs - Track arbitrary ids assigned to labels for use in
@ -108,7 +124,7 @@ private:
llvm::BasicBlock *ContinueBlock;
};
llvm::SmallVector<BreakContinue, 8> BreakContinueStack;
/// SwitchInsn - This is nearest current switch instruction. It is null if
/// if current context is not in a switch.
llvm::SwitchInst *SwitchInsn;