Skip to content

Commit ffcb014

Browse files
authored
JIT: Avoid reordering operands in fgMorphModToSubMulDiv (#71615)
* JIT: Avoid reordering operands in fgMorphModToSubMulDiv fgMorphModToSubMulDiv tries to check if it is ok to "clone" locals instead of spilling them by checking for address exposure. This was necessary because GTF_GLOB_REF is not up-to-date in preorder during morph, which is when this runs. However, address exposure is not valid for implicit byrefs at this point, so the check is not enough. The logic is trying to figure out which of the operands need to be spilled and which of them can be cloned. However, the logic was also wrong in the face of potential embedded assignments. Thus, rework it to be a bit more conservative but correct. As part of this, remove fgIsSafeToClone and make fgMakeMultiUse less conservative by avoiding checking for address exposure. This was probably an attempt at some limited interference checks, but from what I could see other uses do not need this. Fix #65118 * Add a test * Revert unnecessary change * Switch to gtCloneExpr
1 parent a33b9d8 commit ffcb014

File tree

4 files changed

+99
-43
lines changed

4 files changed

+99
-43
lines changed

src/coreclr/jit/compiler.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5574,7 +5574,6 @@ class Compiler
55745574
// Create a new temporary variable to hold the result of *ppTree,
55755575
// and transform the graph accordingly.
55765576
GenTree* fgInsertCommaFormTemp(GenTree** ppTree, CORINFO_CLASS_HANDLE structType = nullptr);
5577-
bool fgIsSafeToClone(GenTree* tree);
55785577
TempInfo fgMakeTemp(GenTree* rhs, CORINFO_CLASS_HANDLE structType = nullptr);
55795578
GenTree* fgMakeMultiUse(GenTree** ppTree, CORINFO_CLASS_HANDLE structType = nullptr);
55805579

src/coreclr/jit/morph.cpp

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,39 +1785,6 @@ void CallArgs::SetNeedsTemp(CallArg* arg)
17851785
m_needsTemps = true;
17861786
}
17871787

