[llvm-commits] [129634] llvm-gcc: call terminate if an exception handling cleanup throws an exception

clattner at apple.com clattner at apple.com
Tue Jul 17 08:37:31 PDT 2007


Revision: 129634
Author:   clattner
Date:     2007-07-17 08:37:30 -0700 (Tue, 17 Jul 2007)

Log Message:
-----------
llvm-gcc: call terminate if an exception handling cleanup throws an exception

In gcc, if a language defines lang_protect_cleanup_actions then any exceptions thrown
by an exception handling cleanup ("destructor") are supposed to be routed to a call to
lang_protect_cleanup_actions.  Implement this like in gcc: wrap cleanups in a filter
that has as handler the code specified by lang_protect_cleanup_actions.  In C++ this
means a call to "terminate".  What happens if the handler itself throws an exception?
(This can happen in C++ because the standard terminate can be replaced by user defined
code.)  The runtime unwinder is supposed to deal with it, but in order for this to work
special support is required in the dwarf writer (plus some kind of annotation in the IR),
much the same as for nothrow functions.  Since we don't yet support nothrow functions I
didn't try to add any support for this a priori more complicated case.


Patch by Duncan Sands

Modified Paths:
--------------
    apple-local/branches/llvm/gcc/llvm-convert.cpp
    apple-local/branches/llvm/gcc/llvm-internal.h

Modified: apple-local/branches/llvm/gcc/llvm-convert.cpp
===================================================================
--- apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-07-17 08:37:53 UTC (rev 129633)
+++ apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-07-17 15:37:30 UTC (rev 129634)
@@ -348,6 +348,7 @@
 
   AllocaInsertionPoint = 0;
   
+  CleanupFilter = NULL_TREE;
   ExceptionValue = 0;
   ExceptionSelectorValue = 0;
   FuncEHException = 0;
