Skip to content

Commit 713c8dd

Browse files
Take into account zero-offset field sequences when propagating locals (#64701)
* Add a test * Fix the problem LCL_VAR use VN != VN of its SSA def. Using one for replacement can run afoul of substituting into a LCL_VAR that has a zero-offset field sequence on top and thus duplicate the sequence (as the replacement def will already have this sequence incorporated into its VN). * Fix the same problem in local propagation Just one diff, missing null check elimination: we propagated a field sequence for an array address (PtrToArrElem) that later wasn't recognized as implying non-nullness of another PtrToArrElem to the same array.
1 parent 75589ae commit 713c8dd

File tree

5 files changed

+164
-22
lines changed

5 files changed

+164
-22
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,11 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
10621062
{
10631063
printf(".%02u", curAssertion->op1.lcl.ssaNum);
10641064
}
1065+
if (curAssertion->op2.zeroOffsetFieldSeq != nullptr)
1066+
{
1067+
printf(" Zero");
1068+
gtDispFieldSeq(curAssertion->op2.zeroOffsetFieldSeq);
1069+
}
10651070
break;
10661071

10671072
case O2K_CONST_INT:
@@ -1582,10 +1587,14 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
15821587
goto DONE_ASSERTION; // Don't make an assertion
15831588
}
15841589

1585-
assertion.op2.kind = O2K_LCLVAR_COPY;
1586-
assertion.op2.lcl.lclNum = lclNum2;
1587-
assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
1588-
assertion.op2.lcl.ssaNum = op2->AsLclVarCommon()->GetSsaNum();
1590+
FieldSeqNode* zeroOffsetFieldSeq = nullptr;
1591+
GetZeroOffsetFieldMap()->Lookup(op2, &zeroOffsetFieldSeq);
1592+
1593+
assertion.op2.kind = O2K_LCLVAR_COPY;
1594+
assertion.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
1595+
assertion.op2.lcl.lclNum = lclNum2;
1596+
assertion.op2.lcl.ssaNum = op2->AsLclVarCommon()->GetSsaNum();
1597+
assertion.op2.zeroOffsetFieldSeq = zeroOffsetFieldSeq;
15891598

15901599
// Ok everything has been set and the assertion looks good
15911600
assertion.assertionKind = assertionKind;
@@ -3316,14 +3325,30 @@ GenTree* Compiler::optCopyAssertionProp(AssertionDsc* curAssertion,
33163325
return nullptr;
33173326
}
33183327

