]> granicus.if.org Git - clang/commitdiff
Emit @finally blocks completely lazily instead of forcing their
authorJohn McCall <rjmccall@apple.com>
Wed, 22 Jun 2011 02:32:12 +0000 (02:32 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 22 Jun 2011 02:32:12 +0000 (02:32 +0000)
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

lib/CodeGen/CGException.cpp
lib/CodeGen/CGObjCRuntime.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenObjC/exceptions-nonfragile.m

index ce57d02bcd3d8f6aaf69996c3dea4d56cbd331ce..1a4a5f988a5818527a4ab45d111f452c72305ca6 100644 (file)
@@ -1298,14 +1298,16 @@ 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 (*)()
@@ -1313,13 +1315,12 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body,
   // 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
@@ -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() {
index a2c3ea591e400b4160818349ee4ccd25e274830b..6ce1cb943533f49f2057a2b42b009ddf25cf3ed5 100644 (file)
@@ -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;
 
@@ -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());
index e72d6ced921ebea08712004a0fc6ea342f2d3a9b..1aaa700f7e04f2d39a715eda73fe6ba70d6547d8 100644 (file)
@@ -635,16 +635,28 @@ public:
   /// 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
index 280b5d416ffac98e74f49ac9cbc6b62f24a45af8..2557aab252190ada844973eb6169554e1dc7f436 100644 (file)
@@ -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()
@@ -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()
+}