@@ -1909,7 +1910,7 @@
 void TreeToLLVM::dumpEHScopes() const {
   std::cerr << CurrentEHScopes.size() << " EH Scopes:\n";
   for (unsigned i = 0, e = CurrentEHScopes.size(); i != e; ++i) {
-    std::cerr << "  " << i << ". tree=" << (void*)CurrentEHScopes[i].TryExpr
+    std::cerr << "  " << i << ". catch=" << (void*)CurrentEHScopes[i].CatchExpr
               << " #blocks=" << CurrentEHScopes[i].Blocks.size()
               << " #fixups=" << CurrentEHScopes[i].BranchFixups.size() << "\n";
     for (unsigned f = 0, e = CurrentEHScopes[i].BranchFixups.size(); f != e;++f)
@@ -2018,14 +2019,13 @@
 
   for (std::vector<EHScope>::reverse_iterator I = CurrentEHScopes.rbegin(),
        E = CurrentEHScopes.rend(); I != E; ++I) {
-    if (TREE_CODE(I->TryExpr) == TRY_CATCH_EXPR) {
+    if (I->CatchExpr) {
       if (I->InfosType == Unknown) {
          // Gather the type info and determine the catch type.
-         tree Catches = TREE_OPERAND(I->TryExpr, 1);
-         GatherTypeInfo(Catches, I->TypeInfos);
-         I->InfosType = (TREE_CODE(Catches) == STATEMENT_LIST &&
-                         !tsi_end_p(tsi_start(Catches)) &&
-                         TREE_CODE(tsi_stmt(tsi_start(Catches))) ==
+         GatherTypeInfo(I->CatchExpr, I->TypeInfos);
+         I->InfosType = (TREE_CODE(I->CatchExpr) == STATEMENT_LIST &&
+                         !tsi_end_p(tsi_start(I->CatchExpr)) &&
+                         TREE_CODE(tsi_stmt(tsi_start(I->CatchExpr))) ==
                          EH_FILTER_EXPR) ? FilterExpr : CatchList;
       }
 
@@ -2078,28 +2078,61 @@
 }
 
 
+/// EmitProtectedCleanups - Wrap cleanups in a TRY_FILTER_EXPR that executes the
+/// code specified by lang_protect_cleanup_actions if an exception is thrown.
+void TreeToLLVM::EmitProtectedCleanups(tree cleanups) {
+  if (!lang_protect_cleanup_actions) {
+    Emit(cleanups, 0);
+    return;
+  }
+
+  if (CleanupFilter == NULL_TREE) {
+    // Create a catch-all filter that routes exceptions to the code specified
+    // by the lang_protect_cleanup_actions langhook.
+    // FIXME: the handler is supposed to be a "nothrow region".  Support for
+    // this is blocked on support for nothrow functions.
+    tree filter = build (EH_FILTER_EXPR, void_type_node, NULL, NULL);
+    append_to_statement_list (lang_protect_cleanup_actions(),
+                              &EH_FILTER_FAILURE (filter));
+    // CleanupFilter is the filter wrapped in a STATEMENT_LIST.
+    append_to_statement_list (filter, &CleanupFilter);
+  }
+
+  EmitTryInternal(cleanups, CleanupFilter, true);
+}
+
+
 /// EmitTRY_EXPR - Handle TRY_FINALLY_EXPR and TRY_CATCH_EXPR.
 Value *TreeToLLVM::EmitTRY_EXPR(tree exp) {
+  return EmitTryInternal(TREE_OPERAND(exp, 0), TREE_OPERAND(exp, 1),
+                         TREE_CODE(exp) == TRY_CATCH_EXPR);
+}
+
+
+/// EmitTryInternal - Handle TRY_FINALLY_EXPR and TRY_CATCH_EXPR given only the
+/// expression operands.  Done to avoid having EmitProtectedCleanups build a new
+/// TRY_CATCH_EXPR for every cleanup block it wraps.
+Value *TreeToLLVM::EmitTryInternal(tree inner, tree handler, bool isCatch) {
   // The C++ front-end produces a lot of TRY_FINALLY_EXPR nodes that have empty
   // try blocks.  When these are seen, just emit the finally block directly for
   // a small compile time speedup.
-  if (TREE_CODE(TREE_OPERAND(exp, 0)) == STATEMENT_LIST &&
-      tsi_end_p(tsi_start(TREE_OPERAND(exp, 0)))) {
-    if (TREE_CODE(exp) == TRY_CATCH_EXPR)
+  if (TREE_CODE(inner) == STATEMENT_LIST && tsi_end_p(tsi_start(inner))) {
+    if (isCatch)
       return 0;   // TRY_CATCH_EXPR with empty try block: nothing thrown.
 
     // TRY_FINALLY_EXPR - Just run the finally block.
-    assert(TREE_CODE(exp) == TRY_FINALLY_EXPR);
-    Emit(TREE_OPERAND(exp, 1), 0);
+    assert(!isCatch);
+    EmitProtectedCleanups(handler);
     return 0;
   }  
 
   // Remember that we are in this scope.
-  CurrentEHScopes.push_back(exp);
+  CurrentEHScopes.push_back(isCatch ? handler : NULL);
   
-  Emit(TREE_OPERAND(exp, 0), 0);
+  Emit(inner, 0);
   
-  assert(CurrentEHScopes.back().TryExpr == exp && "Scope imbalance!");
+  assert(!isCatch || CurrentEHScopes.back().CatchExpr == handler
+         && "Scope imbalance!");
 
   // Emit a new block for the fall-through of the finally block.
   BasicBlock *FinallyBlock = new BasicBlock("finally");
@@ -2161,7 +2194,7 @@
     
     // If this is a TRY_CATCH expression and the fixup isn't for an exception
     // edge, punt the fixup up to the parent scope.
-    if (TREE_CODE(exp) == TRY_CATCH_EXPR && !BranchFixups[i].isExceptionEdge) {
+    if (isCatch && !BranchFixups[i].isExceptionEdge) {
       // Add the fixup to the parent cleanup scope if there is one.
       if (!CurrentEHScopes.empty())
         AddBranchFixup(BranchFixups[i].SrcBranch, false);
@@ -2183,17 +2216,29 @@
     // Okay, the destination is in a parent to this scope, which means that the
     // branch fixup corresponds to an exit from this region.  Expand the cleanup
     // code then patch it into the code sequence.
-    tree CleanupCode = TREE_OPERAND(exp, 1);
-    
+
     // Add a basic block to emit the code into.
     BasicBlock *CleanupBB = new BasicBlock("cleanup");
     EmitBlock(CleanupBB);
     
     // Provide exit point for cleanup code.
     FinallyStack.push_back(FinallyBlock);
-    
+
     // Emit the code.
-    Emit(CleanupCode, 0);
+    if (isCatch)
+      switch (TREE_CODE (tsi_stmt (tsi_start (handler)))) {
+      case CATCH_EXPR:
+      case EH_FILTER_EXPR:
+        Emit(handler, 0);
+        break;
+      default:
+        // Wrap the handler in a filter, like for TRY_FINALLY_EXPR, since this
+        // is what tree-eh.c does.
+        EmitProtectedCleanups(handler);
+        break;
+      }
+    else
+      EmitProtectedCleanups(handler);
 
     // Clear exit point for cleanup code.
     FinallyStack.pop_back();
@@ -2201,7 +2246,7 @@
     // Because we can emit the same cleanup in more than one context, we must
     // strip off LLVM information from the decls in the code.  Otherwise, we
     // will try to insert the same label into multiple places in the code.
-    StripLLVMTranslation(CleanupCode);
+    StripLLVMTranslation(handler);
 
 
     // Catches will supply own terminator.

Modified: apple-local/branches/llvm/gcc/llvm-internal.h
===================================================================
--- apple-local/branches/llvm/gcc/llvm-internal.h	2007-07-17 08:37:53 UTC (rev 129633)
+++ apple-local/branches/llvm/gcc/llvm-internal.h	2007-07-17 15:37:30 UTC (rev 129634)
@@ -264,8 +264,9 @@
   /// EHScope - One of these scopes is maintained for each TRY_CATCH_EXPR and
   /// TRY_FINALLY_EXPR blocks that we are currently in.
   struct EHScope {
-    /// TryExpr - This is the actual TRY_CATCH_EXPR or TRY_FINALLY_EXPR.
-    tree_node *TryExpr;
+    /// CatchExpr - Contains the cleanup code for a TRY_CATCH_EXPR, and NULL for
+    /// a TRY_FINALLY_EXPR.
+    tree_node *CatchExpr;
 
     /// UnwindBlock - A basic block in this scope that branches to the unwind
     /// destination.  This is lazily created by the first invoke in this scope.
@@ -278,18 +279,18 @@
     /// or in a parent scope.
     std::vector<BranchFixup> BranchFixups;
 
-    /// InfosType - The nature of the type infos TryExpr contains: a list of
+    /// InfosType - The nature of the type infos CatchExpr contains: a list of
     /// CATCH_EXPR (-> CatchList) or an EH_FILTER_EXPR (-> FilterExpr).  Equal
     /// to Unknown if type info information has not yet been gathered.
     CatchTypes InfosType;
 
     /// TypeInfos - The type infos corresponding to the catches or filter in
-    /// TryExpr.  If InfosType is Unknown then this information has not yet
+    /// CatchExpr.  If InfosType is Unknown then this information has not yet
     /// been gathered.
     std::vector<Constant *> TypeInfos;
 
     EHScope(tree_node *expr) :
-        TryExpr(expr), UnwindBlock(0), InfosType(Unknown) {}
+        CatchExpr(expr), UnwindBlock(0), InfosType(Unknown) {}
   };
   
   /// CurrentEHScopes - The current stack of exception scopes we are
@@ -301,7 +302,13 @@
   /// list of blocks maintained by the scope and the scope number is added to
   /// this map.
   std::map<BasicBlock*, unsigned> BlockEHScope;
-  
+
+  /// CleanupFilter - Lazily created EH_FILTER_EXPR wrapped in a STATEMENT_LIST
+  /// used to catch exceptions thrown by the finally part of a TRY_FINALLY_EXPR.
+  /// The handler code is specified by the lang_protect_cleanup_actions langhook
+  /// (which returns a call to "terminate" in the case of C++).
+  tree_node *CleanupFilter;
+
   /// ExceptionValue - Is the local to receive the current exception.
   /// 
   Value *ExceptionValue;
@@ -482,7 +489,11 @@
   // Basic lists and binding scopes.
   Value *EmitBIND_EXPR(tree_node *exp, Value *DestLoc);
   Value *EmitSTATEMENT_LIST(tree_node *exp, Value *DestLoc);
-  
+
+  // Helpers for exception handling.
+  void   EmitProtectedCleanups(tree_node *cleanups);
+  Value *EmitTryInternal(tree_node *inner, tree_node *handler, bool isCatch);
+
   // Control flow.
   Value *EmitLABEL_EXPR(tree_node *exp);
   Value *EmitGOTO_EXPR(tree_node *exp);
@@ -493,7 +504,7 @@
   Value *EmitCATCH_EXPR(tree_node *exp);
   Value *EmitEXC_PTR_EXPR(tree_node *exp);
   Value *EmitEH_FILTER_EXPR(tree_node *exp);
-  
+
   // Expressions.
   void   EmitINTEGER_CST_Aggregate(tree_node *exp, Value *DestLoc);
   Value *EmitLoadOfLValue(tree_node *exp, Value *DestLoc);





More information about the llvm-commits mailing list