Skip to content

Commit 1b19fe3

Browse files
authored
JIT: Model partial defs less conservatively in liveness when possible (#115081)
Before this change a partial def `v.x = 123` was modelled semantically something like `v = v with x = 123`. This matches how SSA models these definitions. However, it has the downside that it causes partial defs to be considered uses. This PR changes things so that other liveness passes take a more liberal view, where partial defs are no longer considered full defs, but are then also not considered uses. This allows more dead-code elimination around these cases. The liveness that runs during SSA is not changed; it still takes the old view.
1 parent bb6b09a commit 1b19fe3

File tree

2 files changed

+43
-19
lines changed

2 files changed

+43
-19
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6737,6 +6737,7 @@ class Compiler
67376737

67386738
PhaseStatus fgEarlyLiveness();
67396739

6740+
template<bool ssaLiveness>
67406741
void fgMarkUseDef(GenTreeLclVarCommon* tree);
67416742

67426743
//-------------------------------------------------------------------------

src/coreclr/jit/liveness.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,27 @@
1515
#endif
1616
#include "lower.h" // for LowerRange()
1717

18-
/*****************************************************************************
19-
*
20-
* Helper for Compiler::fgPerBlockLocalVarLiveness().
21-
* The goal is to compute the USE and DEF sets for a basic block.
22-
*/
18+
//------------------------------------------------------------------------
19+
// fgMarkUseDef:
20+
// Mark a local in the current def/use set.
21+
//
22+
// Parameters:
23+
// tree - The local
24+
//
25+
// Template parameters:
26+
// ssaLiveness - Whether the liveness computed is for SSA and should follow
27+
// same modelling rules as SSA. SSA models partial defs like (v.x = 123) as
28+
// (v = v with x = 123), which also implies that these partial definitions
29+
// become uses. For dead-code elimination this is more conservative than
30+
// needed, so outside SSA we do not model partial defs in this way:
31+
//
32+
// * In SSA: Partial defs are full defs but are also uses. They impact both
33+
// bbVarUse and bbVarDef.
34+
//
35+
// * Outside SSA: Partial defs are _not_ full defs and are also not
36+
// considered uses. They do not get included in bbVarUse/bbVarDef.
37+
//
38+
template <bool ssaLiveness>
2339
void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
2440
{
2541
assert((tree->OperIsLocal() && (tree->OperGet() != GT_PHI_ARG)) || tree->OperIs(GT_LCL_ADDR));
@@ -35,8 +51,9 @@ void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
3551
varDsc->setLvRefCnt(1);
3652
}
3753

38-
const bool isDef = (tree->gtFlags & GTF_VAR_DEF) != 0;
39-
const bool isUse = !isDef || ((tree->gtFlags & GTF_VAR_USEASG) != 0);
54+
const bool isDef = ((tree->gtFlags & GTF_VAR_DEF) != 0);
55+
const bool isFullDef = isDef && ((tree->gtFlags & GTF_VAR_USEASG) == 0);
56+
const bool isUse = ssaLiveness ? !isFullDef : !isDef;
4057

4158
if (varDsc->lvTracked)
4259
{
@@ -60,7 +77,7 @@ void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
6077
VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex);
6178
}
6279

63-
if (isDef)
80+
if (ssaLiveness ? isDef : isFullDef)
6481
{
6582
// This is a def, add it to the set of defs.
6683
VarSetOps::AddElemD(this, fgCurDefSet, varDsc->lvVarIndex);
@@ -106,7 +123,7 @@ void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
106123
VarSetOps::AddElemD(this, fgCurUseSet, varIndex);
107124
}
108125

109-
if (isDef)
126+
if (ssaLiveness ? isDef : isFullDef)
110127
{
111128
VarSetOps::AddElemD(this, fgCurDefSet, varIndex);
112129
}
@@ -116,7 +133,10 @@ void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
116133
}
117134
}
118135

