https://github.com/MuellerMP updated https://github.com/llvm/llvm-project/pull/167176
>From 435309c27a62172ece6232e49177b90b9c168f67 Mon Sep 17 00:00:00 2001 From: MuellerMP <[email protected]> Date: Wed, 26 Nov 2025 16:20:12 +0100 Subject: [PATCH] [WinEH] Fix try scopes leaking to caller on inline This fixes issue #164169 When inlining functions compiled with -EHa, try scope terminators might need to be inserted before inlined returns. This prevents leaking try scopes over to the caller. Try scopes can be ended before a ret due to the unwinder forwarding exceptions in the seh epilog to the caller. --- clang/lib/CodeGen/CGCleanup.cpp | 16 +- clang/lib/CodeGen/CGException.cpp | 48 ++++- clang/lib/CodeGen/CodeGenFunction.h | 5 +- .../test/CodeGen/windows-seh-EHa-Inline1.cpp | 62 +++++++ .../test/CodeGen/windows-seh-EHa-Inline2.cpp | 103 +++++++++++ .../test/CodeGen/windows-seh-EHa-Inline3.cpp | 91 ++++++++++ llvm/lib/CodeGen/WinEHPrepare.cpp | 7 +- .../WinEH/wineh-statenumbering-seh-try-end.ll | 170 ++++++++++++++++++ 8 files changed, 487 insertions(+), 15 deletions(-) create mode 100644 clang/test/CodeGen/windows-seh-EHa-Inline1.cpp create mode 100644 clang/test/CodeGen/windows-seh-EHa-Inline2.cpp create mode 100644 clang/test/CodeGen/windows-seh-EHa-Inline3.cpp create mode 100644 llvm/test/CodeGen/WinEH/wineh-statenumbering-seh-try-end.ll diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 28ac9bf396356..fe490d215d684 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -1329,8 +1329,11 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary, // Need to set "funclet" in OperandBundle properly for noThrow // intrinsic (see CGCall.cpp) static void EmitSehScope(CodeGenFunction &CGF, - llvm::FunctionCallee &SehCppScope) { - llvm::BasicBlock *InvokeDest = CGF.getInvokeDest(); + llvm::FunctionCallee &SehCppScope, + llvm::BasicBlock *InvokeDest = nullptr) { + if (!InvokeDest) + InvokeDest = CGF.getInvokeDest(); + assert(CGF.Builder.GetInsertBlock() && InvokeDest); llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); SmallVector<llvm::OperandBundleDef, 1> BundleList = @@ -1373,11 +1376,16 @@ void CodeGenFunction::EmitSehTryScopeBegin() { } // Invoke a llvm.seh.try.end at the end of a SEH scope for -EHa -void CodeGenFunction::EmitSehTryScopeEnd() { +void CodeGenFunction::EmitSehTryScopeEnd(bool ReuseCachedInvokeDest) { assert(getLangOpts().EHAsynch); llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); llvm::FunctionCallee SehCppScope = CGM.CreateRuntimeFunction(FTy, "llvm.seh.try.end"); - EmitSehScope(*this, SehCppScope); + if (ReuseCachedInvokeDest) { + EmitSehScope(*this, SehCppScope, SehTryEndInvokeDest); + SehTryEndInvokeDest = nullptr; + } else { + EmitSehScope(*this, SehCppScope); + } } diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index e9d20672ce185..704a144fd6af7 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -635,14 +635,24 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { ExitCXXTryStmt(S); } +struct TerminateTryScope final : EHScopeStack::Cleanup { + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitSehTryScopeEnd(true); + } +}; + +struct HandlerInfo { + CatchTypeInfo TypeInfo; + bool RequiresSehScope; +}; + void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { unsigned NumHandlers = S.getNumHandlers(); - EHCatchScope *CatchScope = EHStack.pushCatch(NumHandlers); - + unsigned NumHandlerInfos = NumHandlers > 0 ? NumHandlers - 1 : 0; + llvm::SmallVector<HandlerInfo> HandlerInfos; + HandlerInfos.reserve(NumHandlerInfos); for (unsigned I = 0; I != NumHandlers; ++I) { const CXXCatchStmt *C = S.getHandler(I); - - llvm::BasicBlock *Handler = createBasicBlock("catch"); if (C->getExceptionDecl()) { // FIXME: Dropping the reference type on the type into makes it // impossible to correctly implement catch-by-reference @@ -660,14 +670,30 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { else TypeInfo = CGM.getCXXABI().getAddrOfCXXCatchHandlerType( CaughtType, C->getCaughtType()); - CatchScope->setHandler(I, TypeInfo, Handler); + HandlerInfos.push_back({TypeInfo, false}); } else { + bool HasEHa = getLangOpts().EHAsynch; // No exception decl indicates '...', a catch-all. - CatchScope->setHandler(I, CGM.getCXXABI().getCatchAllTypeInfo(), Handler); + HandlerInfos.push_back({CGM.getCXXABI().getCatchAllTypeInfo(), HasEHa}); + // Push, if needed, a terminator for the created SEH __try scope + if (HasEHa && !SehTryEndInvokeDest) { + EHStack.pushCleanup<TerminateTryScope>(NormalCleanup); + } + } + } + + EHCatchScope *CatchScope = EHStack.pushCatch(NumHandlers); + + for (unsigned I = 0; I < HandlerInfos.size(); ++I) { + llvm::BasicBlock *Handler = createBasicBlock("catch"); + auto HandlerInfo = HandlerInfos[I]; + CatchScope->setHandler(I, HandlerInfo.TypeInfo, Handler); + if (HandlerInfo.RequiresSehScope) { // Under async exceptions, catch(...) need to catch HW exception too // Mark scope with SehTryBegin as a SEH __try scope - if (getLangOpts().EHAsynch) - EmitSehTryScopeBegin(); + EmitSehTryScopeBegin(); + if (!SehTryEndInvokeDest) + SehTryEndInvokeDest = getInvokeDest(); } } } @@ -1675,6 +1701,8 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { EmitRuntimeCallOrInvoke(getSehTryBeginFn(CGM)); if (SEHTryEpilogueStack.size() == 1) // outermost only TryBB = Builder.GetInsertBlock(); + if (!SehTryEndInvokeDest) + SehTryEndInvokeDest = getInvokeDest(); } EmitStmt(S.getTryBlock()); @@ -2186,6 +2214,10 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) { // Otherwise, we must have an __except block. const SEHExceptStmt *Except = S.getExceptHandler(); assert(Except); + + if (getLangOpts().EHAsynch && !SehTryEndInvokeDest) + EHStack.pushCleanup<TerminateTryScope>(NormalCleanup); + EHCatchScope *CatchScope = EHStack.pushCatch(1); SEHCodeSlotStack.push_back( CreateMemTemp(getContext().IntTy, "__exception_code")); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 8c4c1c8c2dc95..8930d784da5e4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2129,6 +2129,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// Terminate funclets keyed by parent funclet pad. llvm::MapVector<llvm::Value *, llvm::BasicBlock *> TerminateFunclets; + /// Unwind destination for try scope end. + llvm::BasicBlock *SehTryEndInvokeDest = nullptr; + /// Largest vector width used in ths function. Will be used to create a /// function attribute. unsigned LargestVectorWidth = 0; @@ -3241,7 +3244,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitSehCppScopeBegin(); void EmitSehCppScopeEnd(); void EmitSehTryScopeBegin(); - void EmitSehTryScopeEnd(); + void EmitSehTryScopeEnd(bool ReuseCachedInvokeDest = false); bool EmitLifetimeStart(llvm::Value *Addr); void EmitLifetimeEnd(llvm::Value *Addr); diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp new file mode 100644 index 0000000000000..26d8cc204274e --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline1.cpp @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that the try scope of ExitOnThrow is terminated upon inlining into main. +int AlwaysThrows(int); +[[noreturn]] void Exit(); + +int ExitOnThrow(int argc) noexcept +{ + try { + if (!argc) { throw -1; } + return argc; + } catch (...) { + } + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] personality ptr @__CxxFrameHandler3 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: store i32 -1, ptr [[TMP_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @_CxxThrowException(ptr nonnull [[TMP_I]], ptr nonnull @_TI1H) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[CATCH_I:.*]]] unwind to caller +// CHECK: [[CATCH_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP2]] to label %[[TRY_CONT_I:.*]] +// CHECK: [[TRY_CONT_I]]: +// CHECK-NEXT: call void @"?Exit@@YAXXZ"() #[[ATTR7:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@[email protected]" unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[UNREACHABLE_I]]: +// CHECK-NEXT: unreachable +// CHECK: "?ExitOnThrow@@[email protected]": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +//. diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp new file mode 100644 index 0000000000000..b7edcf00ecb7e --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that only the outermost try scope containing a return statement is terminated upon inlining into main. +void DoSth(); +int AlwaysThrows(int); +[[noreturn]] void Exit(); + +int ExitOnThrow(int argc) noexcept +{ + try { + try { + DoSth(); + } catch(...) {} + } catch(...) {} + + try { + try { + if (!argc) { throw -1; } + return argc; + } catch (...) {} + } catch (...) {} + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] personality ptr @__CxxFrameHandler3 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH4_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT1_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[INVOKE_CONT1_I]]: +// CHECK-NEXT: invoke void @"?DoSth@@YAXXZ"() +// CHECK-NEXT: to label %[[TRY_CONT7_I:.*]] unwind label %[[CATCH_DISPATCH_I]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[CATCH_I:.*]]] unwind label %[[CATCH_DISPATCH4_I]] +// CHECK: [[CATCH_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: invoke void @llvm.seh.scope.end() [ "funclet"(token [[TMP2]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT3_I:.*]] unwind label %[[CATCH_DISPATCH4_I]] +// CHECK: [[CATCH_DISPATCH4_I]]: +// CHECK-NEXT: [[TMP3:%.*]] = catchswitch within none [label %[[CATCH5_I:.*]]] unwind to caller +// CHECK: [[CATCH5_I]]: +// CHECK-NEXT: [[TMP4:%.*]] = catchpad within [[TMP3]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP4]] to label %[[TRY_CONT7_I]] +// CHECK: [[TRY_CONT7_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT8_I:.*]] unwind label %[[CATCH_DISPATCH15_I:.*]] +// CHECK: [[INVOKE_CONT8_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT9_I:.*]] unwind label %[[CATCH_DISPATCH10_I:.*]] +// CHECK: [[INVOKE_CONT9_I]]: +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: store i32 -1, ptr [[TMP_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @_CxxThrowException(ptr nonnull [[TMP_I]], ptr nonnull @_TI1H) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH10_I]] +// CHECK: [[CATCH_DISPATCH10_I]]: +// CHECK-NEXT: [[TMP5:%.*]] = catchswitch within none [label %[[CATCH11_I:.*]]] unwind label %[[CATCH_DISPATCH15_I]] +// CHECK: [[CATCH11_I]]: +// CHECK-NEXT: [[TMP6:%.*]] = catchpad within [[TMP5]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: invoke void @llvm.seh.scope.end() [ "funclet"(token [[TMP6]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT12_I:.*]] unwind label %[[CATCH_DISPATCH15_I]] +// CHECK: [[CATCH_DISPATCH15_I]]: +// CHECK-NEXT: [[TMP7:%.*]] = catchswitch within none [label %[[CATCH16_I:.*]]] unwind to caller +// CHECK: [[CATCH16_I]]: +// CHECK-NEXT: [[TMP8:%.*]] = catchpad within [[TMP7]] [ptr null, i32 0, ptr null] +// CHECK-NEXT: catchret from [[TMP8]] to label %[[TRY_CONT18_I:.*]] +// CHECK: [[TRY_CONT18_I]]: +// CHECK-NEXT: call void @"?Exit@@YAXXZ"() #[[ATTR8:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT12_I]]: +// CHECK-NEXT: catchret from [[TMP6]] to label %[[TRY_CONT18_I]] +// CHECK: [[INVOKE_CONT3_I]]: +// CHECK-NEXT: catchret from [[TMP2]] to label %[[TRY_CONT7_I]] +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@[email protected]" unwind label %[[CATCH_DISPATCH4_I]] +// CHECK: [[UNREACHABLE_I]]: +// CHECK-NEXT: unreachable +// CHECK: "?ExitOnThrow@@[email protected]": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +//. diff --git a/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp b/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp new file mode 100644 index 0000000000000..0ab117b6d2bf8 --- /dev/null +++ b/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp @@ -0,0 +1,91 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -O3 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s +// Check that the outermost __try scope containing a return statement is terminated upon inlining into main. +int AlwaysThrows(int); +[[noreturn]] void Exit(); +volatile int *p{nullptr}; + +int ExitOnThrow(int argc) noexcept +{ + __try { + __try { + if (!argc) { *p = 0; } + return argc; + } __except(1) {} + } __except(1) {} + + Exit(); + return 0; +} + +// CHECK-LABEL: define dso_local noundef i32 @main( +// CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef readnone captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] personality ptr @__C_specific_handler { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARGC_ADDR_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[__EXCEPTION_CODE1_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[CLEANUP_DEST_SLOT_I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[ARGC_ADDR_I]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[__EXCEPTION_CODE1_I]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[CLEANUP_DEST_SLOT_I]]) +// CHECK-NEXT: store i32 [[ARGC]], ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7:![0-9]+]] +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT_I:.*]] unwind label %[[CATCH_DISPATCH5_I:.*]] +// CHECK: [[INVOKE_CONT_I]]: +// CHECK-NEXT: invoke void @llvm.seh.try.begin() +// CHECK-NEXT: to label %[[INVOKE_CONT2_I:.*]] unwind label %[[CATCH_DISPATCH_I:.*]] +// CHECK: [[CATCH_DISPATCH_I]]: +// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[__EXCEPT_I:.*]]] unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: [[__EXCEPT_I]]: +// CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null] +// CHECK-NEXT: catchret from [[TMP2]] to label %[[__EXCEPT3_I:.*]] +// CHECK: [[__EXCEPT3_I]]: +// CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP2]]) +// CHECK-NEXT: store volatile i32 [[TMP3]], ptr [[__EXCEPTION_CODE1_I]], align 4 +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %[[__TRY_CONT8_I:.*]] unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: [[CATCH_DISPATCH5_I]]: +// CHECK-NEXT: [[TMP4:%.*]] = catchswitch within none [label %[[__EXCEPT6_I:.*]]] unwind to caller +// CHECK: [[__EXCEPT6_I]]: +// CHECK-NEXT: [[TMP5:%.*]] = catchpad within [[TMP4]] [ptr null] +// CHECK-NEXT: catchret from [[TMP5]] to label %[[__EXCEPT7_I:.*]] +// CHECK: [[__EXCEPT7_I]]: +// CHECK-NEXT: [[TMP6:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP5]]) +// CHECK-NEXT: br label %[[__TRY_CONT8_I]] +// CHECK: [[__TRY_CONT8_I]]: +// CHECK-NEXT: tail call void @"?Exit@@YAXXZ"() #[[ATTR7:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT2_I]]: +// CHECK-NEXT: [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I]], 0 +// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]] +// CHECK: [[IF_THEN_I]]: +// CHECK-NEXT: [[TMP7:%.*]] = load volatile ptr, ptr @"?p@@3PECHEC", align 8, !tbaa [[INTPTR_TBAA11:![0-9]+]] +// CHECK-NEXT: store volatile i32 0, ptr [[TMP7]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: br label %[[IF_END_I]] +// CHECK: [[IF_END_I]]: +// CHECK-NEXT: [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_10_I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7]] +// CHECK-NEXT: store volatile i32 1, ptr [[CLEANUP_DEST_SLOT_I]], align 4 +// CHECK-NEXT: invoke void @llvm.seh.try.end() +// CHECK-NEXT: to label %"?ExitOnThrow@@[email protected]" unwind label %[[CATCH_DISPATCH5_I]] +// CHECK: "?ExitOnThrow@@[email protected]": +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[ARGC_ADDR_I]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[__EXCEPTION_CODE1_I]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[CLEANUP_DEST_SLOT_I]]) +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_10_I]]) +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_10_I]] +// CHECK-NEXT: ret i32 [[ADD]] +// +int main(int argc, char**) +{ + auto data = ExitOnThrow(argc); + return data + AlwaysThrows(data); +} +//. +// CHECK: [[INT_TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META9:![0-9]+]], i64 0} +// CHECK: [[META9]] = !{!"omnipotent char", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"Simple C++ TBAA"} +// CHECK: [[INTPTR_TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"p1 int", [[META13:![0-9]+]], i64 0} +// CHECK: [[META13]] = !{!"any pointer", [[META9]], i64 0} +//. diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp index 66d29cb5d65e4..a291354427e49 100644 --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -341,9 +341,12 @@ void llvm::calculateSEHStateForAsynchEH(const BasicBlock *BB, int State, // Retrive the new State from seh_try_begin State = EHInfo.InvokeStateMap[cast<InvokeInst>(TI)]; else if (Fn && Fn->isIntrinsic() && - Fn->getIntrinsicID() == Intrinsic::seh_try_end) - // end of current state, retrive new state from UnwindMap + Fn->getIntrinsicID() == Intrinsic::seh_try_end) { + // end of current state, retrieve new state through InvokeStateMap and + // UnwindMap + State = EHInfo.InvokeStateMap[cast<InvokeInst>(TI)]; State = EHInfo.SEHUnwindMap[State].ToState; + } } // Continue push successors into worklist for (auto *SuccBB : successors(BB)) { diff --git a/llvm/test/CodeGen/WinEH/wineh-statenumbering-seh-try-end.ll b/llvm/test/CodeGen/WinEH/wineh-statenumbering-seh-try-end.ll new file mode 100644 index 0000000000000..c169ba5af4b53 --- /dev/null +++ b/llvm/test/CodeGen/WinEH/wineh-statenumbering-seh-try-end.ll @@ -0,0 +1,170 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc < %s | FileCheck %s +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-pc" + +@p = dso_local global ptr null, align 8 + +define dso_local noundef i32 @main(i32 noundef %argc, ptr noundef readnone captures(none) %argv) local_unnamed_addr personality ptr @__C_specific_handler { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pushq %rbp +; CHECK-NEXT: .seh_pushreg %rbp +; CHECK-NEXT: pushq %rsi +; CHECK-NEXT: .seh_pushreg %rsi +; CHECK-NEXT: subq $56, %rsp +; CHECK-NEXT: .seh_stackalloc 56 +; CHECK-NEXT: leaq {{[0-9]+}}(%rsp), %rbp +; CHECK-NEXT: .seh_setframe %rbp, 48 +; CHECK-NEXT: .seh_endprologue +; CHECK-NEXT: .Ltmp0: # EH_LABEL +; CHECK-NEXT: nop +; CHECK-NEXT: movl %ecx, 4(%rbp) +; CHECK-NEXT: .Ltmp1: # EH_LABEL +; CHECK-NEXT: # %bb.1: # %invoke.cont2.i +; CHECK-NEXT: .Ltmp6: # EH_LABEL +; CHECK-NEXT: nop +; CHECK-NEXT: cmpl $0, 4(%rbp) +; CHECK-NEXT: .Ltmp7: # EH_LABEL +; CHECK-NEXT: jne .LBB0_3 +; CHECK-NEXT: # %bb.2: # %if.then.i +; CHECK-NEXT: .Ltmp8: # EH_LABEL +; CHECK-NEXT: nop +; CHECK-NEXT: movq p(%rip), %rax +; CHECK-NEXT: movl $1, (%rax) +; CHECK-NEXT: .Ltmp9: # EH_LABEL +; CHECK-NEXT: .LBB0_3: # %if.end.i +; CHECK-NEXT: .Ltmp10: # EH_LABEL +; CHECK-NEXT: nop +; CHECK-NEXT: movl 4(%rbp), %esi +; CHECK-NEXT: movl $1, (%rbp) +; CHECK-NEXT: .Ltmp11: # EH_LABEL +; CHECK-NEXT: # %bb.4: # %"?ExitOnThrow@@[email protected]" +; CHECK-NEXT: .Ltmp12: # EH_LABEL +; CHECK-NEXT: leal 5(%rsi), %ecx +; CHECK-NEXT: callq "?AlwaysThrows@@YAHH@Z" +; CHECK-NEXT: # kill: def $eax killed $eax def $rax +; CHECK-NEXT: leal 5(%rax,%rsi), %eax +; CHECK-NEXT: .Ltmp13: # EH_LABEL +; CHECK-NEXT: .seh_startepilogue +; CHECK-NEXT: addq $56, %rsp +; CHECK-NEXT: popq %rsi +; CHECK-NEXT: popq %rbp +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: retq +; CHECK-NEXT: .LBB0_5: # %__except.i +; CHECK-NEXT: $ehgcr_0_5: +; CHECK-NEXT: .Ltmp2: # EH_LABEL +; CHECK-NEXT: nop +; CHECK-NEXT: movl %eax, -4(%rbp) +; CHECK-NEXT: .Ltmp3: # EH_LABEL +; CHECK-NEXT: jmp .LBB0_7 +; CHECK-NEXT: .LBB0_6: # %__except6.i +; CHECK-NEXT: $ehgcr_0_6: +; CHECK-NEXT: .LBB0_7: # %__try.cont8.i +; CHECK-NEXT: .Ltmp4: # EH_LABEL +; CHECK-NEXT: callq Exit +; CHECK-NEXT: int3 +; CHECK-NEXT: .Ltmp5: # EH_LABEL +; CHECK-NEXT: .Lfunc_end0: +; CHECK-NEXT: .seh_handlerdata +; CHECK-NEXT: .Lmain$parent_frame_offset = 48 +; CHECK-NEXT: .long (.Llsda_end0-.Llsda_begin0)/16 # Number of call sites +; CHECK-NEXT: .Llsda_begin0: +; CHECK-NEXT: .long .Ltmp6@IMGREL # LabelStart +; CHECK-NEXT: .long .Ltmp11@IMGREL # LabelEnd +; CHECK-NEXT: .long 1 # CatchAll +; CHECK-NEXT: .long .LBB0_5@IMGREL # ExceptionHandler +; CHECK-NEXT: .long .Ltmp6@IMGREL # LabelStart +; CHECK-NEXT: .long .Ltmp11@IMGREL # LabelEnd +; CHECK-NEXT: .long 1 # CatchAll +; CHECK-NEXT: .long .LBB0_6@IMGREL # ExceptionHandler +; CHECK-NEXT: .long .Ltmp2@IMGREL # LabelStart +; CHECK-NEXT: .long .Ltmp3@IMGREL # LabelEnd +; CHECK-NEXT: .long 1 # CatchAll +; CHECK-NEXT: .long .LBB0_6@IMGREL # ExceptionHandler +; CHECK-NEXT: .Llsda_end0: +; CHECK-NEXT: .text +; CHECK-NEXT: .seh_endproc +entry: + %argc.addr.i = alloca i32, align 4 + %__exception_code1.i = alloca i32, align 4 + %cleanup.dest.slot.i = alloca i32, align 4 + call void @llvm.lifetime.start.p0(ptr nonnull %argc.addr.i) + call void @llvm.lifetime.start.p0(ptr nonnull %__exception_code1.i) + call void @llvm.lifetime.start.p0(ptr nonnull %cleanup.dest.slot.i) + store i32 %argc, ptr %argc.addr.i, align 4 + invoke void @llvm.seh.try.begin() + to label %invoke.cont.i unwind label %catch.dispatch5.i + +invoke.cont.i: ; preds = %entry + invoke void @llvm.seh.try.begin() + to label %invoke.cont2.i unwind label %catch.dispatch.i + +catch.dispatch.i: ; preds = %invoke.cont.i + %0 = catchswitch within none [label %__except.i] unwind label %catch.dispatch5.i + +__except.i: ; preds = %catch.dispatch.i + %1 = catchpad within %0 [ptr null] + catchret from %1 to label %__except3.i + +__except3.i: ; preds = %__except.i + %2 = tail call i32 @llvm.eh.exceptioncode(token %1) + store volatile i32 %2, ptr %__exception_code1.i, align 4 + invoke void @llvm.seh.try.end() + to label %__try.cont8.i unwind label %catch.dispatch5.i + +catch.dispatch5.i: ; preds = %if.end.i, %__except3.i, %catch.dispatch.i, %entry + %3 = catchswitch within none [label %__except6.i] unwind to caller + +__except6.i: ; preds = %catch.dispatch5.i + %4 = catchpad within %3 [ptr null] + catchret from %4 to label %__except7.i + +__except7.i: ; preds = %__except6.i + %5 = tail call i32 @llvm.eh.exceptioncode(token %4) + br label %__try.cont8.i + +__try.cont8.i: ; preds = %__except7.i, %__except3.i + tail call void @Exit() #0 + unreachable + +invoke.cont2.i: ; preds = %invoke.cont.i + %argc.addr.i.0.argc.addr.i.0.argc.addr.i.0.argc.addr.0.argc.addr.0.argc.addr.0..i = load volatile i32, ptr %argc.addr.i, align 4 + %tobool.not.i = icmp eq i32 %argc.addr.i.0.argc.addr.i.0.argc.addr.i.0.argc.addr.0.argc.addr.0.argc.addr.0..i, 0 + br i1 %tobool.not.i, label %if.then.i, label %if.end.i + +if.then.i: ; preds = %invoke.cont2.i + %6 = load volatile ptr, ptr @p, align 8 + store atomic volatile i32 1, ptr %6 release, align 4 + br label %if.end.i + +if.end.i: ; preds = %if.then.i, %invoke.cont2.i + %argc.addr.i.0.argc.addr.i.0.argc.addr.i.0.argc.addr.0.argc.addr.0.argc.addr.0.10.i = load volatile i32, ptr %argc.addr.i, align 4 + store volatile i32 1, ptr %cleanup.dest.slot.i, align 4 + invoke void @llvm.seh.try.end() + to label %"?ExitOnThrow@@[email protected]" unwind label %catch.dispatch5.i + +"?ExitOnThrow@@[email protected]": ; preds = %if.end.i + %add.i = add nsw i32 %argc.addr.i.0.argc.addr.i.0.argc.addr.i.0.argc.addr.0.argc.addr.0.argc.addr.0.10.i, 5 + call void @llvm.lifetime.end.p0(ptr nonnull %argc.addr.i) + call void @llvm.lifetime.end.p0(ptr nonnull %__exception_code1.i) + call void @llvm.lifetime.end.p0(ptr nonnull %cleanup.dest.slot.i) + %call1 = tail call noundef i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef %add.i) + %add = add nsw i32 %call1, %add.i + ret i32 %add +} + +declare void @Exit() +declare i32 @"?AlwaysThrows@@YAHH@Z"(i32 noundef %id) +declare dso_local void @llvm.seh.try.begin() +declare dso_local i32 @__C_specific_handler(...) +declare i32 @llvm.eh.exceptioncode(token) +declare dso_local void @llvm.seh.try.end() +declare void @llvm.lifetime.start.p0(ptr captures(none)) +declare void @llvm.lifetime.end.p0(ptr captures(none)) + +attributes #0 = { noreturn nounwind } + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"eh-asynch", i32 1} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
