Skip to content

Commit 9e57de2

Browse files
authored
JIT: Fix downwards loop transformation with multiply executed exiting blocks (#103723)
The transformation to turn loops into downwards counted loops was missing a check for whether the block containing the exit test was potentially executed multiple times.
1 parent 8ab80b9 commit 9e57de2

5 files changed

+120
-0
lines changed

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,8 @@ class FlowGraphNaturalLoop
22302230
bool CanDuplicate(INDEBUG(const char** reason));
22312231
void Duplicate(BasicBlock** insertAfter, BlockToBlockMap* map, weight_t weightScale);
22322232

2233+
bool MayExecuteBlockMultipleTimesPerIteration(BasicBlock* block);
2234+
22332235
#ifdef DEBUG
22342236
static void Dump(FlowGraphNaturalLoop* loop);
22352237
#endif // DEBUG

src/coreclr/jit/flowgraph.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5768,6 +5768,39 @@ void FlowGraphNaturalLoop::Duplicate(BasicBlock** insertAfter, BlockToBlockMap*
57685768
});
57695769
}
57705770

5771+
//------------------------------------------------------------------------
5772+
// MayExecuteBlockMultipleTimesPerIteration:
5773+
// Check if the loop may execute a particular loop block multiple times for
5774+
// each iteration.
5775+
//
5776+
// Parameters:
5777+
// block - The basic block
5778+
//
5779+
// Returns:
5780+
// True if so. May return true even if the true answer is false.
5781+
//
5782+
bool FlowGraphNaturalLoop::MayExecuteBlockMultipleTimesPerIteration(BasicBlock* block)
5783+
{
5784+
assert(ContainsBlock(block));
5785+
5786+
if (ContainsImproperHeader())
5787+
{
5788+
// To be more precise we could check if 'block' can reach itself
5789+
// without going through the header, but this case is rare.
5790+
return true;
5791+
}
5792+
5793+
for (FlowGraphNaturalLoop* child = GetChild(); child != nullptr; child = child->GetSibling())
5794+
{
5795+
if (child->ContainsBlock(block))
5796+
{
5797+
return true;
5798+
}
5799+
}
5800+
5801+
return false;
5802+
}
5803+
57715804
//------------------------------------------------------------------------
57725805
// IterConst: Get the constant with which the iterator is modified
57735806
//

src/coreclr/jit/inductionvariableopts.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,12 @@ bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext,
10561056
// At this point we know that the single exit dominates all backedges.
10571057
JITDUMP(" All backedges are dominated by exiting block " FMT_BB "\n", exiting->bbNum);
10581058

1059+
if (loop->MayExecuteBlockMultipleTimesPerIteration(exiting))
1060+
{
1061+
JITDUMP(" Exiting block may be executed multiple times per iteration; cannot place decrement in it\n");
1062+
return false;
1063+
}
1064+
10591065
Scev* backedgeCount = scevContext.ComputeExitNotTakenCount(exiting);
10601066
if (backedgeCount == nullptr)
10611067
{
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using Xunit;
7+
8+
public class DownwardsLoopMultiplyExecutedExiting
9+
{
10+
[Fact]
11+
public static int ExitFromNestedLoop()
12+
{
13+
int n = Get10();
14+
int i = 0;
15+
int result = 0;
16+
if (n < 0)
17+
return -1;
18+
19+
do
20+
{
21+
int j = 0;
22+
do
23+
{
24+
if (i >= n)
25+
return result;
26+
27+
j++;
28+
result++;
29+
} while (j < 10);
30+
31+
i++;
32+
} while (true);
33+
}
34+
35+
[Fact]
36+
public static int ExitFromNestedIrreducibleLoop()
37+
{
38+
int n = Get10();
39+
int i = 0;
40+
int result = -1;
41+
if (n < 0)
42+
return -1;
43+
44+
do
45+
{
46+
int j = 0;
47+
if (AlwaysFalse())
48+
goto InsideLoop;
49+
50+
LoopHeader:
51+
j++;
52+
result++;
53+
54+
InsideLoop:;
55+
if (i >= n)
56+
return result;
57+
58+
if (j < 10)
59+
goto LoopHeader;
60+
61+
i++;
62+
} while (true);
63+
}
64+
65+
[MethodImpl(MethodImplOptions.NoInlining)]
66+
private static bool AlwaysFalse() => false;
67+
68+
[MethodImpl(MethodImplOptions.NoInlining)]
69+
private static int Get10() => 10;
70+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<DebugType>PdbOnly</DebugType>
4+
<Optimize>True</Optimize>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
</ItemGroup>
9+
</Project>

0 commit comments

Comments
 (0)