Skip to content

Commit c0a419d

Browse files
author
Sergey Andreenko
authored
Fix wrong copy prop after ASG(promoted LCL_VAR with 1 field, call). (#41197)
* Add Andy's repro test. * Andy's fix. * fix other places * Improve dump.
1 parent 3a4307e commit c0a419d

File tree

5 files changed

+137
-18
lines changed

5 files changed

+137
-18
lines changed

src/coreclr/src/jit/compiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6512,7 +6512,7 @@ class Compiler
65126512
void optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, LclNumToGenTreePtrStack* curSsaName);
65136513
void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName);
65146514
void optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName);
6515-
bool optIsSsaLocal(GenTree* tree);
6515+
unsigned optIsSsaLocal(GenTree* tree);
65166516
int optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2);
65176517
void optVnCopyProp();
65186518
INDEBUG(void optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName));

src/coreclr/src/jit/copyprop.cpp

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrSt
3636
{
3737
continue;
3838
}
39-
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
40-
if (!lvaInSsa(lclNum))
39+
const unsigned lclNum = optIsSsaLocal(tree);
40+
if (lclNum == BAD_VAR_NUM)
4141
{
4242
continue;
4343
}
@@ -61,8 +61,19 @@ void Compiler::optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName)
6161
JITDUMP("{ ");
6262
for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
6363
{
64-
GenTree* node = iter.GetValue()->Top();
65-
JITDUMP("%d-[%06d]:V%02u ", iter.Get(), dspTreeID(node), node->AsLclVarCommon()->GetLclNum());
64+
GenTreeLclVarCommon* lclVar = iter.GetValue()->Top()->AsLclVarCommon();
65+
unsigned ssaLclNum = optIsSsaLocal(lclVar);
66+
assert(ssaLclNum != BAD_VAR_NUM);
67+
68+
if (ssaLclNum == lclVar->GetLclNum())
69+
{
70+
JITDUMP("%d-[%06d]:V%02u ", iter.Get(), dspTreeID(lclVar), ssaLclNum);
71+
}
72+
else
73+
{
74+
// A promoted field was asigned using the parent struct, print `ssa field lclNum(parent lclNum)`.
75+
JITDUMP("%d-[%06d]:V%02u(V%02u) ", iter.Get(), dspTreeID(lclVar), ssaLclNum, lclVar->GetLclNum());
76+
}
6677
}
6778
JITDUMP("}\n\n");
6879
}
@@ -150,10 +161,10 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc
150161
{
151162
return;
152163
}
153-
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
164+
const unsigned lclNum = optIsSsaLocal(tree);
154165

155166
// Skip non-SSA variables.
156-
if (!lvaInSsa(lclNum))
167+
if (lclNum == BAD_VAR_NUM)
157168
{
158169
return;
159170
}
@@ -291,13 +302,39 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc
291302
return;
292303
}
293304

294-
/**************************************************************************************
295-
*
296-
* Helper to check if tree is a local that participates in SSA numbering.
297-
*/
298-
bool Compiler::optIsSsaLocal(GenTree* tree)
305+
//------------------------------------------------------------------------------
306+
// optIsSsaLocal : helper to check if the tree is a local that participates in SSA numbering.
307+
//
308+
// Arguments:
309+
// tree - The tree to perform the check on;
310+
//
311+
// Returns:
312+
// - lclNum if the local is participating in SSA;
313+
// - fieldLclNum if the parent local can be replaced by its only field;
314+
// - BAD_VAR_NUM otherwise.
315+
//
316+
unsigned Compiler::optIsSsaLocal(GenTree* tree)
299317
{
300-
return tree->IsLocal() && lvaInSsa(tree->AsLclVarCommon()->GetLclNum());
318+
if (!tree->IsLocal())
319+
{
320+
return BAD_VAR_NUM;
321+
}
322+
323+
GenTreeLclVarCommon* lclNode = tree->AsLclVarCommon();
324+
unsigned lclNum = lclNode->GetLclNum();
325+
LclVarDsc* varDsc = lvaGetDesc(lclNum);
326+
327+
if (!lvaInSsa(lclNum) && varDsc->CanBeReplacedWithItsField(this))
328+
{
329+
lclNum = varDsc->lvFieldLclStart;
330+
}
331+
332+
if (!lvaInSsa(lclNum))
333+
{
334+
return BAD_VAR_NUM;
335+
}
336+
337+
return lclNum;
301338
}
302339

