Hi rnk,

I tried a few different approaches, in the end I went with something that has a 
simple implementation and doesn't try to cleverly reuse labels that sometimes 
already exist in the right places.

I found PR22553 while writing the tests for this.

http://reviews.llvm.org/D7575

Files:
  lib/CodeGen/CGException.cpp
  lib/CodeGen/CGStmt.cpp
  lib/CodeGen/CodeGenFunction.h
  test/CodeGen/exceptions-seh-leave.c

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
Index: lib/CodeGen/CGException.cpp
===================================================================
--- lib/CodeGen/CGException.cpp
+++ lib/CodeGen/CGException.cpp
@@ -1707,9 +1707,19 @@
   SEHFinallyInfo FI;
   EnterSEHTryStmt(S, FI);
   {
+    JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave");
+    SEHTryEpilogueStack.push_back(&TryExit);
+
     // Disable inlining inside SEH __try scopes.
     SaveAndRestore<bool> Saver(IsSEHTryScope, true);
     EmitStmt(S.getTryBlock());
+
+    if (!TryExit.getBlock()->use_empty()) {
+      EmitBlock(TryExit.getBlock(), /*IsFinished=*/true);
+    } else {
+      delete TryExit.getBlock();
+    }
+    SEHTryEpilogueStack.pop_back();
   }
   ExitSEHTryStmt(S, FI);
 }
@@ -1988,5 +1998,13 @@
 }
 
 void CodeGenFunction::EmitSEHLeaveStmt(const SEHLeaveStmt &S) {
-  CGM.ErrorUnsupported(&S, "SEH __leave");
+  // If this code is reachable then emit a stop point (if generating
+  // debug info). We have to do this ourselves because we are on the
+  // "simple" statement path.
+  if (HaveInsertPoint())
+    EmitStopPoint(&S);
+
+  assert(!SEHTryEpilogueStack.empty() &&
+         "sema should have rejected this __leave");
+  EmitBranchThroughCleanup(*SEHTryEpilogueStack.back());
 }
Index: lib/CodeGen/CGStmt.cpp
===================================================================
--- lib/CodeGen/CGStmt.cpp
+++ lib/CodeGen/CGStmt.cpp
@@ -88,6 +88,7 @@
   case Stmt::ContinueStmtClass:
   case Stmt::DefaultStmtClass:
   case Stmt::CaseStmtClass:
+  case Stmt::SEHLeaveStmtClass:
     llvm_unreachable("should have emitted these statements as simple");
 
 #define STMT(Type, Base)
@@ -173,9 +174,6 @@
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;
-  case Stmt::SEHLeaveStmtClass:
-    EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S));
-    break;
   case Stmt::OMPParallelDirectiveClass:
     EmitOMPParallelDirective(cast<OMPParallelDirective>(*S));
     break;
@@ -256,6 +254,7 @@
   case Stmt::ContinueStmtClass: EmitContinueStmt(cast<ContinueStmt>(*S)); break;
   case Stmt::DefaultStmtClass:  EmitDefaultStmt(cast<DefaultStmt>(*S));   break;
   case Stmt::CaseStmtClass:     EmitCaseStmt(cast<CaseStmt>(*S));         break;
+  case Stmt::SEHLeaveStmtClass: EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S)); break;
   }
 
   return true;
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -278,6 +278,7 @@
 
   EHScopeStack EHStack;
   llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack;
