Skip to content

Commit 9012b23

Browse files
Use SSA def descriptors in copy propagation (#65250)
* Use direct SSA defs in copy propagation * Add asserts
1 parent 30f30a3 commit 9012b23

File tree

4 files changed

+170
-80
lines changed

4 files changed

+170
-80
lines changed

src/coreclr/jit/compiler.h

+58-7
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,13 @@ class SsaDefArray
352352
assert(ssaNum != SsaConfig::RESERVED_SSA_NUM);
353353
return GetSsaDefByIndex(ssaNum - GetMinSsaNum());
354354
}
355+
356+
// Get an SSA number associated with the specified SSA def (that must be in this array).
357+
unsigned GetSsaNum(T* ssaDef)
358+
{
359+
assert((m_array <= ssaDef) && (ssaDef < &m_array[m_count]));
360+
return GetMinSsaNum() + static_cast<unsigned>(ssaDef - &m_array[0]);
361+
}
355362
};
356363

357364
enum RefCountState
@@ -1082,6 +1089,13 @@ class LclVarDsc
10821089
return lvPerSsaData.GetSsaDef(ssaNum);
10831090
}
10841091

1092+
// Returns the SSA number for "ssaDef". Requires "ssaDef" to be a valid definition
1093+
// of this variable.
1094+
unsigned GetSsaNumForSsaDef(LclSsaVarDsc* ssaDef)
1095+
{
1096+
return lvPerSsaData.GetSsaNum(ssaDef);
1097+
}
1098+
10851099
var_types GetRegisterType(const GenTreeLclVarCommon* tree) const;
10861100

10871101
var_types GetRegisterType() const;
@@ -7399,17 +7413,54 @@ class Compiler
73997413

74007414
public:
74017415
// VN based copy propagation.
7402-
typedef ArrayStack<GenTree*> GenTreePtrStack;
7403-
typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, GenTreePtrStack*> LclNumToGenTreePtrStack;
7416+
7417+
// In DEBUG builds, we'd like to know the tree that the SSA definition was pushed for.
7418+
// While for ordinary SSA defs it will be available (as an ASG) in the SSA descriptor,
7419+
// for locals which will use "definitions from uses", it will not be, so we store it
7420+
// in this class instead.
7421+
class CopyPropSsaDef
7422+
{
7423+
LclSsaVarDsc* m_ssaDef;
7424+
#ifdef DEBUG
7425+
GenTree* m_defNode;
7426+
#endif
7427+
public:
7428+
CopyPropSsaDef(LclSsaVarDsc* ssaDef, GenTree* defNode)
7429+
: m_ssaDef(ssaDef)
7430+
#ifdef DEBUG
7431+
, m_defNode(defNode)
7432+
#endif
7433+
{
7434+
}
7435+
7436+
LclSsaVarDsc* GetSsaDef() const
7437+
{
7438+
return m_ssaDef;
7439+
}
7440+
7441+
#ifdef DEBUG
7442+
GenTree* GetDefNode() const
7443+
{
7444+
return m_defNode;
7445+
}
7446+
#endif
7447+
};
7448+
7449+
typedef ArrayStack<CopyPropSsaDef> CopyPropSsaDefStack;
7450+
typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, CopyPropSsaDefStack*> LclNumToLiveDefsMap;
74047451

74057452
// Copy propagation functions.
7406-
void optCopyProp(Statement* stmt, GenTreeLclVarCommon* tree, unsigned lclNum, LclNumToGenTreePtrStack* curSsaName);
7407-
void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName);
7408-
void optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName);
7453+
void optCopyProp(Statement* stmt, GenTreeLclVarCommon* tree, unsigned lclNum, LclNumToLiveDefsMap* curSsaName);
7454+
void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
7455+
void optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
7456+
void optCopyPropPushDef(GenTreeOp* asg,
7457+
GenTreeLclVarCommon* lclNode,
7458+
unsigned lclNum,
7459+
LclNumToLiveDefsMap* curSsaName);
74097460
unsigned optIsSsaLocal(GenTreeLclVarCommon* lclNode);
7410-
int optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2);
7461+
int optCopyProp_LclVarScore(const LclVarDsc* lclVarDsc, const LclVarDsc* copyVarDsc, bool preferOp2);
74117462
void optVnCopyProp();
7412-
INDEBUG(void optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName));
7463+
INDEBUG(void optDumpCopyPropStack(LclNumToLiveDefsMap* curSsaName));
74137464