1788-
//------------------------------------------------------------------------------
1789-
// fgIsSafeToClone: If the node is an unaliased local or constant,
1790-
// then it is safe to clone.
1791-
//
1792-
// Arguments:
1793-
// tree - The node to check if it is safe to clone.
1794-
//
1795-
// Return Value:
1796-
// True if the tree is cloneable. False if the tree is not cloneable.
1797-
//
1798-
// Notes:
1799-
// This is conservative as this will return False if the local's address
1800-
// is exposed.
1801-
//
1802-
bool Compiler::fgIsSafeToClone(GenTree* tree)
1803-
{
1804-
if (tree->IsInvariant())
1805-
{
1806-
return true;
1807-
}
1808-
else if (tree->IsLocal())
1809-
{
1810-
// Can't rely on GTF_GLOB_REF here.
1811-
//
1812-
if (!lvaGetDesc(tree->AsLclVarCommon())->IsAddressExposed())
1813-
{
1814-
return true;
1815-
}
1816-
}
1817-
1818-
return false;
1819-
}
1820-
18211788
//------------------------------------------------------------------------------
18221789
// fgMakeTemp: Make a temp variable with a right-hand side expression as the assignment.
18231790
//
@@ -1865,18 +1832,18 @@ TempInfo Compiler::fgMakeTemp(GenTree* rhs, CORINFO_CLASS_HANDLE structType /*=
18651832
// A fresh GT_LCL_VAR node referencing the temp which has not been used
18661833
//
18671834
// Notes:
1868-
// Caller must ensure that if the node is an unaliased local, the second use this
1869-
// creates will be evaluated before the local can be reassigned.
1870-
//
1871-
// Can be safely called in morph preorder, before GTF_GLOB_REF is reliable.
1835+
// This function will clone invariant nodes and locals, so this function
1836+
// should only be used in situations where no interference between the
1837+
// original use and new use is possible. Otherwise, fgInsertCommaFormTemp
1838+
// should be used directly.
18721839
//
18731840
GenTree* Compiler::fgMakeMultiUse(GenTree** pOp, CORINFO_CLASS_HANDLE structType /*= nullptr*/)
18741841
{
18751842
GenTree* const tree = *pOp;
18761843

1877-
if (fgIsSafeToClone(tree))
1844+
if (tree->IsInvariant() || tree->OperIsLocal())
18781845
{
1879-
return gtClone(tree);
1846+
return gtCloneExpr(tree);
18801847
}
18811848

18821849
return fgInsertCommaFormTemp(pOp, structType);
@@ -13816,20 +13783,43 @@ GenTree* Compiler::fgMorphModToSubMulDiv(GenTreeOp* tree)
1381613783

1381713784
GenTreeOp* const div = tree;
1381813785

13786+
assert(!div->IsReverseOp());
13787+
1381913788
GenTree* dividend = div->gtGetOp1();
1382013789
GenTree* divisor = div->gtGetOp2();
1382113790

13822-
TempInfo tempInfos[2]{};
13791+
TempInfo tempInfos[2];
1382313792
int tempInfoCount = 0;
1382413793

13825-
if (!fgIsSafeToClone(dividend))
13794+
// This transform runs in pre-morph so we cannot rely on GTF_GLOB_REF.
13795+
// Furthermore, this logic is somewhat complicated since the divisor and
13796+
// dividend are arbitrary nodes. For instance, if we spill the divisor and
13797+
// the dividend is a local, we need to spill the dividend too unless the
13798+
// divisor could not cause it to be reassigned.
13799+
// This could be slightly better via GTF_CALL and GTF_ASG checks on the
13800+
// divisor but the diffs of this were minor and the extra complexity seemed
13801+
// not worth it.
13802+
bool spillDividend;
13803+
bool spillDivisor;
13804+
if (divisor->IsInvariant() || divisor->OperIsLocal())
13805+
{
13806+
spillDivisor = false;
13807+
spillDividend = !dividend->IsInvariant() && !dividend->OperIsLocal();
13808+
}
13809+
else
13810+
{
13811+
spillDivisor = true;
13812+
spillDividend = !dividend->IsInvariant();
13813+
}
13814+
13815+
if (spillDividend)
1382613816
{
1382713817
tempInfos[tempInfoCount] = fgMakeTemp(dividend);
1382813818
dividend = tempInfos[tempInfoCount].load;
1382913819
tempInfoCount++;
1383013820
}
1383113821

13832-
if (!fgIsSafeToClone(divisor))
13822+
if (spillDivisor)
1383313823
{
1383413824
tempInfos[tempInfoCount] = fgMakeTemp(divisor);
1383513825
divisor = tempInfos[tempInfoCount].load;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
// Generated by Fuzzlyn v1.5 on 2022-07-03 14:40:52
5+
// Run on Arm64 MacOS
6+
// Seed: 5960657379714964908
7+
// Reduced from 328.5 KiB to 0.8 KiB in 00:02:08
8+
// Debug: Outputs 0
9+
// Release: Outputs 1
10+
using System.Runtime.CompilerServices;
11+
12+
public struct S0
13+
{
14+
public long F0;
15+
public long F1;
16+
public S0(long f1): this()
17+
{
18+
F1 = f1;
19+
}
20+
}
21+
22+
public struct S1
23+
{
24+
public uint F1;
25+
public ushort F2;
26+
public S0 F4;
27+
public S1(ulong f5): this()
28+
{
29+
}
30+
31+
public long M82(ref short[] arg0)
32+
{
33+
Runtime_71600.s_13 = this.F1++;
34+
S1 var1 = new S1(0);
35+
return Runtime_71600.s_39[0].F1;
36+
}
37+
}
38+
39+
public class Runtime_71600
40+
{
41+
public static uint s_13;
42+
public static short[] s_28;
43+
public static S0[] s_39;
44+
public static int Main()
45+
{
46+
S0[] vr2 = new S0[]{new S0(-1)};
47+
s_39 = vr2;
48+
S1 vr3 = default(S1);
49+
return M80(vr3) == 0 ? 100 : -1;
50+
}
51+
52+
[MethodImpl(MethodImplOptions.NoInlining)]
53+
public static int M80(S1 arg0)
54+
{
55+
arg0.F2 += (ushort)(arg0.F1 % (uint)arg0.M82(ref s_28));
56+
return arg0.F2;
57+
}
58+
}
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+
<OutputType>Exe</OutputType>
4+
<Optimize>True</Optimize>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
</ItemGroup>
9+
</Project>

0 commit comments

Comments
 (0)