From: Nico Weber Date: Thu, 12 Feb 2015 23:16:11 +0000 (+0000) Subject: [ms] Implement codegen for __leave. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=635977ba69c62a0a1d82a8b8a00839f5b1c37841;p=clang [ms] Implement codegen for __leave. Reviewed at http://reviews.llvm.org/D7575 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@228977 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 4ec87e07df..a76b3d82ab 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -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 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()); } diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index d921c0d9b9..0d160d3eee 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -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(*S)); break; - case Stmt::SEHLeaveStmtClass: - EmitSEHLeaveStmt(cast(*S)); - break; case Stmt::OMPParallelDirectiveClass: EmitOMPParallelDirective(cast(*S)); break; @@ -256,6 +254,7 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S) { case Stmt::ContinueStmtClass: EmitContinueStmt(cast(*S)); break; case Stmt::DefaultStmtClass: EmitDefaultStmt(cast(*S)); break; case Stmt::CaseStmtClass: EmitCaseStmt(cast(*S)); break; + case Stmt::SEHLeaveStmtClass: EmitSEHLeaveStmt(cast(*S)); break; } return true; diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 057b97053a..67188dfa0a 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -278,6 +278,7 @@ public: EHScopeStack EHStack; llvm::SmallVector LifetimeExtendedCleanupStack; + llvm::SmallVector SEHTryEpilogueStack; /// Header for data within LifetimeExtendedCleanupStack. struct LifetimeExtendedCleanupHeader { diff --git a/test/CodeGen/exceptions-seh-leave.c b/test/CodeGen/exceptions-seh-leave.c index 2033c87105..51ffd6126e 100644 --- a/test/CodeGen/exceptions-seh-leave.c +++ b/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