diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 915f19525ffc8958153d2f1ba61028792db63ff4..05c96837bc4ef07d1df43fef6e91721361702893 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1724,6 +1724,25 @@ bool Compiler::optIsLoopClonable(unsigned loopInd) return false; } + // Reject cloning if this is a mid-entry loop and the entry has non-loop predecessors other than its head. + // This loop may be part of a larger looping construct that we didn't recognize. + // + // We should really fix this in optCanonicalizeLoop. + // + if (!loop.lpIsTopEntry()) + { + for (BasicBlock* const entryPred : loop.lpEntry->PredBlocks()) + { + if ((entryPred != loop.lpHead) && !loop.lpContains(entryPred)) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP + ". Is not top entry, and entry has multiple non-loop preds.\n", + loopInd); + return false; + } + } + } + // We've previously made a decision whether to have separate return epilogs, or branch to one. // There's a GCInfo limitation in the x86 case, so that there can be no more than SET_EPILOGCNT_MAX separate // epilogs. Other architectures have a limit of 4 here for "historical reasons", but this should be revisited diff --git a/src/tests/JIT/opt/Cloning/Runtime_70802.cs b/src/tests/JIT/opt/Cloning/Runtime_70802.cs new file mode 100644 index 0000000000000000000000000000000000000000..d10c799d257182c37b251805fd51356f6d469860 --- /dev/null +++ b/src/tests/JIT/opt/Cloning/Runtime_70802.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +public class B +{ + public virtual int V() => 33; +} + +public class D : B +{ + public override int V() => 44; +} + +class Runtime_70802 +{ + [MethodImpl(MethodImplOptions.NoInlining)] + static void G() {} + + [MethodImpl(MethodImplOptions.NoInlining)] + static int F(B b, int n = 10, int m = 10) + { + int i = 0; + int j = 0; + int r = 0; + goto mid; + top: + G(); + mid: + r += b.V(); + i++; + if (i < n) goto top; + j++; + if (i < m) goto mid; + return r; + } + + public static int Main() + { + D d = new D(); + + for (int i = 0; i < 100; i++) + { + _ = F(d); + Thread.Sleep(15); + } + + Thread.Sleep(50); + + int r = 0; + + for (int i = 0; i < 100; i++) + { + r += F(d); + } + + Console.WriteLine($"result is {r} (expected 44000)"); + + return r / 440; + } +} diff --git a/src/tests/JIT/opt/Cloning/Runtime_70802.csproj b/src/tests/JIT/opt/Cloning/Runtime_70802.csproj new file mode 100644 index 0000000000000000000000000000000000000000..6bcd0b60a2939a688a5211c67294c84e75fe11f0 --- /dev/null +++ b/src/tests/JIT/opt/Cloning/Runtime_70802.csproj @@ -0,0 +1,19 @@ + + + Exe + True + + + + + + + + +