119-
/*****************************************************************************/
136+
//------------------------------------------------------------------------
137+
// fgLocalVarLiveness:
138+
// Compute block def/use sets, liveness, and do dead code elimination.
139+
//
120140
void Compiler::fgLocalVarLiveness()
121141
{
122142
#ifdef DEBUG
@@ -216,7 +236,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
216236
case GT_LCL_FLD:
217237
case GT_STORE_LCL_VAR:
218238
case GT_STORE_LCL_FLD:
219-
fgMarkUseDef(tree->AsLclVarCommon());
239+
fgMarkUseDef<!lowered>(tree->AsLclVarCommon());
220240
break;
221241

222242
case GT_LCL_ADDR:
@@ -229,7 +249,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
229249
break;
230250
}
231251

232-
fgMarkUseDef(tree->AsLclVarCommon());
252+
fgMarkUseDef<!lowered>(tree->AsLclVarCommon());
233253
}
234254
break;
235255

@@ -325,7 +345,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
325345
GenTreeLclVarCommon* definedLcl = gtCallGetDefinedRetBufLclAddr(call);
326346
if (definedLcl != nullptr)
327347
{
328-
fgMarkUseDef(definedLcl);
348+
fgMarkUseDef<!lowered>(definedLcl);
329349
}
330350
break;
331351
}
@@ -356,7 +376,10 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTreeHWIntrinsic* hwintrinsic)
356376
}
357377
#endif // FEATURE_HW_INTRINSICS
358378

359-
/*****************************************************************************/
379+
//------------------------------------------------------------------------
380+
// fgPerBlockLocalVarLiveness:
381+
// Compute def and use sets for the IR.
382+
//
360383
void Compiler::fgPerBlockLocalVarLiveness()
361384
{
362385
#ifdef DEBUG
@@ -419,7 +442,7 @@ void Compiler::fgPerBlockLocalVarLiveness()
419442
{
420443
for (GenTreeLclVarCommon* lcl : stmt->LocalsTreeList())
421444
{
422-
fgMarkUseDef(lcl);
445+
fgMarkUseDef<false>(lcl);
423446
}
424447
}
425448
else
@@ -436,12 +459,12 @@ void Compiler::fgPerBlockLocalVarLiveness()
436459
// qmark arms.
437460
for (GenTreeLclVarCommon* lcl : stmt->LocalsTreeList())
438461
{
439-
bool isUse = ((lcl->gtFlags & GTF_VAR_DEF) == 0) || ((lcl->gtFlags & GTF_VAR_USEASG) != 0);
462+
bool isUse = (lcl->gtFlags & GTF_VAR_DEF) == 0;
440463
// We can still handle the pure def at the top level.
441464
bool conditional = lcl != dst;
442465
if (isUse || !conditional)
443466
{
444-
fgMarkUseDef(lcl);
467+
fgMarkUseDef<false>(lcl);
445468
}
446469
}
447470
}
@@ -453,7 +476,7 @@ void Compiler::fgPerBlockLocalVarLiveness()
453476
{
454477
for (GenTreeLclVarCommon* lcl : stmt->LocalsTreeList())
455478
{
456-
fgMarkUseDef(lcl);
479+
fgMarkUseDef<false>(lcl);
457480
}
458481
}
459482
}
@@ -2088,7 +2111,7 @@ void Compiler::fgInterBlockLocalVarLiveness()
20882111
for (GenTree* cur = stmt->GetTreeListEnd(); cur != nullptr;)
20892112
{
20902113
assert(cur->OperIsAnyLocal());
2091-
bool isDef = ((cur->gtFlags & GTF_VAR_DEF) != 0) && ((cur->gtFlags & GTF_VAR_USEASG) == 0);
2114+
bool isDef = (cur->gtFlags & GTF_VAR_DEF) != 0;
20922115
bool conditional = cur != dst;
20932116
// Ignore conditional defs that would otherwise
20942117
// (incorrectly) interfere with liveness in other

0 commit comments

Comments
 (0)