]> granicus.if.org Git - clang/commitdiff
[ms] Implement codegen for __leave.
authorNico Weber <nicolasweber@gmx.de>
Thu, 12 Feb 2015 23:16:11 +0000 (23:16 +0000)
committerNico Weber <nicolasweber@gmx.de>
Thu, 12 Feb 2015 23:16:11 +0000 (23:16 +0000)
Reviewed at http://reviews.llvm.org/D7575

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@228977 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 4ec87e07dfb9fd7c79816ff4ab2397a58ba3d486..a76b3d82abbba18f5496f7a666c22d04b4e983f5 100644 (file)
@@ -1707,9 +1707,18 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
   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 +1997,13 @@ void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) {
 }
 
 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 d921c0d9b973e8433d6352ec3cad40017bd2fa69..0d160d3eee5b1fad025c32439f1b1b82bdeec158 100644 (file)
@@ -88,6 +88,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
   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 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
   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 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S) {
   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 057b97053ae646972efb56d832938348e879c2e6..67188dfa0a135b45c6eebc6e3b29934696afbe0a 100644 (file)
@@ -278,6 +278,7 @@ public:
 
   EHScopeStack EHStack;
   llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack;
+  llvm::SmallVector<const JumpDest *, 2> SEHTryEpilogueStack;
 
   /// Header for data within LifetimeExtendedCleanupStack.
   struct LifetimeExtendedCleanupHeader {
index 2033c87105f0f98a3eb0e1371d3751fb0fdee96f..51ffd6126ed04a3277e9ac6f9c59b256810f16a0 100644 (file)
-// 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