74147465
/**************************************************************************
74157466
* Early value propagation

src/coreclr/jit/copyprop.cpp

+110-71
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* of the graph originating from the block. Refer SSA renaming for any additional info.
2727
* "curSsaName" tracks the currently live definitions.
2828
*/
29-
void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName)
29+
void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap* curSsaName)
3030
{
3131
for (Statement* const stmt : block->Statements())
3232
{
@@ -42,7 +42,7 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrSt
4242
continue;
4343
}
4444

45-
GenTreePtrStack* stack = nullptr;
45+
CopyPropSsaDefStack* stack = nullptr;
4646
curSsaName->Lookup(lclNum, &stack);
4747
stack->Pop();
4848
if (stack->Empty())
@@ -55,12 +55,12 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrSt
5555
}
5656

5757
#ifdef DEBUG
58-
void Compiler::optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName)
58+
void Compiler::optDumpCopyPropStack(LclNumToLiveDefsMap* curSsaName)
5959
{
6060
JITDUMP("{ ");
61-
for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
61+
for (LclNumToLiveDefsMap::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
6262
{
63-
GenTreeLclVarCommon* lclVar = iter.GetValue()->Top()->AsLclVarCommon();
63+
GenTreeLclVarCommon* lclVar = iter.GetValue()->Top().GetDefNode()->AsLclVarCommon();
6464
unsigned ssaLclNum = optIsSsaLocal(lclVar);
6565
assert(ssaLclNum != BAD_VAR_NUM);
6666

@@ -82,7 +82,7 @@ void Compiler::optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName)
8282
* Given the "lclVar" and "copyVar" compute if the copy prop will be beneficial.
8383
*
8484
*/
85-
int Compiler::optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2)
85+
int Compiler::optCopyProp_LclVarScore(const LclVarDsc* lclVarDsc, const LclVarDsc* copyVarDsc, bool preferOp2)
8686
{
8787
int score = 0;
8888

@@ -126,16 +126,17 @@ int Compiler::optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDs
126126
// tree - The local tree to perform copy propagation on
127127
// lclNum - The local number of said tree
128128
// curSsaName - The map from lclNum to its recently live definitions as a stack
129-
130-
void Compiler::optCopyProp(Statement* stmt,
131-
GenTreeLclVarCommon* tree,
132-
unsigned lclNum,
133-
LclNumToGenTreePtrStack* curSsaName)
129+
//
130+
void Compiler::optCopyProp(Statement* stmt, GenTreeLclVarCommon* tree, unsigned lclNum, LclNumToLiveDefsMap* curSsaName)
134131
{
135132
assert((lclNum != BAD_VAR_NUM) && (optIsSsaLocal(tree) == lclNum) && ((tree->gtFlags & GTF_VAR_DEF) == 0));
136-
assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN);
133+
assert(tree->gtVNPair.BothDefined());
137134

138-
for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
135+
LclVarDsc* varDsc = lvaGetDesc(lclNum);
136+
ValueNum lclDefVN = varDsc->GetPerSsaData(tree->GetSsaNum())->m_vnPair.GetConservative();
137+
assert(lclDefVN != ValueNumStore::NoVN);
138+
139+
for (LclNumToLiveDefsMap::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
139140
{
140141
unsigned newLclNum = iter.Get();
141142

@@ -145,35 +146,30 @@ void Compiler::optCopyProp(Statement* stmt,
145146
continue;
146147
}
147148

148-
LclVarDsc* varDsc = lvaGetDesc(lclNum);
149-
LclVarDsc* newLclVarDsc = lvaGetDesc(newLclNum);
150-
GenTree* newLclDefNode = iter.GetValue()->Top(); // Note that this "def" node can actually be a use (for
151-
// parameters and other use-before-def locals).
149+
CopyPropSsaDef newLclDef = iter.GetValue()->Top();
150+
LclSsaVarDsc* newLclSsaDef = newLclDef.GetSsaDef();
152151

153-
// Do not copy propagate if the old and new lclVar have different 'doNotEnregister' settings.
154-
// This is primarily to avoid copy propagating to IND(ADDR(LCL_VAR)) where the replacement lclVar
155-
// is not marked 'lvDoNotEnregister'.
156-
// However, in addition, it may not be profitable to propagate a 'doNotEnregister' lclVar to an
157-
// existing use of an enregisterable lclVar.
158-
if (varDsc->lvDoNotEnregister != newLclVarDsc->lvDoNotEnregister)
152+
// Likewise, nothing to do if the most recent def is not available.
153+
if (newLclSsaDef == nullptr)
159154
{
160155
continue;
161156
}
162157

163-
if ((gsShadowVarInfo != nullptr) && newLclVarDsc->lvIsParam &&
164-
(gsShadowVarInfo[newLclNum].shadowCopy != BAD_VAR_NUM))
165-
{
166-
continue;
167-
}
158+
ValueNum newLclDefVN = newLclSsaDef->m_vnPair.GetConservative();
159+
assert(newLclDefVN != ValueNumStore::NoVN);
168160

169-
ValueNum newLclDefVN = GetUseAsgDefVNOrTreeVN(newLclDefNode);
170-
if (newLclDefVN == ValueNumStore::NoVN)
161+
if (newLclDefVN != lclDefVN)
171162
{
172163
continue;
173164
}
174165

175-
ValueNum lclDefVN = varDsc->GetPerSsaData(tree->GetSsaNum())->m_vnPair.GetConservative();
176-
if (newLclDefVN != lclDefVN)
166+
// Do not copy propagate if the old and new lclVar have different 'doNotEnregister' settings.
167+
// This is primarily to avoid copy propagating to IND(ADDR(LCL_VAR)) where the replacement lclVar
168+
// is not marked 'lvDoNotEnregister'.
169+
// However, in addition, it may not be profitable to propagate a 'doNotEnregister' lclVar to an
170+
// existing use of an enregisterable lclVar.
171+
LclVarDsc* newLclVarDsc = lvaGetDesc(newLclNum);
172+
if (varDsc->lvDoNotEnregister != newLclVarDsc->lvDoNotEnregister)
177173
{
178174
continue;
179175
}
@@ -182,6 +178,7 @@ void Compiler::optCopyProp(Statement* stmt,
182178
{
183179
continue;
184180
}
181+
185182
// Check whether the newLclNum is live before being substituted. Otherwise, we could end
186183
// up in a situation where there must've been a phi node that got pruned because the variable
187184
// is not live anymore. For example,
@@ -197,7 +194,7 @@ void Compiler::optCopyProp(Statement* stmt,
197194
// 'c' with 'x.'
198195

199196
// We compute liveness only on tracked variables. And all SSA locals are tracked.
200-
assert(lvaGetDesc(newLclNum)->lvTracked);
197+
assert(newLclVarDsc->lvTracked);
201198

202199
// Because of this dependence on live variable analysis, CopyProp phase is immediately
203200
// after Liveness, SSA and VN.
@@ -206,21 +203,6 @@ void Compiler::optCopyProp(Statement* stmt,
206203
continue;
207204
}
208205

209-
unsigned newSsaNum = SsaConfig::RESERVED_SSA_NUM;
210-
if (newLclDefNode->gtFlags & GTF_VAR_DEF)
211-
{
212-
newSsaNum = GetSsaNumForLocalVarDef(newLclDefNode);
213-
}
214-
else // parameters, this pointer etc.
215-
{
216-
newSsaNum = newLclDefNode->AsLclVarCommon()->GetSsaNum();
217-
}
218-
219-
if (newSsaNum == SsaConfig::RESERVED_SSA_NUM)
220-
{
221-
continue;
222-
}
223-
224206
var_types newLclType = newLclVarDsc->TypeGet();
225207
if (!newLclVarDsc->lvNormalizeOnLoad())
226208
{
@@ -238,12 +220,15 @@ void Compiler::optCopyProp(Statement* stmt,
238220
JITDUMP("VN based copy assertion for ");
239221
printTreeID(tree);
240222
printf(" V%02d " FMT_VN " by ", lclNum, lclDefVN);
241-
printTreeID(newLclDefNode);
223+
printTreeID(newLclDef.GetDefNode());
242224
printf(" V%02d " FMT_VN ".\n", newLclNum, newLclDefVN);
243225
DISPNODE(tree);
244226
}
245227
#endif
246228

229+
unsigned newSsaNum = newLclVarDsc->GetSsaNumForSsaDef(newLclSsaDef);
230+
assert(newSsaNum != SsaConfig::RESERVED_SSA_NUM);
231+
247232
tree->AsLclVarCommon()->SetLclNum(newLclNum);
248233
tree->AsLclVarCommon()->SetSsaNum(newSsaNum);
249234
gtUpdateSideEffects(stmt, tree);
@@ -288,6 +273,74 @@ unsigned Compiler::optIsSsaLocal(GenTreeLclVarCommon* lclNode)
288273
return lclNum;
289274
}
290275

276+
//------------------------------------------------------------------------------
277+
// optCopyPropPushDef: Push the new live SSA def on the stack for "lclNode".
278+
//
279+
// Arguments:
280+
// asg - The assignment node for this def (will be "nullptr" for "use" defs)
281+
// lclNode - The local tree representing "the def" (that can actually be a use)
282+
// lclNum - The local's number (see "optIsSsaLocal")
283+
// curSsaName - The map of local numbers to stacks of their defs
284+
//
285+
void Compiler::optCopyPropPushDef(GenTreeOp* asg,
286+
GenTreeLclVarCommon* lclNode,
287+
unsigned lclNum,
288+
LclNumToLiveDefsMap* curSsaName)
289+
{
290+
assert((lclNum != BAD_VAR_NUM) && (lclNum == optIsSsaLocal(lclNode)));
291+
292+
// Shadowed parameters are special: they will (at most) have one use, that is one on the RHS of an
293+
// assignment to their shadow, and we must not substitute them anywhere. So we'll not push any defs.
294+
if ((gsShadowVarInfo != nullptr) && lvaGetDesc(lclNum)->lvIsParam &&
295+
(gsShadowVarInfo[lclNum].shadowCopy != BAD_VAR_NUM))
296+
{
297+
assert(!curSsaName->Lookup(lclNum));
298+
return;
299+
}
300+
301+
unsigned ssaDefNum = SsaConfig::RESERVED_SSA_NUM;
302+
if (asg == nullptr)
303+
{
304+
// Parameters, this pointer etc.
305+
assert((lclNode->gtFlags & GTF_VAR_DEF) == 0);
306+
assert(lclNode->GetSsaNum() == SsaConfig::FIRST_SSA_NUM);
307+
ssaDefNum = lclNode->GetSsaNum();
308+
}
309+
else
310+
{
311+
assert((lclNode->gtFlags & GTF_VAR_DEF) != 0);
312+
ssaDefNum = GetSsaNumForLocalVarDef(lclNode);
313+
314+
// This will be "RESERVED_SSA_NUM" for promoted struct fields assigned using the parent struct.
315+
// TODO-CQ: fix this.
316+
assert((ssaDefNum != SsaConfig::RESERVED_SSA_NUM) || lvaGetDesc(lclNode)->CanBeReplacedWithItsField(this));
317+
}
318+
319+
// The default is "not available".
320+
LclSsaVarDsc* ssaDef = nullptr;
321+
if (ssaDefNum != SsaConfig::RESERVED_SSA_NUM)
322+
{
323+
// This code preserves previous behavior. In principle, "ssaDefVN" should
324+
// always be obtained directly from the SSA def descriptor and be valid.
325+
ValueNum ssaDefVN = GetUseAsgDefVNOrTreeVN(lclNode);
326+
assert(ssaDefVN != ValueNumStore::NoVN);
327+
328+
if (ssaDefVN != ValueNumStore::VNForVoid())
329+
{
330+
ssaDef = lvaGetDesc(lclNum)->GetPerSsaData(ssaDefNum);
331+
}
332+
}
333+
334+
CopyPropSsaDefStack* defStack;
335+
if (!curSsaName->Lookup(lclNum, &defStack))
336+
{
337+
defStack = new (curSsaName->GetAllocator()) CopyPropSsaDefStack(curSsaName->GetAllocator());
338+
curSsaName->Set(lclNum, defStack);
339+
}
340+
341+
defStack->Push(CopyPropSsaDef(ssaDef, lclNode));
342+
}
343+
291344
//------------------------------------------------------------------------------
292345
// optBlockCopyProp : Perform copy propagation using currently live definitions on the current block's
293346
// variables. Also as new definitions are encountered update the "curSsaName" which
@@ -296,8 +349,8 @@ unsigned Compiler::optIsSsaLocal(GenTreeLclVarCommon* lclNode)
296349
// Arguments:
297350
// block - Block the tree belongs to
298351
// curSsaName - The map from lclNum to its recently live definitions as a stack
299-
300-
void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName)
352+
//
353+
void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaName)
301354
{
302355
#ifdef DEBUG
303356
JITDUMP("Copy Assertion for " FMT_BB "\n", block->bbNum);
@@ -333,13 +386,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
333386
continue;
334387
}
335388

336-
GenTreePtrStack* stack;
337-
if (!curSsaName->Lookup(lclNum, &stack))
338-
{
339-
stack = new (curSsaName->GetAllocator()) GenTreePtrStack(curSsaName->GetAllocator());
340-
}
341-
stack->Push(lclDefNode);
342-
curSsaName->Set(lclNum, stack, LclNumToGenTreePtrStack::Overwrite);
389+
optCopyPropPushDef(tree->AsOp(), lclDefNode, lclNum, curSsaName);
343390
}
344391
// TODO-CQ: propagate on LCL_FLDs too.
345392
else if (tree->OperIs(GT_LCL_VAR) && ((tree->gtFlags & GTF_VAR_DEF) == 0))
@@ -351,19 +398,11 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
351398
continue;
352399
}
353400

354-
// If we encounter first use of a param or this pointer add it as a live definition.
355-
// Since they are always live, we'll do it only once.
356-
if (lvaGetDesc(lclNum)->lvIsParam || (lclNum == info.compThisArg))
401+
// If we encounter first use of a param or this pointer add it as a
402+
// live definition. Since they are always live, we'll do it only once.
403+
if ((lvaGetDesc(lclNum)->lvIsParam || (lclNum == info.compThisArg)) && !curSsaName->Lookup(lclNum))
357404
{
358-
GenTreePtrStack* stack;
359-
if (!curSsaName->Lookup(lclNum, &stack))
360-
{
361-
assert(tree->AsLclVarCommon()->GetSsaNum() == SsaConfig::FIRST_SSA_NUM);
362-
363-
stack = new (curSsaName->GetAllocator()) GenTreePtrStack(curSsaName->GetAllocator());
364-
stack->Push(tree);
365-
curSsaName->Set(lclNum, stack);
366-
}
405+
optCopyPropPushDef(nullptr, tree->AsLclVarCommon(), lclNum, curSsaName);
367406
}
368407

369408
// TODO-Review: EH successor/predecessor iteration seems broken.
@@ -422,7 +461,7 @@ void Compiler::optVnCopyProp()
422461
class CopyPropDomTreeVisitor : public DomTreeVisitor<CopyPropDomTreeVisitor>
423462
{
424463
// The map from lclNum to its recently live definitions as a stack.
425-
LclNumToGenTreePtrStack m_curSsaName;
464+
LclNumToLiveDefsMap m_curSsaName;
426465

427466
public:
428467
CopyPropDomTreeVisitor(Compiler* compiler)

0 commit comments

Comments
 (0)