Skip to content

Commit db172b4

Browse files
JIT: Extract all side effects of the index in optRemoveRangeCheck (#92210)
optRemoveRangeCheck extracts only GTF_ASG from the bounds check. If the BOUNDS_CHECK is complex, that results in silently dropping side effects on the floor (see the example case). The ideal fix is that we should always extract all side effects from the index and length operands, however this has large regressions because the length typically has an ARR_LENGTH that we then extract. This PR instead has a surgical fix for the problem case that can be backported to .NET 8. It extracts all side effects from the index, but keeps extracting only GTF_ASG from the length to get around the issue mentioned above. Fix #91862 Co-authored-by: Jakob Botsch Nielsen <[email protected]>
1 parent 6c9a743 commit db172b4

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

src/coreclr/jit/optimizer.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8875,9 +8875,15 @@ GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma,
88758875
}
88768876
#endif
88778877

8878-
// Extract side effects
8878+
// TODO-Bug: We really should be extracting all side effects from the
8879+
// length and index here, but the length typically involves a GT_ARR_LENGTH
8880+
// that we would preserve. Usually, as part of proving that the range check
8881+
// passes, we have also proven that the ARR_LENGTH is non-faulting. We need
8882+
// a good way to communicate to this function that it is ok to ignore side
8883+
// effects of the ARR_LENGTH.
88798884
GenTree* sideEffList = nullptr;
8880-
gtExtractSideEffList(check, &sideEffList, GTF_ASG);
8885+
gtExtractSideEffList(check->GetArrayLength(), &sideEffList, GTF_ASG);
8886+
gtExtractSideEffList(check->GetIndex(), &sideEffList);
88818887

88828888
if (sideEffList != nullptr)
88838889
{

src/tests/JIT/Regression/JitBlue/Runtime_91576/Runtime_91576.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Licensed to the .NET Foundation under one or more agreements.
2-
// The .NET Foundation licenses this file to you under the MIT license.aa
2+
// The .NET Foundation licenses this file to you under the MIT license.
33

44
// Generated by Fuzzlyn v1.6 on 2023-09-03 15:59:01
55
// Run on X64 Windows
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 System.Runtime.Intrinsics;
7+
using Xunit;
8+
9+
public class Runtime_91862
10+
{
11+
[Fact]
12+
public static int TestEntryPoint()
13+
{
14+
return Foo(default);
15+
}
16+
17+
[MethodImpl(MethodImplOptions.NoInlining)]
18+
private static int Foo(Vector128<float> v)
19+
{
20+
int result = 101;
21+
// This tree results in a BOUNDS_CHECK for Bar(...) & 3
22+
float x = Vector128.GetElement(v, Bar(ref result) & 3);
23+
24+
if (result != 100)
25+
{
26+
Console.WriteLine("FAIL");
27+
}
28+
29+
// After inlining x is DCE'd, which will extract side effects of its assignment above.
30+
// That results IR amenable to forward sub, and we end up with a BOUNDS_CHECK
31+
// with a complex index expression that we can still prove is within bounds.
32+
Baz(x);
33+
return result;
34+
}
35+
36+
[MethodImpl(MethodImplOptions.NoInlining)]
37+
private static int Bar(ref int result)
38+
{
39+
result = 100;
40+
return 0;
41+
}
42+
43+
private static void Baz(float x)
44+
{
45+
}
46+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<Optimize>True</Optimize>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Compile Include="$(MSBuildProjectName).cs" />
7+
</ItemGroup>
8+
</Project>

0 commit comments

Comments
 (0)