3319-
// Extract the matching lclNum and ssaNum.
3320-
const unsigned copyLclNum = (op1.lcl.lclNum == lclNum) ? op2.lcl.lclNum : op1.lcl.lclNum;
3321-
unsigned copySsaNum = SsaConfig::RESERVED_SSA_NUM;
3328+
// Extract the matching lclNum and ssaNum, as well as the field sequence.
3329+
unsigned copyLclNum;
3330+
unsigned copySsaNum;
3331+
FieldSeqNode* zeroOffsetFieldSeq;
3332+
if (op1.lcl.lclNum == lclNum)
3333+
{
3334+
copyLclNum = op2.lcl.lclNum;
3335+
copySsaNum = op2.lcl.ssaNum;
3336+
zeroOffsetFieldSeq = op2.zeroOffsetFieldSeq;
3337+
}
3338+
else
3339+
{
3340+
copyLclNum = op1.lcl.lclNum;
3341+
copySsaNum = op1.lcl.ssaNum;
3342+
zeroOffsetFieldSeq = nullptr; // Only the RHS of an assignment can have a FldSeq.
3343+
assert(optLocalAssertionProp); // Were we to perform replacements in global propagation, that makes copy
3344+
// assertions for control flow ("if (a == b) { ... }"), where both operands
3345+
// could have a FldSeq, we'd need to save it for "op1" too.
3346+
}
3347+
33223348
if (!optLocalAssertionProp)
33233349
{
33243350
// Extract the ssaNum of the matching lclNum.
33253351
unsigned ssaNum = (op1.lcl.lclNum == lclNum) ? op1.lcl.ssaNum : op2.lcl.ssaNum;
3326-
copySsaNum = (op1.lcl.lclNum == lclNum) ? op2.lcl.ssaNum : op1.lcl.ssaNum;
33273352

33283353
if (ssaNum != tree->GetSsaNum())
33293354
{
@@ -3349,12 +3374,25 @@ GenTree* Compiler::optCopyAssertionProp(AssertionDsc* curAssertion,
33493374
tree->SetLclNum(copyLclNum);
33503375
tree->SetSsaNum(copySsaNum);
33513376

3377+
// The sequence we are propagating (if any) represents the inner fields.
3378+
if (zeroOffsetFieldSeq != nullptr)
3379+
{
3380+
FieldSeqNode* outerZeroOffsetFieldSeq = nullptr;
3381+
if (GetZeroOffsetFieldMap()->Lookup(tree, &outerZeroOffsetFieldSeq))
3382+
{
3383+
zeroOffsetFieldSeq = GetFieldSeqStore()->Append(zeroOffsetFieldSeq, outerZeroOffsetFieldSeq);
3384+
GetZeroOffsetFieldMap()->Remove(tree);
3385+
}
3386+
3387+
fgAddFieldSeqForZeroOffset(tree, zeroOffsetFieldSeq);
3388+
}
3389+
33523390
#ifdef DEBUG
33533391
if (verbose)
33543392
{
33553393
printf("\nAssertion prop in " FMT_BB ":\n", compCurBB->bbNum);
33563394
optPrintAssertion(curAssertion, index);
3357-
gtDispTree(tree, nullptr, nullptr, true);
3395+
DISPNODE(tree);
33583396
}
33593397
#endif
33603398

@@ -4665,15 +4703,15 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
46654703
}
46664704

46674705
//------------------------------------------------------------------------
4668-
// optImpliedAssertions: Given a tree node that makes an assertion this
4669-
// method computes the set of implied assertions
4670-
// that are also true. The updated assertions are
4671-
// maintained on the Compiler object.
4706+
// optImpliedAssertions: Given an assertion this method computes the set
4707+
// of implied assertions that are also true.
46724708
//
46734709
// Arguments:
46744710
// assertionIndex : The id of the assertion.
46754711
// activeAssertions : The assertions that are already true at this point.
4676-
4712+
// This method will add the discovered implied assertions
4713+
// to this set.
4714+
//
46774715
void Compiler::optImpliedAssertions(AssertionIndex assertionIndex, ASSERT_TP& activeAssertions)
46784716
{
46794717
noway_assert(!optLocalAssertionProp);
@@ -4822,7 +4860,6 @@ void Compiler::optImpliedByTypeOfAssertions(ASSERT_TP& activeAssertions)
48224860
// Return Value:
48234861
// The assertions we have about the value number.
48244862
//
4825-
48264863
ASSERT_VALRET_TP Compiler::optGetVnMappedAssertions(ValueNum vn)
48274864
{
48284865
ASSERT_TP set = BitVecOps::UninitVal();

src/coreclr/jit/compiler.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7627,7 +7627,6 @@ class Compiler
76277627
O2K_CONST_INT,
76287628
O2K_CONST_LONG,
76297629
O2K_CONST_DOUBLE,
7630-
O2K_ARR_LEN,
76317630
O2K_SUBRANGE,
76327631
O2K_COUNT
76337632
};
@@ -7667,7 +7666,11 @@ class Compiler
76677666
GenTreeFlags iconFlags; // gtFlags
76687667
};
76697668
union {
7670-
SsaVar lcl;
7669+
struct
7670+
{
7671+
SsaVar lcl;
7672+
FieldSeqNode* zeroOffsetFieldSeq;
7673+
};
76717674
IntVal u1;
76727675
__int64 lconVal;
76737676
double dconVal;
@@ -7746,6 +7749,7 @@ class Compiler
77467749
{
77477750
return false;
77487751
}
7752+
77497753
switch (op2.kind)
77507754
{
77517755
case O2K_IND_CNS_INT:
@@ -7760,9 +7764,9 @@ class Compiler
77607764
return (memcmp(&op2.dconVal, &that->op2.dconVal, sizeof(double)) == 0);
77617765

77627766
case O2K_LCLVAR_COPY:
7763-
case O2K_ARR_LEN:
77647767
return (op2.lcl.lclNum == that->op2.lcl.lclNum) &&
7765-
(!vnBased || op2.lcl.ssaNum == that->op2.lcl.ssaNum);
7768+
(!vnBased || op2.lcl.ssaNum == that->op2.lcl.ssaNum) &&
7769+
(op2.zeroOffsetFieldSeq == that->op2.zeroOffsetFieldSeq);
77667770

77677771
case O2K_SUBRANGE:
77687772
return op2.u2.Equals(that->op2.u2);
@@ -7775,6 +7779,7 @@ class Compiler
77757779
assert(!"Unexpected value for op2.kind in AssertionDsc.");
77767780
break;
77777781
}
7782+
77787783
return false;
77797784
}
77807785

src/coreclr/jit/copyprop.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ void Compiler::optCopyProp(Statement* stmt,
177177
continue;
178178
}
179179

180-
if (newLclDefVN != tree->gtVNPair.GetConservative())
180+
ValueNum lclDefVN = varDsc->GetPerSsaData(tree->GetSsaNum())->m_vnPair.GetConservative();
181+
if (newLclDefVN != lclDefVN)
181182
{
182183
continue;
183184
}
@@ -230,9 +231,9 @@ void Compiler::optCopyProp(Statement* stmt,
230231
{
231232
JITDUMP("VN based copy assertion for ");
232233
printTreeID(tree);
233-
printf(" V%02d " FMT_VN " by ", lclNum, tree->GetVN(VNK_Conservative));
234+
printf(" V%02d " FMT_VN " by ", lclNum, lclDefVN);
234235
printTreeID(newLclDefNode);
235-
printf(" V%02d " FMT_VN ".\n", newLclNum, newLclDefNode->GetVN(VNK_Conservative));
236+
printf(" V%02d " FMT_VN ".\n", newLclNum, newLclDefVN);
236237
DISPNODE(tree);
237238
}
238239
#endif
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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.Numerics;
6+
using System.Runtime.CompilerServices;
7+
8+
class Runtime_64700
9+
{
10+
private static StructWithVtors _structWithVtorsStatic;
11+
12+
static int Main()
13+
{
14+
_structWithVtorsStatic = new StructWithVtors { StructWithOneVtor = { OneVtor = new Vector2(1, 0) } };
15+
16+
if (ProblemWithCopyProp(0) != 0)
17+
{
18+
return 101;
19+
}
20+
21+
if (ProblemWithLocalAssertionProp(new SmallerStruct[] { default }) != 1)
22+
{
23+
return 102;
24+
}
25+
26+
return 100;
27+
}
28+
29+
[MethodImpl(MethodImplOptions.NoInlining)]
30+
private static float ProblemWithCopyProp(float a)
31+
{
32+
ref var p1 = ref _structWithVtorsStatic.StructWithOneVtor.OneVtor;
33+
ref var p2 = ref _structWithVtorsStatic;
34+
35+
if (_structWithVtorsStatic.StructWithOneVtor.OneVtor.X == 1)
36+
{
37+
p2.StructWithOneVtor.OneVtor = Vector2.Zero;
38+
if (_structWithVtorsStatic.StructWithOneVtor.OneVtor.X == 1)
39+
{
40+
a = 1;
41+
}
42+
}
43+
44+
return a + p1.Y;
45+
}
46+
47+
struct StructWithVtors
48+
{
49+
public StructWithOneVtor StructWithOneVtor;
50+
}
51+
52+
struct StructWithOneVtor
53+
{
54+
public Vector2 OneVtor;
55+
}
56+
57+
[MethodImpl(MethodImplOptions.NoInlining)]
58+
private static long ProblemWithLocalAssertionProp(SmallerStruct[] a)
59+
{
60+
ref var p1 = ref a[0];
61+
Use(ref p1);
62+
ref var p2 = ref p1.RegStruct;
63+
Use(ref p2);
64+
65+
var t = p2.FirstLngValue;
66+
a[0].RegStruct.FirstLngValue = 1;
67+
68+
return t + p2.FirstLngValue;
69+
}
70+
71+
public static void Use<T>(ref T arg) { }
72+
73+
struct SmallerStruct
74+
{
75+
public RegStruct RegStruct;
76+
}
77+
78+
struct RegStruct
79+
{
80+
public long FirstLngValue;
81+
}
82+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<Optimize>True</Optimize>
5+
<CLRTestBatchPreCommands><![CDATA[
6+
$(CLRTestBatchPreCommands)
7+
set DOTNET_JitOptRepeat=ProblemWithCopyProp
8+
]]></CLRTestBatchPreCommands>
9+
<BashCLRTestPreCommands><![CDATA[
10+
$(BashCLRTestPreCommands)
11+
export DOTNET_JitOptRepeat=ProblemWithCopyProp
12+
]]></BashCLRTestPreCommands>
13+
</PropertyGroup>
14+
<ItemGroup>
15+
<Compile Include="$(MSBuildProjectName).cs" />
16+
</ItemGroup>
17+
</Project>

0 commit comments

Comments
 (0)