diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index acfef18d75e2..29fd7b49cb27 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -699,6 +699,8 @@ LANGBUILTIN(__exception_code, "ULi", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_exception_code, "ULi", "n", ALL_MS_LANGUAGES) LANGBUILTIN(__exception_info, "v*", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_exception_info, "v*", "n", ALL_MS_LANGUAGES) +LANGBUILTIN(__abnormal_termination, "i", "n", ALL_MS_LANGUAGES) +LANGBUILTIN(_abnormal_termination, "i", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_InterlockedCompareExchange, "LiLiD*LiLi", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_InterlockedCompareExchangePointer, "v*v*D*v*v*", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_InterlockedDecrement, "LiLiD*", "n", ALL_MS_LANGUAGES) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index f192788c787e..aa6d8d6587d0 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1663,6 +1663,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__exception_info: case Builtin::BI_exception_info: return RValue::get(EmitSEHExceptionInfo()); + case Builtin::BI__abnormal_termination: + case Builtin::BI_abnormal_termination: + return RValue::get(EmitSEHAbnormalTermination()); case Builtin::BI_setjmpex: { if (getTarget().getTriple().isOSMSVCRT()) { llvm::Type *ArgTypes[] = {Int8PtrTy, Int8PtrTy}; diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index f94a380c2104..e90f56810dd0 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -451,6 +451,12 @@ llvm::Value *CodeGenFunction::getSelectorFromSlot() { return Builder.CreateLoad(getEHSelectorSlot(), "sel"); } +llvm::Value *CodeGenFunction::getAbnormalTerminationSlot() { + if (!AbnormalTerminationSlot) + AbnormalTerminationSlot = CreateTempAlloca(Int8Ty, "abnormal.termination.slot"); + return AbnormalTerminationSlot; +} + void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint) { if (!E->getSubExpr()) { @@ -1686,18 +1692,45 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { return; } - EnterSEHTryStmt(S); + SEHFinallyInfo FI; + EnterSEHTryStmt(S, FI); EmitStmt(S.getTryBlock()); - ExitSEHTryStmt(S); + ExitSEHTryStmt(S, FI); } namespace { struct PerformSEHFinally : EHScopeStack::Cleanup { - Stmt *Block; - PerformSEHFinally(Stmt *Block) : Block(Block) {} + CodeGenFunction::SEHFinallyInfo *FI; + PerformSEHFinally(CodeGenFunction::SEHFinallyInfo *FI) : FI(FI) {} + void Emit(CodeGenFunction &CGF, Flags F) override { - // FIXME: Don't double-emit LabelDecls. - CGF.EmitStmt(Block); + // Cleanups are emitted at most twice: once for normal control flow and once + // for exception control flow. Branch into the finally block, and remember + // the continuation block so we can branch out later. + if (!FI->FinallyBB) { + FI->FinallyBB = CGF.createBasicBlock("__finally"); + FI->FinallyBB->insertInto(CGF.CurFn); + FI->FinallyBB->moveAfter(CGF.Builder.GetInsertBlock()); + } + + // Set the termination status and branch in. + CGF.Builder.CreateStore( + llvm::ConstantInt::get(CGF.Int8Ty, F.isForEHCleanup()), + CGF.getAbnormalTerminationSlot()); + CGF.Builder.CreateBr(FI->FinallyBB); + + // Create a continuation block for normal or exceptional control. + if (F.isForEHCleanup()) { + assert(!FI->ResumeBB && "double emission for EH"); + FI->ResumeBB = CGF.createBasicBlock("__finally.resume"); + CGF.EmitBlock(FI->ResumeBB); + } else { + assert(F.isForNormalCleanup() && !FI->ContBB && "double normal emission"); + FI->ContBB = CGF.createBasicBlock("__finally.cont"); + CGF.EmitBlock(FI->ContBB); + // Try to keep source order. + FI->ContBB->moveAfter(FI->FinallyBB); + } } }; } @@ -1827,11 +1860,17 @@ llvm::Value *CodeGenFunction::EmitSEHExceptionCode() { return Builder.CreateTrunc(Code, CGM.Int32Ty); } -void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) { +llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() { + // Load from the abnormal termination slot. It will be uninitialized outside + // of __finally blocks, which we should warn or error on. + llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot()); + return Builder.CreateZExt(IsEH, Int32Ty); +} + +void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { if (SEHFinallyStmt *Finally = S.getFinallyHandler()) { // Push a cleanup for __finally blocks. - EHStack.pushCleanup(NormalAndEHCleanup, - Finally->getBlock()); + EHStack.pushCleanup(NormalAndEHCleanup, &FI); return; } @@ -1859,15 +1898,34 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) { CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except")); } -void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) { +void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { // Just pop the cleanup if it's a __finally block. - if (S.getFinallyHandler()) { + if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) { PopCleanupBlock(); + + // Emit the code into FinallyBB. + Builder.SetInsertPoint(FI.FinallyBB); + EmitStmt(Finally->getBlock()); + + assert(FI.ContBB); + if (FI.ResumeBB) { + llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot(), + "abnormal.termination"); + IsEH = Builder.CreateICmpEQ(IsEH, llvm::ConstantInt::get(Int8Ty, 0)); + Builder.CreateCondBr(IsEH, FI.ContBB, FI.ResumeBB); + } else { + // There was nothing exceptional in the try body, so we only have normal + // control flow. + Builder.CreateBr(FI.ContBB); + } + + Builder.SetInsertPoint(FI.ContBB); + return; } // Otherwise, we must have an __except block. - SEHExceptStmt *Except = S.getExceptHandler(); + const SEHExceptStmt *Except = S.getExceptHandler(); assert(Except && "__try must have __finally xor __except"); EHCatchScope &CatchScope = cast(*EHStack.begin()); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 43c9dfe87a09..79425d4c21ee 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -43,7 +43,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) BlockInfo(nullptr), BlockPointer(nullptr), LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr), NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr), - ExceptionSlot(nullptr), EHSelectorSlot(nullptr), SEHPointersDecl(nullptr), + ExceptionSlot(nullptr), EHSelectorSlot(nullptr), + AbnormalTerminationSlot(nullptr), SEHPointersDecl(nullptr), DebugInfo(CGM.getModuleDebugInfo()), DisableDebugInfo(false), DidCallStackSave(false), IndirectBranch(nullptr), PGO(cgm), SwitchInsn(nullptr), SwitchWeights(nullptr), CaseRangeBlock(nullptr), diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 60e81db3753c..f6e2bae3195a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -306,6 +306,8 @@ public: /// write the current selector value into this alloca. llvm::AllocaInst *EHSelectorSlot; + llvm::AllocaInst *AbnormalTerminationSlot; + /// The implicit parameter to SEH filter functions of type /// 'EXCEPTION_POINTERS*'. ImplicitParamDecl *SEHPointersDecl; @@ -348,6 +350,17 @@ public: void exit(CodeGenFunction &CGF); }; + /// Cleanups can be emitted for two reasons: normal control leaving a region + /// exceptional control flow leaving a region. + struct SEHFinallyInfo { + SEHFinallyInfo() + : FinallyBB(nullptr), ContBB(nullptr), ResumeBB(nullptr) {} + + llvm::BasicBlock *FinallyBB; + llvm::BasicBlock *ContBB; + llvm::BasicBlock *ResumeBB; + }; + /// pushFullExprCleanup - Push a cleanup to be run at the end of the /// current full-expression. Safe against the possibility that /// we're currently inside a conditionally-evaluated expression. @@ -1085,6 +1098,10 @@ public: llvm::Value *getExceptionSlot(); llvm::Value *getEHSelectorSlot(); + /// Stack slot that contains whether a __finally block is being executed as an + /// EH cleanup or as a normal cleanup. + llvm::Value *getAbnormalTerminationSlot(); + /// Returns the contents of the function's exception object and selector /// slots. llvm::Value *getExceptionFromSlot(); @@ -2007,8 +2024,8 @@ public: void EmitCXXTryStmt(const CXXTryStmt &S); void EmitSEHTryStmt(const SEHTryStmt &S); void EmitSEHLeaveStmt(const SEHLeaveStmt &S); - void EnterSEHTryStmt(const SEHTryStmt &S); - void ExitSEHTryStmt(const SEHTryStmt &S); + void EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI); + void ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI); llvm::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, const SEHExceptStmt &Except); @@ -2016,6 +2033,7 @@ public: void EmitSEHExceptionCodeSave(); llvm::Value *EmitSEHExceptionCode(); llvm::Value *EmitSEHExceptionInfo(); + llvm::Value *EmitSEHAbnormalTermination(); void EmitCXXForRangeStmt(const CXXForRangeStmt &S, ArrayRef Attrs = None); diff --git a/clang/test/CodeGen/exceptions-seh-finally.c b/clang/test/CodeGen/exceptions-seh-finally.c index c8bf237f5a07..1eca72ae15ca 100644 --- a/clang/test/CodeGen/exceptions-seh-finally.c +++ b/clang/test/CodeGen/exceptions-seh-finally.c @@ -13,19 +13,44 @@ void basic_finally(void) { // CHECK-LABEL: define void @basic_finally() // CHECK: invoke void @might_crash() +// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] +// +// CHECK: [[invoke_cont]] +// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] +// CHECK: br label %[[finally:[^ ]*]] +// +// CHECK: [[finally]] // CHECK: call void @cleanup() +// CHECK: load i8* %[[abnormal]] +// CHECK: icmp eq +// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]] // -// CHECK: landingpad +// CHECK: [[finallycont]] +// CHECK-NEXT: ret void +// +// CHECK: [[lpad]] +// CHECK-NEXT: landingpad // CHECK-NEXT: cleanup -// CHECK: invoke void @cleanup() +// CHECK: store i8 1, i8* %[[abnormal]] +// CHECK: br label %[[finally]] // -// CHECK: landingpad -// CHECK-NEXT: catch i8* null -// CHECK: call void @abort() +// CHECK: [[resumecont]] +// CHECK: br label %[[ehresume:[^ ]*]] +// +// CHECK: [[ehresume]] +// CHECK: resume { i8*, i32 } -// FIXME: This crashes. -#if 0 -void basic_finally(void) { +// Mostly check that we don't double emit 'r' which would crash. +void decl_in_finally(void) { + __try { + might_crash(); + } __finally { + int r; + } +} + +// Ditto, don't crash double emitting 'l'. +void label_in_finally(void) { __try { might_crash(); } __finally { @@ -35,4 +60,60 @@ l: goto l; } } -#endif + +// CHECK-LABEL: define void @label_in_finally() +// CHECK: invoke void @might_crash() +// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] +// +// CHECK: [[invoke_cont]] +// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] +// CHECK: br label %[[finally:[^ ]*]] +// +// CHECK: [[finally]] +// CHECK: br label %[[l:[^ ]*]] +// +// CHECK: [[l]] +// CHECK: call void @cleanup() +// CHECK: call i32 @check_condition() +// CHECK: br i1 {{.*}}, label +// CHECK: br label %[[l]] + +int crashed; +void use_abnormal_termination(void) { + __try { + might_crash(); + } __finally { + crashed = __abnormal_termination(); + } +} + +// CHECK-LABEL: define void @use_abnormal_termination() +// CHECK: invoke void @might_crash() +// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] +// +// CHECK: [[invoke_cont]] +// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] +// CHECK: br label %[[finally:[^ ]*]] +// +// CHECK: [[finally]] +// CHECK: load i8* %[[abnormal]] +// CHECK: zext i8 %{{.*}} to i32 +// CHECK: store i32 %{{.*}}, i32* @crashed +// CHECK: load i8* %[[abnormal]] +// CHECK: icmp eq +// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]] +// +// CHECK: [[finallycont]] +// CHECK-NEXT: ret void +// +// CHECK: [[lpad]] +// CHECK-NEXT: landingpad +// CHECK-NEXT: cleanup +// CHECK: store i8 1, i8* %[[abnormal]] +// CHECK: br label %[[finally]] +// +// CHECK: [[resumecont]] +// CHECK: br label %[[ehresume:[^ ]*]] +// +// CHECK: [[ehresume]] +// CHECK: resume { i8*, i32 } diff --git a/clang/test/CodeGen/exceptions-seh.c b/clang/test/CodeGen/exceptions-seh.c index 07b43c6f108f..fcd90b991326 100644 --- a/clang/test/CodeGen/exceptions-seh.c +++ b/clang/test/CodeGen/exceptions-seh.c @@ -138,17 +138,22 @@ void basic_finally(void) { // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // // CHECK: [[cont]] +// CHECK: br label %[[finally:[^ ]*]] +// +// CHECK: [[finally]] // CHECK: load i32* @g // CHECK: add i32 %{{.*}}, -1 // CHECK: store i32 %{{.*}}, i32* @g +// CHECK: icmp eq +// CHECK: br i1 %{{.*}}, label +// // CHECK: ret void // // CHECK: [[lpad]] // CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) // CHECK-NEXT: cleanup -// CHECK: load i32* @g -// CHECK: add i32 %{{.*}}, -1 -// CHECK: store i32 %{{.*}}, i32* @g +// CHECK: br label %[[finally]] +// // CHECK: resume int returns_int(void); diff --git a/clang/test/Sema/__try.c b/clang/test/Sema/__try.c index 607da9df27e6..0e5de2018db0 100644 --- a/clang/test/Sema/__try.c +++ b/clang/test/Sema/__try.c @@ -15,7 +15,7 @@ unsigned long __exception_code(); #ifdef BORLAND struct EXCEPTION_INFO* __exception_info(); #endif -void __abnormal_termination(); +int __abnormal_termination(); #define GetExceptionCode __exception_code #define GetExceptionInformation __exception_info