[cfe-commits] r56827 - in /cfe/trunk/lib/CodeGen: CGObjCMac.cpp CGStmt.cpp CodeGenFunction.h
Daniel Dunbar
daniel at zuster.org
Mon Sep 29 18:06:04 PDT 2008
Author: ddunbar
Date: Mon Sep 29 20:06:03 2008
New Revision: 56827
URL: http://llvm.org/viewvc/llvm-project?rev=56827&view=rev
Log:
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.
Modified:
cfe/trunk/lib/CodeGen/CGObjCMac.cpp
cfe/trunk/lib/CodeGen/CGStmt.cpp
cfe/trunk/lib/CodeGen/CodeGenFunction.h
Modified: cfe/trunk/lib/CodeGen/CGObjCMac.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjCMac.cpp?rev=56827&r1=56826&r2=56827&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjCMac.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjCMac.cpp Mon Sep 29 20:06:03 2008
@@ -1406,12 +1406,14 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
CGF.EmitStmt(CatchStmt->getCatchBody());
- CGF.Builder.CreateBr(FinallyBlock);
+ CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
break;
}
@@ -1579,7 +1600,7 @@
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 @@
// 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 @@
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 @@
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 @@
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
Modified: cfe/trunk/lib/CodeGen/CGStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGStmt.cpp?rev=56827&r1=56826&r2=56827&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGStmt.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGStmt.cpp Mon Sep 29 20:06:03 2008
@@ -437,6 +437,15 @@
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
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=56827&r1=56826&r2=56827&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Sep 29 20:06:03 2008
@@ -31,6 +31,7 @@
namespace llvm {
class BasicBlock;
class Module;
+ class SwitchInst;
}
namespace clang {
@@ -88,13 +89,42 @@
// 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;
+ ObjCEHEntry(llvm::BasicBlock *fb, llvm::BasicBlock *fne,
+ llvm::SwitchInst *fs, llvm::Value *dc)
+ : FinallyBlock(fb), FinallyNoExit(fne), FinallySwitch(fs),
+ DestCode(dc), Exception(0) {}
+
+ /// 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
More information about the cfe-commits
mailing list