Skip to content

Commit 23323e9

Browse files
authored
JIT: Preparatory refactoring to support multiple defs per single node (#117662)
Replace `GenTree::DefinesLocal` by a `GenTree::VisitLocalDefs` to prepare for being able to allow a single node to have multiple definitions. Update uses to use the new function. This will be used to allow async calls to define a new value that represents whether the call suspended or not. This is necessary because runtime async calls should save and restore `Thread.CurrentThread._synchronizationContext`, but the restore should only happen when the call finishes synchronously (with an exception or not). We need a proper representation in the JIT IR of these conditions to properly be able to model these conditions in the face of inlining. In a future change the expectation is to add a `LCL_ADDR` node as a new well-known argument to async calls that will be a definition that works similar to ret buffers. The async transformation will later expand the definition out in the resumption path.
1 parent 4b97676 commit 23323e9

File tree

14 files changed

+351
-294
lines changed

14 files changed

+351
-294
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7321,7 +7321,7 @@ class Compiler
73217321
LclNumToLiveDefsMap* curSsaName);
73227322
void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
73237323
bool optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
7324-
void optCopyPropPushDef(GenTree* defNode, GenTreeLclVarCommon* lclNode, LclNumToLiveDefsMap* curSsaName);
7324+
void optCopyPropPushDef(GenTreeLclVarCommon* lclNode, LclNumToLiveDefsMap* curSsaName);
73257325
int optCopyProp_LclVarScore(const LclVarDsc* lclVarDsc, const LclVarDsc* copyVarDsc, bool preferOp2);
73267326
PhaseStatus optVnCopyProp();
73277327
INDEBUG(void optDumpCopyPropStack(LclNumToLiveDefsMap* curSsaName));

src/coreclr/jit/compiler.hpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4683,6 +4683,105 @@ GenTree::VisitResult GenTree::VisitOperands(TVisitor visitor)
46834683
}
46844684
}
46854685

