diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 62fecaf3c3d2e..7bf00c5cc84d6 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2230,6 +2230,8 @@ class FlowGraphNaturalLoop bool CanDuplicate(INDEBUG(const char** reason)); void Duplicate(BasicBlock** insertAfter, BlockToBlockMap* map, weight_t weightScale); + bool MayExecuteBlockMultipleTimesPerIteration(BasicBlock* block); + #ifdef DEBUG static void Dump(FlowGraphNaturalLoop* loop); #endif // DEBUG diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 12183919a754f..ddd08e04d336a 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -5768,6 +5768,39 @@ void FlowGraphNaturalLoop::Duplicate(BasicBlock** insertAfter, BlockToBlockMap* }); } +//------------------------------------------------------------------------ +// MayExecuteBlockMultipleTimesPerIteration: +// Check if the loop may execute a particular loop block multiple times for +// each iteration. +// +// Parameters: +// block - The basic block +// +// Returns: +// True if so. May return true even if the true answer is false. +// +bool FlowGraphNaturalLoop::MayExecuteBlockMultipleTimesPerIteration(BasicBlock* block) +{ + assert(ContainsBlock(block)); + + if (ContainsImproperHeader()) + { + // To be more precise we could check if 'block' can reach itself + // without going through the header, but this case is rare. + return true; + } + + for (FlowGraphNaturalLoop* child = GetChild(); child != nullptr; child = child->GetSibling()) + { + if (child->ContainsBlock(block)) + { + return true; + } + } + + return false; +} + //------------------------------------------------------------------------ // IterConst: Get the constant with which the iterator is modified // diff --git a/src/coreclr/jit/inductionvariableopts.cpp b/src/coreclr/jit/inductionvariableopts.cpp index ffcfef21ef2e3..69ff2302eb383 100644 --- a/src/coreclr/jit/inductionvariableopts.cpp +++ b/src/coreclr/jit/inductionvariableopts.cpp @@ -1056,6 +1056,12 @@ bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, // At this point we know that the single exit dominates all backedges. JITDUMP(" All backedges are dominated by exiting block " FMT_BB "\n", exiting->bbNum); + if (loop->MayExecuteBlockMultipleTimesPerIteration(exiting)) + { + JITDUMP(" Exiting block may be executed multiple times per iteration; cannot place decrement in it\n"); + return false; + } + Scev* backedgeCount = scevContext.ComputeExitNotTakenCount(exiting); if (backedgeCount == nullptr) { diff --git a/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.cs b/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.cs new file mode 100644 index 0000000000000..c68263b0ba88b --- /dev/null +++ b/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.cs @@ -0,0 +1,70 @@ +// 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 Xunit; + +public class DownwardsLoopMultiplyExecutedExiting +{ + [Fact] + public static int ExitFromNestedLoop() + { + int n = Get10(); + int i = 0; + int result = 0; + if (n < 0) + return -1; + + do + { + int j = 0; + do + { + if (i >= n) + return result; + + j++; + result++; + } while (j < 10); + + i++; + } while (true); + } + + [Fact] + public static int ExitFromNestedIrreducibleLoop() + { + int n = Get10(); + int i = 0; + int result = -1; + if (n < 0) + return -1; + + do + { + int j = 0; + if (AlwaysFalse()) + goto InsideLoop; + + LoopHeader: + j++; + result++; + + InsideLoop:; + if (i >= n) + return result; + + if (j < 10) + goto LoopHeader; + + i++; + } while (true); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AlwaysFalse() => false; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Get10() => 10; +} diff --git a/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.csproj b/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.csproj new file mode 100644 index 0000000000000..b47c3e8e8d9f5 --- /dev/null +++ b/src/tests/JIT/opt/Loops/DownwardsLoopMultiplyExecutedExitingBlock.csproj @@ -0,0 +1,9 @@ + + + PdbOnly + True + + + + +