+  llvm::SmallVector<const JumpDest *, 2> SEHTryEpilogueStack;
 
   /// Header for data within LifetimeExtendedCleanupStack.
   struct LifetimeExtendedCleanupHeader {
Index: test/CodeGen/exceptions-seh-leave.c
===================================================================
--- test/CodeGen/exceptions-seh-leave.c
+++ test/CodeGen/exceptions-seh-leave.c
@@ -1,19 +1,229 @@
-// RUN: not %clang_cc1 -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
 
-// This is a codegen test because we only emit the diagnostic when we start
-// generating code.
+void g();
 
-int SaveDiv(int numerator, int denominator, int *res) {
+//////////////////////////////////////////////////////////////////////////////
+// __leave with __except
+
+// Nothing in the __try block can trap, so __try.cont isn't created.
+int __leave_with___except_simple() {
   int myres = 0;
   __try {
-    myres = numerator / denominator;
+    myres = 15;
     __leave;
+    myres = 23;
   } __except (1) {
     return 0;
   }
-  *res = myres;
   return 1;
 }
-// CHECK-NOT: error:
-// CHECK: error: cannot compile this SEH __leave yet
-// CHECK-NOT: error:
+// CHECK-LABEL: define i32 @__leave_with___except_simple()
+// CHECK: store i32 15, i32* %myres
+// CHECK-NEXT: br label %__try.__leave
+// CHECK-NOT: store i32 23
+// CHECK: __try.__leave:
+// CHECK-NEXT: ret i32 1
+
+
+// The "normal" case.
+int __leave_with___except() {
+  int myres = 0;
+  __try {
+    g();
+    __leave;
+    myres = 23;
+  } __except (1) {
+    return 0;
+  }
+  return 1;
+}
+// CHECK-LABEL: define i32 @__leave_with___except()
+// CHECK: invoke void bitcast (void (...)* @g to void ()*)()
+// CHECK-NEXT:       to label %[[cont:.*]] unwind label %{{.*}}
+// For __excepts, instead of an explicit __try.__leave label, we could use
+// use invoke.cont as __leave jump target instead.  However, not doing this
+// keeps the CodeGen code simpler, __leave is very rare, and SimplifyCFG will
+// simplify this anyways.
+// CHECK: [[cont]]
+// CHECK-NEXT: br label %__try.__leave
+// CHECK-NOT: store i32 23
+// CHECK: __try.__leave:
+// CHECK-NEXT: br label %__try.cont
+
+
+//////////////////////////////////////////////////////////////////////////////
+// __leave with __finally
+
+void abort(void) __attribute__((noreturn));
+
+// Nothing in the __try block can trap, so __finally.cont and friends aren't
+// created.
+int __leave_with___finally_simple() {
+  int myres = 0;
+  __try {
+    myres = 15;
+    __leave;
+    myres = 23;
+  } __finally {
+    return 0;
+  }
+  return 1;
+}
+// CHECK-LABEL: define i32 @__leave_with___finally_simple()
+// CHECK: store i32 15, i32* %myres
+// CHECK-NEXT: br label %__try.__leave
+// CHECK-NOT: store i32 23
+// CHECK: __try.__leave:
+// CHECK-NEXT: store i8 0, i8* %abnormal.termination.slot
+// CHECK-NEXT: br label %__finally
+
+// __finally block doesn't return, __finally.cont doesn't exist.
+int __leave_with___finally_noreturn() {
+  int myres = 0;
+  __try {
+    myres = 15;
+    __leave;
+    myres = 23;
+  } __finally {
+    abort();
+  }
+  return 1;
+}
+// CHECK-LABEL: define i32 @__leave_with___finally_noreturn()
+// CHECK: store i32 15, i32* %myres
+// CHECK-NEXT: br label %__try.__leave
+// CHECK-NOT: store i32 23
+// CHECK: __try.__leave:
+// CHECK-NEXT: store i8 0, i8* %abnormal.termination.slot
+// CHECK-NEXT: br label %__finally
+
+// The "normal" case.
+int __leave_with___finally() {
+  int myres = 0;
+  __try {
+    g();
+    __leave;
+    myres = 23;
+  } __finally {
+    return 0;
+  }
+  return 1;
+}
+// CHECK-LABEL: define i32 @__leave_with___finally()
+// CHECK: invoke void bitcast (void (...)* @g to void ()*)()
+// CHECK-NEXT:       to label %[[cont:.*]] unwind label %{{.*}}
+// For __finally, there needs to be an explicit __try.__leave, because
+// abnormal.termination.slot needs to be set there.
+// CHECK: [[cont]]
+// CHECK-NEXT: br label %__try.__leave
+// CHECK-NOT: store i32 23
+// CHECK: __try.__leave:
+// CHECK-NEXT: store i8 0, i8* %abnormal.termination.slot
+// CHECK-NEXT: br label %__finally
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Mixed, nested cases.
+
+// FIXME: Test with outer __finally once PR22553 is fixed.
+
+int nested___except___finally() {
+  int myres = 0;
+  __try {
+    __try {
+      g();
+    } __finally {
+      g();
+      __leave;  // Refers to the outer __try, not the __finally!
+      myres = 23;
+      return 0;
+    }
+
+    myres = 51;
+  } __except (1) {
+  }
+  return 1;
+}
+// The order of basic blocks in the below doesn't matter.
+// CHECK-LABEL: define i32 @nested___except___finally()
+
+// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)()
+// CHECK-NEXT:       to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]
+
+// CHECK: [[g1_cont]]:
+// CHECK-NEXT: store i8 0, i8* %abnormal.termination.slot
+// CHECK-NEXT: br label %__finally
+
+// CHECK-LABEL: __finally:
+// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)() #3
+// CHECK-NEXT:       to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
+
+// CHECK: [[g2_cont]]:
+// CHECK-NOT: store i32 23
+// CHECK: br label %__try.__leave
+
+// CHECK: [[g1_lpad]]:
+// CHECK: store i8 1, i8* %abnormal.termination.slot
+// CHECK-NEXT:  br label %__finally
+
+// CHECK: [[g2_lpad]]:
+// CHECK-NOT: %abnormal.termination.slot
+// CHECK: br label %__except
+
+// CHECK-LABEL: __except:
+// CHECK-NEXT: br label %__try.cont
+
+// CHECK-LABEL: __try.__leave:
+// CHECK-NEXT: br label %__try.cont
+
+int nested___except___except() {
+  int myres = 0;
+  __try {
+    __try {
+      g();
+      myres = 16;
+    } __except (1) {
+      g();
+      __leave;  // Refers to the outer __try, not the __except we're in!
+      myres = 23;
+      return 0;
+    }
+
+    myres = 51;
+  } __except (1) {
+  }
+  return 1;
+}
+// The order of basic blocks in the below doesn't matter.
+// CHECK-LABEL: define i32 @nested___except___except()
+
+// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)()
+// CHECK-NEXT:       to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]
+
+// CHECK: [[g1_cont]]:
+// CHECK: store i32 16, i32* %myres
+// CHECK-NEXT: br label %__try.cont
+
+// CHECK: [[g1_lpad]]:
+// CHECK:  br label %__except
+
+// CHECK-LABEL: __except:
+// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)() #3
+// CHECK-NEXT:       to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
+
+// CHECK: [[g2_cont]]:
+// CHECK-NOT: store i32 23
+// CHECK: br label %__try.__leave
+
+// CHECK: [[g2_lpad]]:
+// CHECK: br label %__except3
+
+// CHECK-LABEL: __except3:
+// CHECK-NEXT: br label %__try.cont4
+
+// CHECK-LABEL: __try.cont:
+// CHECK-NEXT: store i32 51, i32* %myres
+// CHECK-NEXT: br label %__try.__leave
+
+// CHECK-LABEL: __try.__leave:
+// CHECK-NEXT: br label %__try.cont4
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to