[clang] [llvm] [WinEH] Fix try scopes leaking to caller on inline (PR #167176)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Dec 7 04:30:18 PST 2025
https://github.com/MuellerMP updated https://github.com/llvm/llvm-project/pull/167176
>From 21895c963677c644b365b7dd3ad5497608e6944a Mon Sep 17 00:00:00 2001
From: MuellerMP <mirkomueller97 at live.de>
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 | 11 +-
clang/lib/CodeGen/CGException.cpp | 57 ++++-
clang/lib/CodeGen/CodeGenFunction.h | 2 +-
.../test/CodeGen/windows-seh-EHa-Inline1.cpp | 62 +++++
.../test/CodeGen/windows-seh-EHa-Inline2.cpp | 121 +++++++++
.../test/CodeGen/windows-seh-EHa-Inline3.cpp | 111 +++++++++
llvm/lib/CodeGen/WinEHPrepare.cpp | 2 +-
.../WinEH/wineh-statenumbering-seh-try-end.ll | 235 ++++++++++++++++++
8 files changed, 588 insertions(+), 13 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..6d235e289428d 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,11 @@ void CodeGenFunction::EmitSehTryScopeBegin() {
}
// Invoke a llvm.seh.try.end at the end of a SEH scope for -EHa
-void CodeGenFunction::EmitSehTryScopeEnd() {
+void CodeGenFunction::EmitSehTryScopeEnd(llvm::BasicBlock *InvokeDest) {
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);
+ EmitSehScope(*this, SehCppScope, InvokeDest);
}
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index e9d20672ce185..9bfffcf2c15b2 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -635,14 +635,30 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) {
ExitCXXTryStmt(S);
}
+struct TerminateTryScope final : EHScopeStack::Cleanup {
+ llvm::BasicBlock *InvokeDest;
+
+ TerminateTryScope(llvm::BasicBlock *BB) : InvokeDest(BB) {}
+
+ void Emit(CodeGenFunction &CGF, Flags flags) override {
+ CGF.EmitSehTryScopeEnd(InvokeDest);
+ }
+};
+
+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::BasicBlock *DispatchBlock = nullptr;
+ 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 +676,32 @@ 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});
+ if (HasEHa) {
+ DispatchBlock = createBasicBlock("catch.dispatch");
+ // Push a terminator for the created SEH __try scope
+ EHStack.pushCleanup<TerminateTryScope>(NormalCleanup, DispatchBlock);
+ }
+ }
+ }
+
+ 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();
+ auto &Scope = *EHStack.find(EHStack.getInnermostEHScope());
+ Scope.setCachedEHDispatchBlock(DispatchBlock);
+ EmitSehTryScopeBegin();
}
}
}
@@ -2186,7 +2220,16 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
// Otherwise, we must have an __except block.
const SEHExceptStmt *Except = S.getExceptHandler();
assert(Except);
+
+ auto *DispatchBlock = createBasicBlock("catch.dispatch");
+ if (getLangOpts().EHAsynch) {
+ EHStack.pushCleanup<TerminateTryScope>(NormalCleanup, DispatchBlock);
+ }
+
EHCatchScope *CatchScope = EHStack.pushCatch(1);
+ auto &Scope = *EHStack.find(EHStack.getInnermostEHScope());
+ Scope.setCachedEHDispatchBlock(DispatchBlock);
+
SEHCodeSlotStack.push_back(
CreateMemTemp(getContext().IntTy, "__exception_code"));
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 8c4c1c8c2dc95..4574adf264ab9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3241,7 +3241,7 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitSehCppScopeBegin();
void EmitSehCppScopeEnd();
void EmitSehTryScopeBegin();
- void EmitSehTryScopeEnd();
+ void EmitSehTryScopeEnd(llvm::BasicBlock *InvokeDest = nullptr);
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@@YAHH at Z.exit" unwind label %[[CATCH_DISPATCH_I]]
+// CHECK: [[UNREACHABLE_I]]:
+// CHECK-NEXT: unreachable
+// CHECK: "?ExitOnThrow@@YAHH at Z.exit":
+// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]])
+// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 @"?AlwaysThrows@@YAHH at 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..200ddc67a4c26
--- /dev/null
+++ b/clang/test/CodeGen/windows-seh-EHa-Inline2.cpp
@@ -0,0 +1,121 @@
+// 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_DISPATCH6_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_CONT_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+// CHECK: [[CATCH_DISPATCH_I]]:
+// CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %[[CATCH_I:.*]]] unwind label %[[CATCH_DISPATCH6_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_DISPATCH6_I]]
+// CHECK: [[INVOKE_CONT3_I]]:
+// CHECK-NEXT: catchret from [[TMP2]] to label %[[TRY_CONT_I]]
+// CHECK: [[TRY_CONT_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.scope.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT4_I:.*]] unwind label %[[CATCH_DISPATCH6_I]]
+// CHECK: [[CATCH_DISPATCH6_I]]:
+// CHECK-NEXT: [[TMP3:%.*]] = catchswitch within none [label %[[CATCH7_I:.*]]] unwind to caller
+// CHECK: [[CATCH7_I]]:
+// CHECK-NEXT: [[TMP4:%.*]] = catchpad within [[TMP3]] [ptr null, i32 0, ptr null]
+// CHECK-NEXT: catchret from [[TMP4]] to label %[[TRY_CONT9_I:.*]]
+// CHECK: [[TRY_CONT9_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.begin()
+// CHECK-NEXT: to label %[[INVOKE_CONT10_I:.*]] unwind label %[[CATCH_DISPATCH19_I:.*]]
+// CHECK: [[INVOKE_CONT10_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.begin()
+// CHECK-NEXT: to label %[[INVOKE_CONT11_I:.*]] unwind label %[[CATCH_DISPATCH12_I:.*]]
+// CHECK: [[INVOKE_CONT11_I]]:
+// CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0
+// CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I:.*]], label %[[CLEANUP_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_DISPATCH12_I]]
+// CHECK: [[CATCH_DISPATCH12_I]]:
+// CHECK-NEXT: [[TMP5:%.*]] = catchswitch within none [label %[[CATCH13_I:.*]]] unwind label %[[CATCH_DISPATCH19_I]]
+// CHECK: [[CATCH13_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_CONT14_I:.*]] unwind label %[[CATCH_DISPATCH19_I]]
+// CHECK: [[INVOKE_CONT14_I]]:
+// CHECK-NEXT: catchret from [[TMP6]] to label %[[CLEANUP_I]]
+// CHECK: [[CLEANUP_I]]:
+// CHECK-NEXT: [[COND_I:%.*]] = phi i1 [ true, %[[INVOKE_CONT14_I]] ], [ false, %[[INVOKE_CONT11_I]] ]
+// CHECK-NEXT: invoke void @llvm.seh.scope.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT17_I:.*]] unwind label %[[CATCH_DISPATCH19_I]]
+// CHECK: [[CATCH_DISPATCH19_I]]:
+// CHECK-NEXT: [[TMP7:%.*]] = catchswitch within none [label %[[CATCH20_I:.*]]] unwind to caller
+// CHECK: [[CATCH20_I]]:
+// CHECK-NEXT: [[TMP8:%.*]] = catchpad within [[TMP7]] [ptr null, i32 0, ptr null]
+// CHECK-NEXT: catchret from [[TMP8]] to label %[[TRY_CONT22_I:.*]]
+// CHECK: [[TRY_CONT22_I]]:
+// CHECK-NEXT: call void @"?Exit@@YAXXZ"() #[[ATTR8:[0-9]+]]
+// CHECK-NEXT: unreachable
+// CHECK: [[INVOKE_CONT17_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT18_I:.*]] unwind label %[[CATCH_DISPATCH12_I]]
+// CHECK: [[INVOKE_CONT18_I]]:
+// CHECK-NEXT: br i1 [[COND_I]], label %[[TRY_CONT22_I]], label %[[CLEANUP23_I:.*]]
+// CHECK: [[INVOKE_CONT4_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[TRY_CONT9_I]] unwind label %[[CATCH_DISPATCH_I]]
+// CHECK: [[CLEANUP23_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT24_I:.*]] unwind label %[[CATCH_DISPATCH19_I]]
+// CHECK: [[INVOKE_CONT24_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %"?ExitOnThrow@@YAHH at Z.exit" unwind label %[[CATCH_DISPATCH6_I]]
+// CHECK: [[UNREACHABLE_I]]:
+// CHECK-NEXT: unreachable
+// CHECK: "?ExitOnThrow@@YAHH at Z.exit":
+// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TMP_I]])
+// CHECK-NEXT: [[CALL1:%.*]] = call noundef i32 @"?AlwaysThrows@@YAHH at 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..c224f95c30119
--- /dev/null
+++ b/clang/test/CodeGen/windows-seh-EHa-Inline3.cpp
@@ -0,0 +1,111 @@
+// 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: [[RETVAL_I:%.*]] = alloca i32, align 4
+// 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 [[RETVAL_I]])
+// 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_DISPATCH7_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_DISPATCH7_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: br label %[[CLEANUP_I:.*]]
+// CHECK: [[CLEANUP_I]]:
+// CHECK-NEXT: [[DOTSINK_I:%.*]] = phi i32 [ 0, %[[__EXCEPT3_I]] ], [ 1, %[[IF_END_I:.*]] ]
+// CHECK-NEXT: store volatile i32 [[DOTSINK_I]], ptr [[CLEANUP_DEST_SLOT_I]], align 4
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT4_I:.*]] unwind label %[[CATCH_DISPATCH7_I]]
+// CHECK: [[INVOKE_CONT4_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[INVOKE_CONT5_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+// CHECK: [[INVOKE_CONT5_I]]:
+// CHECK-NEXT: [[CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_I:%.*]] = load volatile i32, ptr [[CLEANUP_DEST_SLOT_I]], align 4
+// CHECK-NEXT: [[COND_I:%.*]] = icmp eq i32 [[CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_I_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_SLOT_0_CLEANUP_DEST_I]], 0
+// CHECK-NEXT: br i1 [[COND_I]], label %[[CLEANUP_CONT_I:.*]], label %[[CLEANUP11_I:.*]]
+// CHECK: [[CLEANUP_CONT_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %[[__TRY_CONT10_I:.*]] unwind label %[[CATCH_DISPATCH7_I]]
+// CHECK: [[CATCH_DISPATCH7_I]]:
+// CHECK-NEXT: [[TMP4:%.*]] = catchswitch within none [label %[[__EXCEPT8_I:.*]]] unwind to caller
+// CHECK: [[__EXCEPT8_I]]:
+// CHECK-NEXT: [[TMP5:%.*]] = catchpad within [[TMP4]] [ptr null]
+// CHECK-NEXT: catchret from [[TMP5]] to label %[[__EXCEPT9_I:.*]]
+// CHECK: [[__EXCEPT9_I]]:
+// CHECK-NEXT: [[TMP6:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP5]])
+// CHECK-NEXT: br label %[[__TRY_CONT10_I]]
+// CHECK: [[__TRY_CONT10_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_14_I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4, !tbaa [[INT_TBAA7]]
+// CHECK-NEXT: store volatile i32 [[ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_I_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_14_I]], ptr [[RETVAL_I]], align 4
+// CHECK-NEXT: br label %[[CLEANUP_I]]
+// CHECK: [[CLEANUP11_I]]:
+// CHECK-NEXT: invoke void @llvm.seh.try.end()
+// CHECK-NEXT: to label %"?ExitOnThrow@@YAHH at Z.exit" unwind label %[[CATCH_DISPATCH7_I]]
+// CHECK: "?ExitOnThrow@@YAHH at Z.exit":
+// CHECK-NEXT: [[RETVAL_I_0_RETVAL_I_0_RETVAL_I_0_RETVAL_0_RETVAL_0_RETVAL_0__I:%.*]] = load i32, ptr [[RETVAL_I]], align 4
+// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[RETVAL_I]])
+// 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 at Z"(i32 noundef [[RETVAL_I_0_RETVAL_I_0_RETVAL_I_0_RETVAL_0_RETVAL_0_RETVAL_0__I]])
+// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[RETVAL_I_0_RETVAL_I_0_RETVAL_I_0_RETVAL_0_RETVAL_0_RETVAL_0__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..c1fd9720f8478 100644
--- a/llvm/lib/CodeGen/WinEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WinEHPrepare.cpp
@@ -341,7 +341,7 @@ 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)
+ Fn->getIntrinsicID() == Intrinsic::seh_try_end && State >= 0)
// end of current state, retrive new state from UnwindMap
State = EHInfo.SEHUnwindMap[State].ToState;
}
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..c9fc7b94ebd97
--- /dev/null
+++ b/llvm/test/CodeGen/WinEH/wineh-statenumbering-seh-try-end.ll
@@ -0,0 +1,235 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s | FileCheck %s
+
+; Generated from the following c code with clang-cl /EHa /O2 and reduction of the ir:
+; #include "stdnoreturn.h"
+; int AlwaysThrows(int id);
+; noreturn void Exit();
+; volatile int* p = 0;
+; int ExitOnThrow(int argc) {
+; __try {
+; __try {
+; if (!argc) { *p = 1; }
+; return argc+ 5;
+; } __except(1) {}
+; } __except(1) {}
+; Exit();
+; return 0;
+; }
+; int main(int argc, char** argv) {
+; int data = ExitOnThrow(argc);
+; return data + AlwaysThrows(data);
+; }
+
+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"
+
+ at 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: .Ltmp10: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: cmpl $0, 4(%rbp)
+; CHECK-NEXT: .Ltmp11: # EH_LABEL
+; CHECK-NEXT: jne .LBB0_3
+; CHECK-NEXT: # %bb.2: # %if.then.i
+; CHECK-NEXT: .Ltmp12: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: movq p(%rip), %rax
+; CHECK-NEXT: movl $1, (%rax)
+; CHECK-NEXT: .Ltmp13: # EH_LABEL
+; CHECK-NEXT: .LBB0_3: # %if.end.i
+; CHECK-NEXT: .Ltmp14: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: movl 4(%rbp), %eax
+; CHECK-NEXT: addl $5, %eax
+; CHECK-NEXT: movl %eax, -4(%rbp)
+; CHECK-NEXT: movl $1, %eax
+; CHECK-NEXT: .Ltmp15: # EH_LABEL
+; CHECK-NEXT: .LBB0_5: # %cleanup.i
+; CHECK-NEXT: .Ltmp4: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: movl %eax, (%rbp)
+; CHECK-NEXT: .Ltmp5: # EH_LABEL
+; CHECK-NEXT: # %bb.6: # %invoke.cont5.i
+; CHECK-NEXT: .Ltmp6: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: cmpl $0, (%rbp)
+; CHECK-NEXT: .Ltmp7: # EH_LABEL
+; CHECK-NEXT: je .LBB0_9
+; CHECK-NEXT: # %bb.7: # %ExitOnThrow.exit
+; CHECK-NEXT: .Ltmp16: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: movl -4(%rbp), %esi
+; CHECK-NEXT: movl %esi, %ecx
+; CHECK-NEXT: callq AlwaysThrows
+; CHECK-NEXT: addl %esi, %eax
+; CHECK-NEXT: .Ltmp17: # 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_4: # %__except.i
+; CHECK-NEXT: $ehgcr_0_4:
+; CHECK-NEXT: .Ltmp2: # EH_LABEL
+; CHECK-NEXT: nop
+; CHECK-NEXT: movl %eax, -8(%rbp)
+; CHECK-NEXT: xorl %eax, %eax
+; CHECK-NEXT: .Ltmp3: # EH_LABEL
+; CHECK-NEXT: jmp .LBB0_5
+; CHECK-NEXT: .LBB0_8: # %__except8.i
+; CHECK-NEXT: $ehgcr_0_8:
+; CHECK-NEXT: .LBB0_9: # %__try.cont10.i
+; CHECK-NEXT: .Ltmp8: # EH_LABEL
+; CHECK-NEXT: callq Exit
+; CHECK-NEXT: int3
+; CHECK-NEXT: .Ltmp9: # 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 .Ltmp10 at IMGREL # LabelStart
+; CHECK-NEXT: .long .Ltmp15 at IMGREL # LabelEnd
+; CHECK-NEXT: .long 1 # CatchAll
+; CHECK-NEXT: .long .LBB0_4 at IMGREL # ExceptionHandler
+; CHECK-NEXT: .long .Ltmp10 at IMGREL # LabelStart
+; CHECK-NEXT: .long .Ltmp15 at IMGREL # LabelEnd
+; CHECK-NEXT: .long 1 # CatchAll
+; CHECK-NEXT: .long .LBB0_8 at IMGREL # ExceptionHandler
+; CHECK-NEXT: .long .Ltmp4 at IMGREL # LabelStart
+; CHECK-NEXT: .long .Ltmp5 at IMGREL # LabelEnd
+; CHECK-NEXT: .long 1 # CatchAll
+; CHECK-NEXT: .long .LBB0_8 at IMGREL # ExceptionHandler
+; CHECK-NEXT: .long .Ltmp2 at IMGREL # LabelStart
+; CHECK-NEXT: .long .Ltmp3 at IMGREL # LabelEnd
+; CHECK-NEXT: .long 1 # CatchAll
+; CHECK-NEXT: .long .LBB0_8 at IMGREL # ExceptionHandler
+; CHECK-NEXT: .Llsda_end0:
+; CHECK-NEXT: .text
+; CHECK-NEXT: .seh_endproc
+entry:
+ %retval.i = alloca i32, align 4
+ %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 %retval.i)
+ 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.dispatch7.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.cont4.i, %invoke.cont.i
+ %0 = catchswitch within none [label %__except.i] unwind label %catch.dispatch7.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
+ br label %cleanup.i
+
+cleanup.i: ; preds = %if.end.i, %__except3.i
+ %.sink.i = phi i32 [ 0, %__except3.i ], [ 1, %if.end.i ]
+ store volatile i32 %.sink.i, ptr %cleanup.dest.slot.i, align 4
+ invoke void @llvm.seh.try.end()
+ to label %invoke.cont4.i unwind label %catch.dispatch7.i
+
+invoke.cont4.i: ; preds = %cleanup.i
+ invoke void @llvm.seh.try.end()
+ to label %invoke.cont5.i unwind label %catch.dispatch.i
+
+invoke.cont5.i: ; preds = %invoke.cont4.i
+ %cleanup.dest.slot.i.0.cleanup.dest.slot.i.0.cleanup.dest.slot.i.0.cleanup.dest.slot.0.cleanup.dest.slot.0.cleanup.dest.slot.0.cleanup.dest.i = load volatile i32, ptr %cleanup.dest.slot.i, align 4
+ %cond.i = icmp eq i32 %cleanup.dest.slot.i.0.cleanup.dest.slot.i.0.cleanup.dest.slot.i.0.cleanup.dest.slot.0.cleanup.dest.slot.0.cleanup.dest.slot.0.cleanup.dest.i, 0
+ br i1 %cond.i, label %cleanup.cont.i, label %cleanup11.i
+
+cleanup.cont.i: ; preds = %invoke.cont5.i
+ invoke void @llvm.seh.try.end()
+ to label %__try.cont10.i unwind label %catch.dispatch7.i
+
+catch.dispatch7.i: ; preds = %cleanup11.i, %cleanup.cont.i, %cleanup.i, %catch.dispatch.i, %entry
+ %3 = catchswitch within none [label %__except8.i] unwind to caller
+
+__except8.i: ; preds = %catch.dispatch7.i
+ %4 = catchpad within %3 [ptr null]
+ catchret from %4 to label %__except9.i
+
+__except9.i: ; preds = %__except8.i
+ %5 = tail call i32 @llvm.eh.exceptioncode(token %4)
+ br label %__try.cont10.i
+
+__try.cont10.i: ; preds = %__except9.i, %cleanup.cont.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.14.i = load volatile i32, ptr %argc.addr.i, align 4
+ %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.14.i, 5
+ store volatile i32 %add.i, ptr %retval.i, align 4
+ br label %cleanup.i
+
+cleanup11.i: ; preds = %invoke.cont5.i
+ invoke void @llvm.seh.try.end()
+ to label %ExitOnThrow.exit unwind label %catch.dispatch7.i
+
+ExitOnThrow.exit: ; preds = %cleanup11.i
+ %retval.i.0.retval.i.0.retval.i.0.retval.0.retval.0.retval.0..i = load i32, ptr %retval.i, align 4
+ call void @llvm.lifetime.end.p0(ptr nonnull %retval.i)
+ 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 i32 @AlwaysThrows(i32 noundef %retval.i.0.retval.i.0.retval.i.0.retval.0.retval.0.retval.0..i)
+ %add = add nsw i32 %call1, %retval.i.0.retval.i.0.retval.i.0.retval.0.retval.0.retval.0..i
+ ret i32 %add
+}
+
+declare void @Exit()
+declare i32 @AlwaysThrows(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}
More information about the llvm-commits
mailing list