[llvm] [Inline][WinEH] Fix try scopes leaking to caller on inline (PR #164170)

via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 22 01:58:59 PDT 2025


https://github.com/MuellerMP updated https://github.com/llvm/llvm-project/pull/164170

>From 3d05e772bd28f7ebf8cba2c429a9a2dd84ee21a7 Mon Sep 17 00:00:00 2001
From: MuellerMP <mirkomueller97 at live.de>
Date: Wed, 22 Oct 2025 10:58:36 +0200
Subject: [PATCH] [Inline][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.
---
 llvm/lib/Transforms/Utils/InlineFunction.cpp  | 150 +++++++++++++
 .../Inline/eha-try-fixup-multiple-scopes.ll   | 207 ++++++++++++++++++
 .../Transforms/Inline/eha-try-fixup-phis.ll   | 119 ++++++++++
 .../Transforms/Inline/eha-try-fixup-seh.ll    | 154 +++++++++++++
 llvm/test/Transforms/Inline/eha-try-fixup.ll  | 106 +++++++++
 5 files changed, 736 insertions(+)
 create mode 100644 llvm/test/Transforms/Inline/eha-try-fixup-multiple-scopes.ll
 create mode 100644 llvm/test/Transforms/Inline/eha-try-fixup-phis.ll
 create mode 100644 llvm/test/Transforms/Inline/eha-try-fixup-seh.ll
 create mode 100644 llvm/test/Transforms/Inline/eha-try-fixup.ll

diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index f49fbf8807bac..a4ebd992f621a 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -21,6 +21,7 @@
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/CFG.h"
 #include "llvm/Analysis/CallGraph.h"
 #include "llvm/Analysis/CaptureTracking.h"
 #include "llvm/Analysis/CtxProfAnalysis.h"
@@ -2210,6 +2211,147 @@ inlineRetainOrClaimRVCalls(CallBase &CB, objcarc::ARCInstKind RVCallKind,
   }
 }
 
+struct SehScope {
+  llvm::InvokeInst *Begin;
+  llvm::InvokeInst *End;
+};
+
+// Determine SEH try scope begins and ends.
+static SmallVector<SehScope, 1> GetSehTryScopes(Function *Func) {
+  SmallVector<SehScope, 1> Scopes{};
+  DenseMap<llvm::BasicBlock *, llvm::InvokeInst *> ScopeEnds{};
+
+  for (auto &BB : *Func) {
+    auto *TI = BB.getTerminator();
+    if (auto *II = dyn_cast<InvokeInst>(TI)) {
+      auto *Call = cast<CallBase>(II);
+      const auto *Fn = Call->getCalledFunction();
+      if (!Fn || !Fn->isIntrinsic())
+        continue;
+
+      if (Fn->getIntrinsicID() == Intrinsic::seh_try_end) {
+        ScopeEnds[II->getUnwindDest()] = II;
+      } else if (Fn->getIntrinsicID() == Intrinsic::seh_try_begin) {
+        Scopes.push_back({II, nullptr});
+      }
+    }
+  }
+
+  // Assign scope end to begin if the unwind destination matches.
+  for (auto &Scope : Scopes) {
+    auto ScopeEndIt = ScopeEnds.find(Scope.Begin->getUnwindDest());
+    if (ScopeEndIt != ScopeEnds.end())
+      Scope.End = ScopeEndIt->second;
+  }
+
+  return Scopes;
+}
+
+// Find, if present, the outermost unterminated try scope for the input block.
+static llvm::InvokeInst *
+GetOutermostUnterminatedTryScopeBegin(llvm::BasicBlock *ReturnBlock,
+                                      SmallVector<SehScope, 1> &Scopes) {
+  llvm::InvokeInst *OutermostScope{nullptr};
+  DominatorTree DT;
+  DT.recalculate(*ReturnBlock->getParent());
+
+  for (auto &Scope : Scopes) {
+    auto *Invoke = Scope.Begin;
+
+    // Return might not be in a try scope.
+    if (!DT.dominates(Invoke->getParent(), ReturnBlock))
+      continue;
+
+    // If there is a catch which connects to the return, the try scope is
+    // considered to be terminated.
+    if (isPotentiallyReachable(Invoke->getUnwindDest(), ReturnBlock, nullptr,
+                               &DT))
+      continue;
+
+    // For SEH there can be try scope ends on invokes and finally statements.
+    if (Scope.End && isPotentiallyReachable(Scope.End->getParent(), ReturnBlock,
+                                            nullptr, &DT))
+      continue;
+
+    // Keep the outermost scope if we match multiple ones.
+    if (OutermostScope) {
+      if (!DT.dominates(Invoke->getParent(), OutermostScope->getParent()))
+        continue;
+    }
+
+    OutermostScope = Invoke;
+  }
+
+  return OutermostScope;
+}
+
+struct UnterminatedTryScope {
+  llvm::BasicBlock *ReturnBlock;
+  llvm::BasicBlock *TryStart;
+  llvm::BasicBlock *UnwindDestination;
+};
+
+// For Windows -EHa there may be the case of try scopes spanning over a return
+// instruction. If no other return out of the function is given (e.g. by letting
+// all other blocks terminate with unreachable) the try scope is considered
+// unterminated upon inlining. To avoid leaking the try scopes over to a caller
+// we need to add a terminator for the outermost unterminated try scope.
+static SmallVector<UnterminatedTryScope, 1>
+GetUnterminatedTryScopes(Function *CalledFunc) {
+  SmallVector<UnterminatedTryScope, 1> UnterminatedScopes;
+  auto Scopes = GetSehTryScopes(CalledFunc);
+  if (Scopes.empty())
+    return UnterminatedScopes;
+
+  SmallVector<ReturnInst *, 8> Returns;
+  for (auto &BB : *CalledFunc)
+    if (ReturnInst *RI = dyn_cast<ReturnInst>(BB.getTerminator()))
+      Returns.push_back(RI);
+
+  for (auto *RI : Returns) {
+    auto *Block = RI->getParent();
+    auto *OutermostScope = GetOutermostUnterminatedTryScopeBegin(Block, Scopes);
+    if (!OutermostScope)
+      continue;
+
+    UnterminatedScopes.push_back(
+        {Block, OutermostScope->getParent(), OutermostScope->getUnwindDest()});
+  }
+
+  return UnterminatedScopes;
+}
+
+// Insert terminator for unterminated try scopes.
+static void HandleUnterminatedTryScopes(
+    Function *Caller,
+    SmallVector<UnterminatedTryScope, 1> &UnterminatedTryScopes,
+    ValueToValueMapTy &VMap) {
+  for (auto &Scope : UnterminatedTryScopes) {
+    auto *ReturnBlock = cast<BasicBlock>(VMap[Scope.ReturnBlock]);
+    auto *TryStart = cast<BasicBlock>(VMap[Scope.TryStart]);
+    auto *UnwindDestination = cast<BasicBlock>(VMap[Scope.UnwindDestination]);
+    // did not survive (partial) inlining - ignore
+    if (!ReturnBlock || !TryStart || !UnwindDestination)
+      continue;
+
+    auto *Mod = (llvm::Module *)Caller->getParent();
+    auto *SehTryEndFn =
+        Intrinsic::getOrInsertDeclaration(Mod, Intrinsic::seh_try_end);
+    auto *BB = ReturnBlock->splitBasicBlockBefore(ReturnBlock->getTerminator(),
+                                                  "try.end");
+    BB->getTerminator()->eraseFromParent();
+    IRBuilder<>(BB).CreateInvoke(SehTryEndFn, ReturnBlock, UnwindDestination);
+
+    for (auto &Insn : *UnwindDestination) {
+      if (auto *Phi = dyn_cast<PHINode>(&Insn)) {
+        auto *ValType = Phi->getIncomingValueForBlock(TryStart)->getType();
+        auto *Val = PoisonValue::get(ValType);
+        Phi->addIncoming(Val, BB);
+      }
+    }
+  }
+}
+
 // In contextual profiling, when an inline succeeds, we want to remap the
 // indices of the callee into the index space of the caller. We can't just leave
 // them as-is because the same callee may appear in other places in this caller
@@ -2625,6 +2767,7 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
   SmallVector<ReturnInst*, 8> Returns;
   ClonedCodeInfo InlinedFunctionInfo;
   Function::iterator FirstNewBlock;
+  SmallVector<UnterminatedTryScope, 1> UnterminatedTryScopes;
 
   // GC poses two hazards to inlining, which only occur when the callee has GC:
   //  1. If the caller has no GC, then the callee's GC must be propagated to the
@@ -2648,6 +2791,11 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
       assert(Caller->getPersonalityFn()->stripPointerCasts() ==
                  CalledPersonality &&
              "CanInlineCallSite should have verified compatible personality");
+
+    const llvm::Module *M = CalledFunc->getParent();
+    if (M->getModuleFlag("eh-asynch")) {
+      UnterminatedTryScopes = GetUnterminatedTryScopes(CalledFunc);
+    }
   }
 
   { // Scope to destroy VMap after cloning.
@@ -2837,6 +2985,8 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
         for (Instruction &I : NewBlock)
           if (auto *II = dyn_cast<AssumeInst>(&I))
             IFI.GetAssumptionCache(*Caller).registerAssumption(II);
+
+    HandleUnterminatedTryScopes(Caller, UnterminatedTryScopes, VMap);
   }
 
   if (IFI.ConvergenceControlToken) {
diff --git a/llvm/test/Transforms/Inline/eha-try-fixup-multiple-scopes.ll b/llvm/test/Transforms/Inline/eha-try-fixup-multiple-scopes.ll
new file mode 100644
index 0000000000000..21e88308a848f
--- /dev/null
+++ b/llvm/test/Transforms/Inline/eha-try-fixup-multiple-scopes.ll
@@ -0,0 +1,207 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+; Check that both unterminated try scopes of ExitOnThrow are terminated upon inlining into main.
+; The try scope of main should not get an additional terminator.
+
+define i32 @ExitOnThrow(i32 %argc) #0 personality ptr @__CxxFrameHandler3 {
+; CHECK-LABEL: define i32 @ExitOnThrow(
+; CHECK-SAME: i32 [[ARGC:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; 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:    [[CALL_I:%.*]] = invoke i32 @ThrowException()
+; CHECK-NEXT:            to label %[[TRY_CONT_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[CATCH_DISPATCH_I]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch.i] unwind to caller
+; CHECK:       [[CATCH_I:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[TRY_CONT_I]]
+; CHECK:       [[TRY_CONT_I]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT2_I:.*]] unwind label %[[CATCH_DISPATCH3:.*]]
+; CHECK:       [[INVOKE_CONT2_I]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT1:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[INVOKE_CONT1]]:
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[ARGC]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label %[[IF_THEN_I:.*]], label %[[IF_END_I:.*]]
+; CHECK:       [[IF_THEN_I]]:
+; CHECK-NEXT:    invoke void @ThrowException()
+; CHECK-NEXT:            to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH]]
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = catchswitch within none [label %catch] unwind label %[[CATCH_DISPATCH3]]
+; CHECK:       [[CATCH:.*:]]
+; CHECK-NEXT:    [[TMP3:%.*]] = catchpad within [[TMP2]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    invoke void @Exit() #[[ATTR0]] [ "funclet"(token [[TMP3]]) ]
+; CHECK-NEXT:            to label %[[INVOKE_CONT2:.*]] unwind label %[[CATCH_DISPATCH3]]
+; CHECK:       [[CATCH_DISPATCH3]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = catchswitch within none [label %catch4] unwind to caller
+; CHECK:       [[CATCH4:.*:]]
+; CHECK-NEXT:    [[TMP5:%.*]] = catchpad within [[TMP4]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP5]] to label %[[TRY_CONT5:.*]]
+; CHECK:       [[TRY_CONT5]]:
+; CHECK-NEXT:    call void @Exit() #[[ATTR0]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[INVOKE_CONT2]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[IF_END_I]]:
+; CHECK-NEXT:    ret i32 [[ARGC]]
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+;
+entry:
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont.i unwind label %catch.dispatch.i
+
+invoke.cont.i:                                    ; preds = %entry
+  %call.i = invoke i32 @ThrowException()
+  to label %try.cont.i unwind label %catch.dispatch.i
+
+catch.dispatch.i:                                 ; preds = %try.cont9.i, %invoke.cont.i, %entry
+  %0 = catchswitch within none [label %catch.i] unwind to caller
+
+catch.i:                                          ; preds = %catch.dispatch.i
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  catchret from %1 to label %try.cont.i
+
+try.cont.i:                                       ; preds = %catch.i, invoke.cont.i
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont2.i unwind label %catch.dispatch3
+
+invoke.cont2.i:                                   ; preds = %try.cont.i
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont1 unwind label %catch.dispatch
+
+invoke.cont1:                                     ; preds = %invoke.cont2.i
+  %tobool.not = icmp eq i32 %argc, 0
+  br i1 %tobool.not, label %if.then.i, label %if.end.i
+
+if.then.i:                                        ; preds = %invoke.cont1
+  invoke void @ThrowException()
+  to label %unreachable unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %if.then.i, %invoke.cont2.i
+  %2 = catchswitch within none [label %catch] unwind label %catch.dispatch3
+
+catch:                                            ; preds = %catch.dispatch
+  %3 = catchpad within %2 [ptr null, i32 0, ptr null]
+  invoke void @Exit() #0 [ "funclet"(token %3) ]
+  to label %invoke.cont2 unwind label %catch.dispatch3
+
+catch.dispatch3:                                  ; preds = %catch, %catch.dispatch, %entry
+  %4 = catchswitch within none [label %catch4] unwind to caller
+
+catch4:                                           ; preds = %catch.dispatch3
+  %5 = catchpad within %4 [ptr null, i32 0, ptr null]
+  catchret from %5 to label %try.cont5
+
+try.cont5:                                        ; preds = %catch4
+  call void @Exit() #0
+  unreachable
+
+invoke.cont2:                                     ; preds = %catch
+  unreachable
+
+if.end.i:                                         ; preds = %invoke.cont1
+  ret i32 %argc
+
+unreachable:                                      ; preds = %if.then
+  unreachable
+}
+
+define i32 @main(i32 noundef %argc, ptr noundef %argv) personality ptr @__CxxFrameHandler3 {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef [[ARGV:%.*]]) personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch] unwind to caller
+; CHECK:       [[CATCH:.*]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[CLEANUP:.*]]
+; CHECK:       [[INVOKE_CONT]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT_I_I:.*]] unwind label %[[CATCH_DISPATCH_I_I:.*]]
+; CHECK:       [[INVOKE_CONT_I_I]]:
+; CHECK-NEXT:    [[CALL_I_I:%.*]] = invoke i32 @ThrowException()
+; CHECK-NEXT:            to label %[[TRY_CONT_I_I:.*]] unwind label %[[CATCH_DISPATCH_I_I]]
+; CHECK:       [[CATCH_DISPATCH_I_I]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = catchswitch within none [label %catch.i.i] unwind to caller
+; CHECK:       [[CATCH_I_I:.*:]]
+; CHECK-NEXT:    [[TMP3:%.*]] = catchpad within [[TMP2]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP3]] to label %[[TRY_CONT_I_I]]
+; CHECK:       [[TRY_CONT_I_I]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT2_I_I:.*]] unwind label %[[CATCH_DISPATCH3_I:.*]]
+; CHECK:       [[INVOKE_CONT2_I_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:    [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[ARGC]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT_I]], label %[[IF_THEN_I_I:.*]], label %[[TRY_END:.*]]
+; CHECK:       [[IF_THEN_I_I]]:
+; CHECK-NEXT:    invoke void @ThrowException()
+; CHECK-NEXT:            to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[CATCH_DISPATCH_I]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = catchswitch within none [label %catch.i] unwind label %[[CATCH_DISPATCH3_I]]
+; CHECK:       [[CATCH_I:.*:]]
+; CHECK-NEXT:    [[TMP5:%.*]] = catchpad within [[TMP4]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    invoke void @Exit() #[[ATTR0]] [ "funclet"(token [[TMP5]]) ]
+; CHECK-NEXT:            to label %[[INVOKE_CONT2_I:.*]] unwind label %[[CATCH_DISPATCH3_I]]
+; CHECK:       [[CATCH_DISPATCH3_I]]:
+; CHECK-NEXT:    [[TMP6:%.*]] = catchswitch within none [label %catch4.i] unwind to caller
+; CHECK:       [[CATCH4_I:.*:]]
+; CHECK-NEXT:    [[TMP7:%.*]] = catchpad within [[TMP6]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP7]] to label %[[TRY_CONT5_I:.*]]
+; CHECK:       [[TRY_CONT5_I]]:
+; CHECK-NEXT:    call void @Exit() #[[ATTR0]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[INVOKE_CONT2_I]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[TRY_END]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[EXITONTHROW_EXIT:.*]] unwind label %[[CATCH_DISPATCH3_I]]
+; CHECK:       [[UNREACHABLE_I]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[EXITONTHROW_EXIT]]:
+; CHECK-NEXT:    [[CALL1:%.*]] = call noundef i32 @AlwaysThrows(i32 noundef [[ARGC]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ARGC]], [[CALL1]]
+; CHECK-NEXT:    br label %[[CLEANUP]]
+; CHECK:       [[CLEANUP]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i32 [ [[ADD]], %[[EXITONTHROW_EXIT]] ], [ 123, %[[CATCH]] ]
+; CHECK-NEXT:    ret i32 [[RETVAL_0]]
+;
+entry:
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %entry
+  %0 = catchswitch within none [label %catch] unwind to caller
+
+catch:                                            ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  catchret from %1 to label %cleanup
+
+invoke.cont:                                      ; preds = %entry
+  %call = call noundef i32 @ExitOnThrow(i32 noundef %argc)
+  %call1 = call noundef i32 @AlwaysThrows(i32 noundef %call)
+  %add = add nsw i32 %call, %call1
+  br label %cleanup
+
+cleanup:                                          ; preds = %catch, %invoke.cont
+  %retval.0 = phi i32 [ %add, %invoke.cont ], [ 123, %catch ]
+  ret i32 %retval.0
+}
+
+declare void @llvm.seh.try.begin()
+declare i32 @__CxxFrameHandler3(...)
+declare void @ThrowException()
+declare void @Exit() #0
+declare i32 @AlwaysThrows(i32 %id)
+
+attributes #0 = { noreturn }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"eh-asynch", i32 1}
diff --git a/llvm/test/Transforms/Inline/eha-try-fixup-phis.ll b/llvm/test/Transforms/Inline/eha-try-fixup-phis.ll
new file mode 100644
index 0000000000000..2fcf787239579
--- /dev/null
+++ b/llvm/test/Transforms/Inline/eha-try-fixup-phis.ll
@@ -0,0 +1,119 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+; Check that the phi in catch.dispatch is adjusted for the try scope end upon inlining.
+
+define i32 @ExitOnThrow(i32 %argc) #3 personality ptr @__CxxFrameHandler3 {
+; CHECK-LABEL: define i32 @ExitOnThrow(
+; CHECK-SAME: i32 [[ARGC:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[INVOKE_CONT]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[ARGC]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    invoke void @ThrowException() #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:            to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH]]
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 1, %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch] unwind to caller
+; CHECK:       [[CATCH:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    call void @DoSth(i32 noundef [[PHI]]) #[[ATTR3:[0-9]+]] [ "funclet"(token [[TMP1]]) ]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[TRY_CONT:.*]]
+; CHECK:       [[TRY_CONT]]:
+; CHECK-NEXT:    call void @Exit() #[[ATTR4:[0-9]+]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ARGC]], 5
+; CHECK-NEXT:    ret i32 [[ADD]]
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+;
+entry:
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+  %cmp = icmp sgt i32 %argc, 0
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %invoke.cont
+  invoke void @ThrowException() #0
+  to label %unreachable unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %if.then, %entry
+  %phi = phi i32 [ 1, %if.then ], [ 0, %entry ]
+  %0 = catchswitch within none [label %catch] unwind to caller
+
+catch:                                            ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  call void @DoSth(i32 noundef %phi) #1 [ "funclet"(token %1) ]
+  catchret from %1 to label %try.cont
+
+try.cont:                                         ; preds = %catch, %invoke.cont
+  call void @Exit() #2
+  unreachable
+
+if.end:                                           ; preds = %invoke.cont
+  %add = add nsw i32 %argc, 5
+  ret i32 %add
+
+unreachable:                                      ; preds = %if.then
+  unreachable
+}
+
+define i32 @main(i32 noundef %argc, ptr noundef %argv) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef [[ARGV:%.*]]) personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; 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:    [[CMP_I:%.*]] = icmp sgt i32 [[ARGC]], 0
+; CHECK-NEXT:    br i1 [[CMP_I]], label %[[IF_THEN_I:.*]], label %[[TRY_END:.*]]
+; CHECK:       [[IF_THEN_I]]:
+; CHECK-NEXT:    invoke void @ThrowException() #[[ATTR2]]
+; CHECK-NEXT:            to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[CATCH_DISPATCH_I]]:
+; CHECK-NEXT:    [[PHI_I:%.*]] = phi i32 [ 1, %[[IF_THEN_I]] ], [ 0, %[[ENTRY]] ], [ poison, %[[TRY_END]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch.i] unwind to caller
+; CHECK:       [[CATCH_I:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    call void @DoSth(i32 noundef [[PHI_I]]) #[[ATTR3]] [ "funclet"(token [[TMP1]]) ]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[TRY_CONT_I:.*]]
+; CHECK:       [[TRY_CONT_I]]:
+; CHECK-NEXT:    call void @Exit() #[[ATTR4]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[TRY_END]]:
+; CHECK-NEXT:    [[ADD_I:%.*]] = add nsw i32 [[ARGC]], 5
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[EXITONTHROW_EXIT:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[UNREACHABLE_I]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[EXITONTHROW_EXIT]]:
+; CHECK-NEXT:    [[CALL1:%.*]] = call i32 @AlwaysThrows(i32 noundef [[ADD_I]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ADD_I]], [[CALL1]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %call = call i32 @ExitOnThrow(i32 noundef %argc)
+  %call1 = call i32 @AlwaysThrows(i32 noundef %call)
+  %add = add nsw i32 %call, %call1
+  ret i32 %add
+}
+
+declare void @llvm.seh.try.begin()
+declare i32 @__CxxFrameHandler3(...)
+declare void @ThrowException()
+declare void @Exit() #0
+declare i32 @AlwaysThrows(i32 %id)
+declare void @DoSth(i32 %val)
+
+attributes #0 = { noreturn }
+attributes #1 = { nounwind }
+attributes #2 = { noreturn nounwind }
+attributes #3 = { mustprogress noreturn nounwind uwtable }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"eh-asynch", i32 1}
diff --git a/llvm/test/Transforms/Inline/eha-try-fixup-seh.ll b/llvm/test/Transforms/Inline/eha-try-fixup-seh.ll
new file mode 100644
index 0000000000000..e44ea52cc6958
--- /dev/null
+++ b/llvm/test/Transforms/Inline/eha-try-fixup-seh.ll
@@ -0,0 +1,154 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+; Check that only the unterminated try scope for unwind destination catch.dispatch6
+; is terminated upon inlining into main.
+
+define i32 @ExitOnThrow(i32 %argc) personality ptr @__C_specific_handler {
+; CHECK-LABEL: define i32 @ExitOnThrow(
+; CHECK-SAME: i32 [[ARGC:%.*]]) personality ptr @__C_specific_handler {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ARGC_ADDR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 [[ARGC]], ptr [[ARGC_ADDR]], align 4
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[INVOKE_CONT]]:
+; CHECK-NEXT:    invoke void @DoSth()
+; CHECK-NEXT:            to label %[[INVOKE_CONT1:.*]] unwind label %[[CATCH_DISPATCH]]
+; CHECK:       [[INVOKE_CONT1]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[__TRY_CONT:.*]] unwind label %[[CATCH_DISPATCH]]
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %__except] unwind to caller
+; CHECK:       [[__EXCEPT:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[__EXCEPT3:.*]]
+; CHECK:       [[__EXCEPT3]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP1]])
+; CHECK-NEXT:    br label %[[__TRY_CONT]]
+; CHECK:       [[__TRY_CONT]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT5:.*]] unwind label %[[CATCH_DISPATCH6:.*]]
+; CHECK:       [[CATCH_DISPATCH6]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = catchswitch within none [label %__except7] unwind to caller
+; CHECK:       [[__EXCEPT7:.*:]]
+; CHECK-NEXT:    [[TMP4:%.*]] = catchpad within [[TMP3]] [ptr null]
+; CHECK-NEXT:    catchret from [[TMP4]] to label %[[__EXCEPT8:.*]]
+; CHECK:       [[__EXCEPT8]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = tail call i32 @llvm.eh.exceptioncode(token [[TMP4]])
+; CHECK-NEXT:    tail call void @Exit() #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[INVOKE_CONT5]]:
+; CHECK-NEXT:    [[ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_:%.*]] = load volatile i32, ptr [[ARGC_ADDR]], align 4
+; CHECK-NEXT:    ret i32 [[ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_]]
+;
+entry:
+  %argc.addr = alloca i32, align 4
+  store i32 %argc, ptr %argc.addr, align 4
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+  invoke void @DoSth()
+  to label %invoke.cont1 unwind label %catch.dispatch
+
+invoke.cont1:                                     ; preds = %invoke.cont
+  invoke void @llvm.seh.try.end()
+  to label %__try.cont unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %invoke.cont1, %invoke.cont, %entry
+  %0 = catchswitch within none [label %__except] unwind to caller
+
+__except:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null]
+  catchret from %1 to label %__except3
+
+__except3:                                        ; preds = %__except
+  %2 = tail call i32 @llvm.eh.exceptioncode(token %1)
+  br label %__try.cont
+
+__try.cont:                                       ; preds = %invoke.cont1, %__except3
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont5 unwind label %catch.dispatch6
+
+catch.dispatch6:                                  ; preds = %__try.cont
+  %3 = catchswitch within none [label %__except7] unwind to caller
+
+__except7:                                        ; preds = %catch.dispatch6
+  %4 = catchpad within %3 [ptr null]
+  catchret from %4 to label %__except8
+
+__except8:                                        ; preds = %__except7
+  %5 = tail call i32 @llvm.eh.exceptioncode(token %4)
+  tail call void @Exit() #0
+  unreachable
+
+invoke.cont5:                                     ; preds = %__try.cont
+  %argc.addr.0.argc.addr.0.argc.addr.0.argc.addr.0. = load volatile i32, ptr %argc.addr, align 4
+  ret i32 %argc.addr.0.argc.addr.0.argc.addr.0.argc.addr.0.
+}
+
+define i32 @main(i32 noundef %argc, ptr noundef %argv) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef [[ARGV:%.*]]) personality ptr @__C_specific_handler {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ARGC_ADDR_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[ARGC_ADDR_I]])
+; CHECK-NEXT:    store i32 [[ARGC]], ptr [[ARGC_ADDR_I]], align 4
+; 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:    invoke void @DoSth()
+; CHECK-NEXT:            to label %[[INVOKE_CONT1_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[INVOKE_CONT1_I]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[__TRY_CONT_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[CATCH_DISPATCH_I]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %__except.i] unwind to caller
+; CHECK:       [[__EXCEPT_I:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[__EXCEPT3_I:.*]]
+; CHECK:       [[__EXCEPT3_I]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.eh.exceptioncode(token [[TMP1]])
+; CHECK-NEXT:    br label %[[__TRY_CONT_I]]
+; CHECK:       [[__TRY_CONT_I]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[TRY_END:.*]] unwind label %[[CATCH_DISPATCH6_I:.*]]
+; CHECK:       [[CATCH_DISPATCH6_I]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = catchswitch within none [label %__except7.i] unwind to caller
+; CHECK:       [[__EXCEPT7_I:.*:]]
+; CHECK-NEXT:    [[TMP4:%.*]] = catchpad within [[TMP3]] [ptr null]
+; CHECK-NEXT:    catchret from [[TMP4]] to label %[[__EXCEPT8_I:.*]]
+; CHECK:       [[__EXCEPT8_I]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = call i32 @llvm.eh.exceptioncode(token [[TMP4]])
+; CHECK-NEXT:    call void @Exit() #[[ATTR2]]
+; CHECK-NEXT:    unreachable
+; CHECK:       [[TRY_END]]:
+; CHECK-NEXT:    [[ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I:%.*]] = load volatile i32, ptr [[ARGC_ADDR_I]], align 4
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[EXITONTHROW_EXIT:.*]] unwind label %[[CATCH_DISPATCH6_I]]
+; CHECK:       [[EXITONTHROW_EXIT]]:
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[ARGC_ADDR_I]])
+; CHECK-NEXT:    [[CALL1:%.*]] = call i32 @AlwaysThrows(i32 noundef [[ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0_ARGC_ADDR_0__I]], [[CALL1]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %call = call i32 @ExitOnThrow(i32 noundef %argc)
+  %call1 = call i32 @AlwaysThrows(i32 noundef %call)
+  %add = add nsw i32 %call, %call1
+  ret i32 %add
+}
+
+declare void @llvm.seh.try.begin()
+declare void @llvm.seh.try.end()
+declare i32 @llvm.eh.exceptioncode(token)
+declare i32 @__C_specific_handler(...)
+declare void @Fault()
+declare void @Exit() #0
+declare i32 @AlwaysThrows(i32 %id)
+declare void @DoSth(i32 %val)
+
+attributes #0 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"eh-asynch", i32 1}
diff --git a/llvm/test/Transforms/Inline/eha-try-fixup.ll b/llvm/test/Transforms/Inline/eha-try-fixup.ll
new file mode 100644
index 0000000000000..8e16faee450a9
--- /dev/null
+++ b/llvm/test/Transforms/Inline/eha-try-fixup.ll
@@ -0,0 +1,106 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=inline -S | FileCheck %s
+; Check that the try scope of ExitOnThrow is terminated upon inlining into main.
+
+define i32 @ExitOnThrow(i32 %argc) personality ptr @__CxxFrameHandler3 {
+; CHECK-LABEL: define i32 @ExitOnThrow(
+; CHECK-SAME: i32 [[ARGC:%.*]]) personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    invoke void @llvm.seh.try.begin()
+; CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[INVOKE_CONT]]:
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[ARGC]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    invoke void @ThrowException()
+; CHECK-NEXT:            to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH]]
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch] unwind to caller
+; CHECK:       [[CATCH:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[TRY_CONT:.*]]
+; CHECK:       [[TRY_CONT]]:
+; CHECK-NEXT:    call void @Exit()
+; CHECK-NEXT:    unreachable
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    ret i32 [[ARGC]]
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+;
+entry:
+  invoke void @llvm.seh.try.begin()
+  to label %invoke.cont unwind label %catch.dispatch
+
+invoke.cont:                                      ; preds = %entry
+  %tobool.not = icmp eq i32 %argc, 0
+  br i1 %tobool.not, label %if.then, label %if.end
+
+if.then:                                          ; preds = %invoke.cont
+  invoke void @ThrowException()
+  to label %unreachable unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %if.then, %entry
+  %0 = catchswitch within none [label %catch] unwind to caller
+
+catch:                                            ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  catchret from %1 to label %try.cont
+
+try.cont:                                         ; preds = %catch
+  call void @Exit()
+  unreachable
+
+if.end:                                           ; preds = %invoke.cont
+  ret i32 %argc
+
+unreachable:                                      ; preds = %if.then
+  unreachable
+}
+
+define i32 @main(i32 noundef %argc, ptr noundef %argv) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 noundef [[ARGC:%.*]], ptr noundef [[ARGV:%.*]]) personality ptr @__CxxFrameHandler3 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; 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 %[[TRY_END:.*]]
+; CHECK:       [[IF_THEN_I]]:
+; CHECK-NEXT:    invoke void @ThrowException()
+; CHECK-NEXT:            to label %[[UNREACHABLE_I:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[CATCH_DISPATCH_I]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = catchswitch within none [label %catch.i] unwind to caller
+; CHECK:       [[CATCH_I:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    catchret from [[TMP1]] to label %[[TRY_CONT_I:.*]]
+; CHECK:       [[TRY_CONT_I]]:
+; CHECK-NEXT:    call void @Exit()
+; CHECK-NEXT:    unreachable
+; CHECK:       [[TRY_END]]:
+; CHECK-NEXT:    invoke void @llvm.seh.try.end()
+; CHECK-NEXT:            to label %[[EXITONTHROW_EXIT:.*]] unwind label %[[CATCH_DISPATCH_I]]
+; CHECK:       [[UNREACHABLE_I]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[EXITONTHROW_EXIT]]:
+; CHECK-NEXT:    [[CALL1:%.*]] = call i32 @AlwaysThrows(i32 noundef [[ARGC]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ARGC]], [[CALL1]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %call = call i32 @ExitOnThrow(i32 noundef %argc)
+  %call1 = call i32 @AlwaysThrows(i32 noundef %call)
+  %add = add nsw i32 %call, %call1
+  ret i32 %add
+}
+
+declare void @llvm.seh.try.begin()
+declare i32 @__CxxFrameHandler3(...)
+declare void @ThrowException()
+declare void @Exit() #0
+declare i32 @AlwaysThrows(i32 %id)
+
+attributes #0 = { noreturn }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"eh-asynch", i32 1}



More information about the llvm-commits mailing list