303340
//------------------------------------------------------------------------------
@@ -351,22 +388,22 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
351388
// embedded update. Killing the variable is a simplification to produce 0 ASM diffs
352389
// for an update release.
353390
//
354-
if (optIsSsaLocal(tree) && (tree->gtFlags & GTF_VAR_DEF))
391+
const unsigned lclNum = optIsSsaLocal(tree);
392+
if ((lclNum != BAD_VAR_NUM) && (tree->gtFlags & GTF_VAR_DEF))
355393
{
356-
VarSetOps::AddElemD(this, optCopyPropKillSet, lvaTable[tree->AsLclVarCommon()->GetLclNum()].lvVarIndex);
394+
VarSetOps::AddElemD(this, optCopyPropKillSet, lvaTable[lclNum].lvVarIndex);
357395
}
358396
}
359397

360398
// This logic must be in sync with SSA renaming process.
361399
for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
362400
{
363-
if (!optIsSsaLocal(tree))
401+
const unsigned lclNum = optIsSsaLocal(tree);
402+
if (lclNum == BAD_VAR_NUM)
364403
{
365404
continue;
366405
}
367406

368-
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
369-
370407
// As we encounter a definition add it to the stack as a live definition.
371408
if (tree->gtFlags & GTF_VAR_DEF)
372409
{

src/coreclr/src/jit/valuenum.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6789,6 +6789,10 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree)
67896789
{
67906790
fgMutateAddressExposedLocal(tree DEBUGARG("COPYBLK - address-exposed local"));
67916791
}
6792+
else
6793+
{
6794+
JITDUMP("LHS V%02u not in ssa at [%06u], so no VN assigned\n", lhsLclNum, dspTreeID(lclVarTree));
6795+
}
67926796
}
67936797
else
67946798
{
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
// The test was showing a wrong copy propogation when a struct field was rewritten by
5+
// a call assignment to the parent struct but that assignment was not supported by copyprop.
6+
7+
using System;
8+
using System.Collections.Immutable;
9+
using System.Runtime.CompilerServices;
10+
11+
class X
12+
{
13+
[MethodImpl(MethodImplOptions.NoInlining)]
14+
public static void E(ImmutableArray<string> a) {}
15+
16+
[MethodImpl(MethodImplOptions.NoInlining)]
17+
public static ImmutableArray<string> G() => ImmutableArray<string>.Empty;
18+
19+
[MethodImpl(MethodImplOptions.NoInlining)]
20+
public static ImmutableArray<string> H()
21+
{
22+
string[] a = new string[100];
23+
24+
for (int i = 0; i < a.Length; i++)
25+
{
26+
a[i] = "hello";
27+
}
28+
29+
return ImmutableArray.Create<string>(a);
30+
}
31+
32+
[MethodImpl(MethodImplOptions.NoInlining)]
33+
public static int F()
34+
{
35+
var a = H();
36+
int r = 0;
37+
38+
foreach (var s in a)
39+
{
40+
if (s.Equals("hello")) r++;
41+
}
42+
43+
var aa = a;
44+
45+
if (r > 0)
46+
47+
{
48+
foreach (var s in a)
49+
{
50+
if (s.Equals("hello")) r--;
51+
}
52+
53+
aa = G();
54+
55+
foreach (var s in a)
56+
{
57+
if (s.Equals("hello")) r++;
58+
}
59+
}
60+
61+
E(aa);
62+
63+
return r;
64+
}
65+
66+
public static int Main() => F();
67+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<Optimize>True</Optimize>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<Compile Include="$(MSBuildProjectName).cs" />
10+
</ItemGroup>
11+
</Project>

0 commit comments

Comments
 (0)