4686+
//------------------------------------------------------------------------
4687+
// VisitLocalDefs: Visit locals being defined by this node.
4688+
//
4689+
// Arguments:
4690+
// comp - the compiler instance
4691+
// visitor - Functor of type GenTree::VisitResult(LocalDef)
4692+
//
4693+
// Return Value:
4694+
// VisitResult::Abort if the functor aborted; otherwise VisitResult::Continue.
4695+
//
4696+
// Notes:
4697+
// This function is contractually bound to recognize a superset of stores
4698+
// that "LocalAddressVisitor" recognizes and transforms, as it is used to
4699+
// detect which trees can define tracked locals.
4700+
//
4701+
template <typename TVisitor>
4702+
GenTree::VisitResult GenTree::VisitLocalDefs(Compiler* comp, TVisitor visitor)
4703+
{
4704+
if (OperIs(GT_STORE_LCL_VAR))
4705+
{
4706+
unsigned size = comp->lvaLclExactSize(AsLclVarCommon()->GetLclNum());
4707+
return visitor(LocalDef(AsLclVarCommon(), /* isEntire */ true, 0, size));
4708+
}
4709+
if (OperIs(GT_STORE_LCL_FLD))
4710+
{
4711+
GenTreeLclFld* fld = AsLclFld();
4712+
return visitor(LocalDef(fld, !fld->IsPartialLclFld(comp), fld->GetLclOffs(), fld->GetSize()));
4713+
}
4714+
if (OperIs(GT_CALL))
4715+
{
4716+
GenTreeLclVarCommon* lclAddr = comp->gtCallGetDefinedRetBufLclAddr(AsCall());
4717+
if (lclAddr != nullptr)
4718+
{
4719+
unsigned storeSize = comp->typGetObjLayout(AsCall()->gtRetClsHnd)->GetSize();
4720+
4721+
bool isEntire = storeSize == comp->lvaLclExactSize(lclAddr->GetLclNum());
4722+
4723+
return visitor(LocalDef(lclAddr, isEntire, lclAddr->GetLclOffs(), storeSize));
4724+
}
4725+
}
4726+
4727+
return VisitResult::Continue;
4728+
}
4729+
4730+
//------------------------------------------------------------------------
4731+
// VisitLocalDefNodes: Visit GenTreeLclVarCommon nodes representing definitions in the specified node.
4732+
//
4733+
// Arguments:
4734+
// comp - the compiler instance
4735+
// visitor - Functor of type GenTree::VisitResult(GenTreeLclVarCommon*)
4736+
//
4737+
// Return Value:
4738+
// VisitResult::Abort if the functor aborted; otherwise VisitResult::Continue.
4739+
//
4740+
// Notes:
4741+
// This function is contractually bound to recognize a superset of stores
4742+
// that "LocalAddressVisitor" recognizes and transforms, as it is used to
4743+
// detect which trees can define tracked locals.
4744+
//
4745+
template <typename TVisitor>
4746+
GenTree::VisitResult GenTree::VisitLocalDefNodes(Compiler* comp, TVisitor visitor)
4747+
{
4748+
if (OperIs(GT_STORE_LCL_VAR))
4749+
{
4750+
return visitor(AsLclVarCommon());
4751+
}
4752+
if (OperIs(GT_STORE_LCL_FLD))
4753+
{
4754+
return visitor(AsLclFld());
4755+
}
4756+
if (OperIs(GT_CALL))
4757+
{
4758+
GenTreeLclVarCommon* lclAddr = comp->gtCallGetDefinedRetBufLclAddr(AsCall());
4759+
if (lclAddr != nullptr)
4760+
{
4761+
return visitor(lclAddr);
4762+
}
4763+
}
4764+
4765+
return VisitResult::Continue;
4766+
}
4767+
4768+
//------------------------------------------------------------------------
4769+
// HasAnyLocalDefs:
4770+
// Check if a tree is considered as defining any locals.
4771+
//
4772+
// Arguments:
4773+
// comp - the compiler instance
4774+
//
4775+
// Return Value:
4776+
// True if it is.
4777+
//
4778+
inline bool GenTree::HasAnyLocalDefs(Compiler* comp)
4779+
{
4780+
return VisitLocalDefNodes(comp, [](GenTreeLclVarCommon* lcl) {
4781+
return GenTree::VisitResult::Abort;
4782+
}) == GenTree::VisitResult::Abort;
4783+
}
4784+
46864785
/*****************************************************************************
46874786
* operator new
46884787
*

src/coreclr/jit/copyprop.cpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,31 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap*
4646
{
4747
for (GenTree* const tree : stmt->TreeList())
4848
{
49-
GenTreeLclVarCommon* lclDefNode = nullptr;
50-
if (tree->OperIsSsaDef() && tree->DefinesLocal(this, &lclDefNode))
49+
if (!tree->OperIsSsaDef())
5150
{
52-
if (lclDefNode->HasCompositeSsaName())
51+
continue;
52+
}
53+
54+
auto visitDef = [=](GenTreeLclVarCommon* lcl) {
55+
if (lcl->HasCompositeSsaName())
5356
{
54-
LclVarDsc* varDsc = lvaGetDesc(lclDefNode);
57+
LclVarDsc* varDsc = lvaGetDesc(lcl);
5558
assert(varDsc->lvPromoted);
5659

5760
for (unsigned index = 0; index < varDsc->lvFieldCnt; index++)
5861
{
59-
popDef(varDsc->lvFieldLclStart + index, lclDefNode->GetSsaNum(this, index));
62+
popDef(varDsc->lvFieldLclStart + index, lcl->GetSsaNum(this, index));
6063
}
6164
}
6265
else
6366
{
64-
popDef(lclDefNode->GetLclNum(), lclDefNode->GetSsaNum());
67+
popDef(lcl->GetLclNum(), lcl->GetSsaNum());
6568
}
66-
}
69+
70+
return GenTree::VisitResult::Continue;
71+
};
72+
73+
tree->VisitLocalDefNodes(this, visitDef);
6774
}
6875
}
6976
}
@@ -304,11 +311,10 @@ bool Compiler::optCopyProp(
304311
// optCopyPropPushDef: Push the new live SSA def on the stack for "lclNode".
305312
//
306313
// Arguments:
307-
// defNode - The definition node for this def (store/GT_CALL) (will be "nullptr" for "use" defs)
308314
// lclNode - The local tree representing "the def"
309315
// curSsaName - The map of local numbers to stacks of their defs
310316
//
311-
void Compiler::optCopyPropPushDef(GenTree* defNode, GenTreeLclVarCommon* lclNode, LclNumToLiveDefsMap* curSsaName)
317+
void Compiler::optCopyPropPushDef(GenTreeLclVarCommon* lclNode, LclNumToLiveDefsMap* curSsaName)
312318
{
313319
unsigned lclNum = lclNode->GetLclNum();
314320

@@ -400,10 +406,14 @@ bool Compiler::optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaNa
400406
{
401407
treeLifeUpdater.UpdateLife(tree);
402408

403-
GenTreeLclVarCommon* lclDefNode = nullptr;
404-
if (tree->OperIsSsaDef() && tree->DefinesLocal(this, &lclDefNode))
409+
if (tree->OperIsSsaDef())
405410
{
406-
optCopyPropPushDef(tree, lclDefNode, curSsaName);
411+
auto visitDef = [=](GenTreeLclVarCommon* lcl) {
412+
optCopyPropPushDef(lcl, curSsaName);
413+
return GenTree::VisitResult::Continue;
414+
};
415+
416+
tree->VisitLocalDefNodes(this, visitDef);
407417
}
408418
else if (tree->OperIs(GT_LCL_VAR, GT_LCL_FLD) && tree->AsLclVarCommon()->HasSsaName())
409419
{
@@ -413,7 +423,7 @@ bool Compiler::optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaNa
413423
// live definition. Since they are always live, we'll do it only once.
414424
if ((lvaGetDesc(lclNum)->lvIsParam || (lclNum == info.compThisArg)) && !curSsaName->Lookup(lclNum))
415425
{
416-
optCopyPropPushDef(nullptr, tree->AsLclVarCommon(), curSsaName);
426+
optCopyPropPushDef(tree->AsLclVarCommon(), curSsaName);
417427
}
418428

419429
// TODO-Review: EH successor/predecessor iteration seems broken.

src/coreclr/jit/fgdiagnostic.cpp

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4288,61 +4288,58 @@ class SsaCheckVisitor : public GenTreeVisitor<SsaCheckVisitor>
42884288

42894289
void ProcessDefs(GenTree* tree)
42904290
{
4291-
GenTreeLclVarCommon* lclNode;
4292-
bool isFullDef = false;
4293-
ssize_t offset = 0;
4294-
unsigned storeSize = 0;
4295-
bool definesLocal = tree->DefinesLocal(m_compiler, &lclNode, &isFullDef, &offset, &storeSize);
4296-
4297-
if (!definesLocal)
4298-
{
4299-
return;
4300-
}
4301-
4302-
const bool isUse = (lclNode->gtFlags & GTF_VAR_USEASG) != 0;
4303-
unsigned const lclNum = lclNode->GetLclNum();
4304-
LclVarDsc* const varDsc = m_compiler->lvaGetDesc(lclNum);
4291+
auto visitDef = [=](const LocalDef& def) {
4292+
const bool isUse = (def.Def->gtFlags & GTF_VAR_USEASG) != 0;
4293+
unsigned const lclNum = def.Def->GetLclNum();
4294+
LclVarDsc* const varDsc = m_compiler->lvaGetDesc(lclNum);
43054295

4306-
assert(!(isFullDef && isUse));
4296+
assert(!(def.IsEntire && isUse));
43074297

4308-
if (lclNode->HasCompositeSsaName())
4309-
{
4310-
for (unsigned index = 0; index < varDsc->lvFieldCnt; index++)
4298+
if (def.Def->HasCompositeSsaName())
43114299
{
4312-
unsigned const fieldLclNum = varDsc->lvFieldLclStart + index;
4313-
LclVarDsc* const fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum);
4314-
unsigned const fieldSsaNum = lclNode->GetSsaNum(m_compiler, index);
4315-
4316-
ssize_t fieldStoreOffset;
4317-
unsigned fieldStoreSize;
4318-
if (m_compiler->gtStoreDefinesField(fieldVarDsc, offset, storeSize, &fieldStoreOffset, &fieldStoreSize))
4300+
for (unsigned index = 0; index < varDsc->lvFieldCnt; index++)
43194301
{
4320-
ProcessDef(lclNode, fieldLclNum, fieldSsaNum);
4321-
4322-
if (!ValueNumStore::LoadStoreIsEntire(genTypeSize(fieldVarDsc), fieldStoreOffset, fieldStoreSize))
4302+
unsigned const fieldLclNum = varDsc->lvFieldLclStart + index;
4303+
LclVarDsc* const fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum);
4304+
unsigned const fieldSsaNum = def.Def->GetSsaNum(m_compiler, index);
4305+
4306+
ssize_t fieldStoreOffset;
4307+
unsigned fieldStoreSize;
4308+
if (m_compiler->gtStoreDefinesField(fieldVarDsc, def.Offset, def.Size, &fieldStoreOffset,
4309+
&fieldStoreSize))
43234310
{
4324-
assert(isUse);
4325-
unsigned const fieldUseSsaNum = fieldVarDsc->GetPerSsaData(fieldSsaNum)->GetUseDefSsaNum();
4326-
ProcessUse(lclNode, fieldLclNum, fieldUseSsaNum);
4311+
ProcessDef(def.Def, fieldLclNum, fieldSsaNum);
4312+
4313+
if (!ValueNumStore::LoadStoreIsEntire(genTypeSize(fieldVarDsc), fieldStoreOffset,
4314+
fieldStoreSize))
4315+
{
4316+
assert(isUse);
4317+
unsigned const fieldUseSsaNum = fieldVarDsc->GetPerSsaData(fieldSsaNum)->GetUseDefSsaNum();
4318+
ProcessUse(def.Def, fieldLclNum, fieldUseSsaNum);
4319+
}
43274320
}
43284321
}
43294322
}
4330-
}
4331-
else
4332-
{
4333-
unsigned const ssaNum = lclNode->GetSsaNum();
4334-
ProcessDef(lclNode, lclNum, ssaNum);
4335-
4336-
if (isUse)
4323+
else
43374324
{
4338-
unsigned useSsaNum = SsaConfig::RESERVED_SSA_NUM;
4339-
if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
4325+
unsigned const ssaNum = def.Def->GetSsaNum();
4326+
ProcessDef(def.Def, lclNum, ssaNum);
4327+
4328+
if (isUse)
43404329
{
4341-
useSsaNum = varDsc->GetPerSsaData(ssaNum)->GetUseDefSsaNum();
4330+
unsigned useSsaNum = SsaConfig::RESERVED_SSA_NUM;
4331+
if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
4332+
{
4333+
useSsaNum = varDsc->GetPerSsaData(ssaNum)->GetUseDefSsaNum();
4334+
}
4335+
ProcessUse(def.Def, lclNum, useSsaNum);
43424336
}
4343-
ProcessUse(lclNode, lclNum, useSsaNum);
43444337
}
4345-
}
4338+
4339+
return GenTree::VisitResult::Continue;
4340+
};
4341+
4342+
tree->VisitLocalDefs(m_compiler, visitDef);
43464343
}
43474344

43484345
void ProcessUse(GenTreeLclVarCommon* tree, unsigned lclNum, unsigned ssaNum)

src/coreclr/jit/flowgraph.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5387,11 +5387,13 @@ bool FlowGraphNaturalLoop::VisitDefs(TFunc func)
53875387
return Compiler::WALK_SKIP_SUBTREES;
53885388
}
53895389

5390-
GenTreeLclVarCommon* lclDef;
5391-
if (tree->DefinesLocal(m_compiler, &lclDef))
5390+
auto visitDef = [=](GenTreeLclVarCommon* lcl) {
5391+
return m_func(lcl) ? GenTree::VisitResult::Continue : GenTree::VisitResult::Abort;
5392+
};
5393+
5394+
if (tree->VisitLocalDefNodes(m_compiler, visitDef) == GenTree::VisitResult::Abort)
53925395
{
5393-
if (!m_func(lclDef))
5394-
return Compiler::WALK_ABORT;
5396+
return Compiler::WALK_ABORT;
53955397
}
53965398

53975399
return Compiler::WALK_CONTINUE;

0 commit comments

Comments
 (0)