diff --git a/src/coreclr/jit/inductionvariableopts.cpp b/src/coreclr/jit/inductionvariableopts.cpp
index 3384175eee12ae..86d0d5e8802b9c 100644
--- a/src/coreclr/jit/inductionvariableopts.cpp
+++ b/src/coreclr/jit/inductionvariableopts.cpp
@@ -433,8 +433,8 @@ void PerLoopInfo::Invalidate(FlowGraphNaturalLoop* loop)
// sense that all their predecessors must come from inside the loop. Loop
// exit canonicalization guarantees this for regular exit blocks. It is not
// guaranteed for exceptional exits, but we do not expect to widen IVs that
-// are live into exceptional exits since those are marked DNER which makes it
-// unprofitable anyway.
+// are live into exceptional exits since those are not register candidates
+// (see optWidenPrimaryIV) which makes it unprofitable anyway.
//
// Note that there may be natural loops that have not had their regular exits
// canonicalized at the time when IV opts run, in particular if RBO/assertion
@@ -1328,15 +1328,18 @@ bool Compiler::optLocalHasNonLoopUses(unsigned lclNum, FlowGraphNaturalLoop* loo
return true;
}
- if (varDsc->lvDoNotEnregister)
+ if (!varDsc->lvTracked && !varDsc->lvInSsa)
{
- // This filters out locals that may be live into exceptional exits.
+ // We do not have liveness we can use for this untracked local.
return true;
}
- if (!varDsc->lvTracked && !varDsc->lvInSsa)
+ if (varDsc->lvTracked && varDsc->IsLiveInOutOfHandler())
{
- // We do not have liveness we can use for this untracked local.
+ // The local is live into an EH handler (an exceptional exit). The
+ // regular exit blocks visited below do not include handlers, and we
+ // use this as a cheap alternative to checking all EH successors
+ // of all blocks in the loop.
return true;
}
@@ -1358,6 +1361,25 @@ bool Compiler::optLocalHasNonLoopUses(unsigned lclNum, FlowGraphNaturalLoop* loo
return true;
}
+#ifdef DEBUG
+ // We currently do not expect to optimize locals that are live into exceptional
+ // exits. Such IVs are not currently register candidates (EH write-thru is
+ // only for single def locals) which makes it unprofitable. If this ever
+ // changes we need some more expansive handling here.
+ loop->VisitLoopBlocks([=](BasicBlock* block) {
+ block->VisitAllSuccs(this, [=](BasicBlock* succ) {
+ if (!loop->ContainsBlock(succ) && bbIsHandlerBeg(succ))
+ {
+ assert(!optLocalIsLiveIntoBlock(lclNum, succ) && "Candidate local is live into exceptional exit");
+ }
+
+ return BasicBlockVisit::Continue;
+ });
+
+ return BasicBlockVisit::Continue;
+ });
+#endif
+
return false;
}
diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_128392/Runtime_128392.cs b/src/tests/JIT/Regression/JitBlue/Runtime_128392/Runtime_128392.cs
new file mode 100644
index 00000000000000..9857cfe457ae8e
--- /dev/null
+++ b/src/tests/JIT/Regression/JitBlue/Runtime_128392/Runtime_128392.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Runtime_128392;
+
+using System;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+public static class Runtime_128392
+{
+ private static long s_sink;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void Process(int x)
+ {
+ if (x < 0)
+ {
+ throw new Exception("boom");
+ }
+
+ s_sink += x;
+ }
+
+ // The loop counter 'i' is updated inside the try and read in the catch handler.
+ // Induction-variable optimization must not drop the in-loop update of 'i', since
+ // it is live into the EH handler; otherwise the handler observes a stale value.
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private static int CountProcessedBeforeThrow(int[] values)
+ {
+ int i = 0;
+ try
+ {
+ for (; i < values.Length; i++)
+ {
+ Process(values[i]);
+ }
+ }
+ catch (Exception)
+ {
+ return i;
+ }
+
+ return -1;
+ }
+
+ [Fact]
+ public static void TestEntryPoint()
+ {
+ int[] values = { 0, 1, 2, -1, 4 };
+ Assert.Equal(3, CountProcessedBeforeThrow(values));
+ }
+}
diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj
index f8bedd81f9050b..f4bb02ae2ab609 100644
--- a/src/tests/JIT/Regression/Regression_ro_2.csproj
+++ b/src/tests/JIT/Regression/Regression_ro_2.csproj
@@ -98,6 +98,7 @@
+