Skip to content

Commit

Permalink
Include dependently promoted fields in SSA (#77238)
Browse files Browse the repository at this point in the history
* Remove quirks

* Early prop fix for call asgs

* Include all tracked locals in SSA

* Remove a (now incorrect) comment

* Fix a typo

* Encoding fixes

 1) The definition of SIMPLE_NUM_COUNT was wrong.
 2) SsaNumInfo::Composite, in the compact case, did not clear the old value.
 3) SsaNumInfo::Composite, in the outlined case, did not copy the already
    (compactly) encoded simple names.

* Fix store numbering

The load path needs to use the offset relative to
the store's target location.

* Clarify 'fieldValueOffset' calculations
  • Loading branch information
SingleAccretion authored Nov 6, 2022
1 parent 94cbc49 commit 6bae351
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 82 deletions.
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2815,7 +2815,7 @@ class Compiler
bool gtIsStaticFieldPtrToBoxedStruct(var_types fieldNodeType, CORINFO_FIELD_HANDLE fldHnd);

bool gtStoreDefinesField(
LclVarDsc* fieldVarDsc, ssize_t offset, unsigned size, ssize_t* pFieldStoreOffset, unsigned* pFileStoreSize);
LclVarDsc* fieldVarDsc, ssize_t offset, unsigned size, ssize_t* pFieldStoreOffset, unsigned* pFieldStoreSize);

// Return true if call is a recursive call; return false otherwise.
// Note when inlining, this looks for calls back to the root method.
Expand Down
16 changes: 4 additions & 12 deletions src/coreclr/jit/copyprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,20 +326,12 @@ void Compiler::optCopyPropPushDef(GenTree* defNode, GenTreeLclVarCommon* lclNode
LclVarDsc* varDsc = lvaGetDesc(lclNum);
assert(varDsc->lvPromoted);

if (varDsc->CanBeReplacedWithItsField(this))
for (unsigned index = 0; index < varDsc->lvFieldCnt; index++)
{
// TODO-CQ: remove this zero-diff quirk.
pushDef(varDsc->lvFieldLclStart, SsaConfig::RESERVED_SSA_NUM);
}
else
{
for (unsigned index = 0; index < varDsc->lvFieldCnt; index++)
unsigned ssaNum = lclNode->GetSsaNum(this, index);
if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
{
unsigned ssaNum = lclNode->GetSsaNum(this, index);
if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
{
pushDef(varDsc->lvFieldLclStart + index, ssaNum);
}
pushDef(varDsc->lvFieldLclStart + index, ssaNum);
}
}
}
Expand Down
24 changes: 16 additions & 8 deletions src/coreclr/jit/earlyprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,10 @@ GenTree* Compiler::optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropK
LclSsaVarDsc* ssaVarDsc = lvaTable[lclNum].GetPerSsaData(ssaNum);
GenTreeOp* ssaDefAsg = ssaVarDsc->GetAssignment();

if (ssaDefAsg == nullptr)
{
// Incoming parameters or live-in variables don't have actual definition tree node
// for their FIRST_SSA_NUM. See SsaBuilder::RenameVariables.
assert(ssaNum == SsaConfig::FIRST_SSA_NUM);
}
else
// Incoming parameters or live-in variables don't have actual definition tree node for
// their FIRST_SSA_NUM. Definitions induced by calls do not record the store node. See
// SsaBuilder::RenameDef.
if (ssaDefAsg != nullptr)
{
assert(ssaDefAsg->OperIs(GT_ASG));

Expand Down Expand Up @@ -569,8 +566,19 @@ GenTree* Compiler::optFindNullCheckToFold(GenTree* tree, LocalNumberToNullCheckT
return nullptr;
}

GenTree* defRHS = defLoc->GetAssignment()->gtGetOp2();
GenTree* defNode = defLoc->GetAssignment();
if (defNode == nullptr)
{
return nullptr;
}

GenTree* defLHS = defNode->gtGetOp1();
if (!defLHS->OperIs(GT_LCL_VAR) || (defLHS->AsLclVar()->GetLclNum() != lclNum))
{
return nullptr;
}

GenTree* defRHS = defNode->gtGetOp2();
if (defRHS->OperGet() != GT_COMMA)
{
return nullptr;
Expand Down
32 changes: 22 additions & 10 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17626,15 +17626,15 @@ bool Compiler::gtIsStaticFieldPtrToBoxedStruct(var_types fieldNodeType, CORINFO_
// size - Size of the store in bytes
// pFieldStoreOffset - [out] parameter for the store's offset relative
// to the field local itself
// pFileStoreSize - [out] parameter for the amount of the field's
// pFieldStoreSize - [out] parameter for the amount of the field's
// local's bytes affected by the store
//
// Return Value:
// If the given store affects the given field local, "true, "false"
// otherwise.
//
bool Compiler::gtStoreDefinesField(
LclVarDsc* fieldVarDsc, ssize_t offset, unsigned size, ssize_t* pFieldStoreOffset, unsigned* pFileStoreSize)
LclVarDsc* fieldVarDsc, ssize_t offset, unsigned size, ssize_t* pFieldStoreOffset, unsigned* pFieldStoreSize)
{
ssize_t fieldOffset = fieldVarDsc->lvFldOffset;
unsigned fieldSize = genTypeSize(fieldVarDsc); // No TYP_STRUCT field locals.
Expand All @@ -17644,7 +17644,7 @@ bool Compiler::gtStoreDefinesField(
if ((fieldOffset < storeEndOffset) && (offset < fieldEndOffset))
{
*pFieldStoreOffset = (offset < fieldOffset) ? 0 : (offset - fieldOffset);
*pFileStoreSize = static_cast<unsigned>(min(storeEndOffset, fieldEndOffset) - max(offset, fieldOffset));
*pFieldStoreSize = static_cast<unsigned>(min(storeEndOffset, fieldEndOffset) - max(offset, fieldOffset));

return true;
}
Expand Down Expand Up @@ -23790,10 +23790,10 @@ unsigned* SsaNumInfo::GetOutlinedNumSlot(Compiler* compiler, unsigned index) con
return SsaNumInfo(COMPOSITE_ENCODING_BIT | ssaNumEncoded);
}

return SsaNumInfo(ssaNumEncoded | baseNum.m_value);
return SsaNumInfo(ssaNumEncoded | (baseNum.m_value & ~(SIMPLE_NUM_MASK << (index * BITS_PER_SIMPLE_NUM))));
}

if (!baseNum.IsInvalid())
if (!baseNum.IsInvalid() && !baseNum.HasCompactFormat())
{
*baseNum.GetOutlinedNumSlot(compiler, index) = ssaNum;
return baseNum;
Expand All @@ -23807,11 +23807,23 @@ unsigned* SsaNumInfo::GetOutlinedNumSlot(Compiler* compiler, unsigned index) con
}

// Allocate a new chunk for the field numbers. Once allocated, it cannot be expanded.
int count = compiler->lvaGetDesc(parentLclNum)->lvFieldCnt;
JitExpandArrayStack<unsigned>* table = compiler->m_outlinedCompositeSsaNums;
int outIdx = table->Size();
unsigned* pLastSlot = &table->GetRef(outIdx + count - 1); // This will grow the table.
pLastSlot[-(count - 1) + static_cast<int>(index)] = ssaNum;
int count = compiler->lvaGetDesc(parentLclNum)->lvFieldCnt;
JitExpandArrayStack<unsigned>* table = compiler->m_outlinedCompositeSsaNums;
int outIdx = table->Size();
unsigned* pLastSlot = &table->GetRef(outIdx + count - 1); // This will grow the table.
unsigned* pFirstSlot = pLastSlot - count + 1;

// Copy over all of the already encoded numbers.
if (!baseNum.IsInvalid())
{
for (int i = 0; i < SIMPLE_NUM_COUNT; i++)
{
pFirstSlot[i] = baseNum.GetNum(compiler, i);
}
}

// Copy the one being set last to overwrite any previous values.
pFirstSlot[index] = ssaNum;

// Split the index if it does not fit into a small encoding.
if ((outIdx & ~OUTLINED_INDEX_LOW_MASK) != 0)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -3538,7 +3538,7 @@ class SsaNumInfo final
static const int BITS_PER_SIMPLE_NUM = 8;
static const int MAX_SIMPLE_NUM = (1 << (BITS_PER_SIMPLE_NUM - 1)) - 1;
static const int SIMPLE_NUM_MASK = MAX_SIMPLE_NUM;
static const int SIMPLE_NUM_COUNT = sizeof(int) / BITS_PER_SIMPLE_NUM;
static const int SIMPLE_NUM_COUNT = (sizeof(int) * BITS_PER_BYTE) / BITS_PER_SIMPLE_NUM;
static const int COMPOSITE_ENCODING_BIT = 1 << 31;
static const int OUTLINED_ENCODING_BIT = 1 << 15;
static const int OUTLINED_INDEX_LOW_MASK = OUTLINED_ENCODING_BIT - 1;
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/jit/morphblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,6 @@ void MorphCopyBlockHelper::TrySpecialCases()
{
assert(m_dst->OperIs(GT_LCL_VAR));

// This will exclude field locals (if any) from SSA: we do not have a way to
// associate multiple SSA definitions (SSA numbers) with one store.
m_dstVarDsc->lvIsMultiRegRet = true;

JITDUMP("Not morphing a multireg node return\n");
Expand Down
41 changes: 1 addition & 40 deletions src/coreclr/jit/ssabuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ void SsaBuilder::Build()
// Mark all variables that will be tracked by SSA
for (unsigned lclNum = 0; lclNum < m_pCompiler->lvaCount; lclNum++)
{
m_pCompiler->lvaTable[lclNum].lvInSsa = IncludeInSsa(lclNum);
m_pCompiler->lvaTable[lclNum].lvInSsa = m_pCompiler->lvaGetDesc(lclNum)->lvTracked;
}

// Insert phi functions.
Expand Down Expand Up @@ -1641,45 +1641,6 @@ void SsaBuilder::SetupBBRoot()
}
}

//------------------------------------------------------------------------
// IncludeInSsa: Check if the specified variable can be included in SSA.
//
// Arguments:
// lclNum - the variable number
//
// Return Value:
// true if the variable is included in SSA
//
bool SsaBuilder::IncludeInSsa(unsigned lclNum)
{
LclVarDsc* varDsc = m_pCompiler->lvaGetDesc(lclNum);

if (!varDsc->lvTracked)
{
return false; // SSA is only done for tracked variables
}
// lvPromoted structs are never tracked...
assert(!varDsc->lvPromoted);

if (varDsc->lvIsStructField &&
(m_pCompiler->lvaGetParentPromotionType(lclNum) != Compiler::PROMOTION_TYPE_INDEPENDENT))
{
// SSA must exclude struct fields that are not independent
// - because we don't model the struct assignment properly when multiple fields can be assigned by one struct
// assignment.
// - SSA doesn't allow a single node to contain multiple SSA definitions.
// - and PROMOTION_TYPE_DEPENDEDNT fields are never candidates for a register.
//
return false;
}
else if (varDsc->lvIsStructField && m_pCompiler->lvaGetDesc(varDsc->lvParentLcl)->lvIsMultiRegRet)
{
return false;
}
// otherwise this variable is included in SSA
return true;
}

#ifdef DEBUG

//------------------------------------------------------------------------
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/jit/ssabuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ class SsaBuilder
m_pCompiler->EndPhase(phase);
}

bool IncludeInSsa(unsigned lclNum);

public:
// Constructor
SsaBuilder(Compiler* pCompiler);
Expand Down
11 changes: 5 additions & 6 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4966,8 +4966,12 @@ void Compiler::fgValueNumberLocalStore(GenTree* storeNode,
// Avoid redundant bitcasts for the common case of a full definition.
fieldStoreType = fieldVarDsc->TypeGet();
}

// Calculate offset of this field's value, relative to the entire one.
ssize_t fieldOffset = fieldVarDsc->lvFldOffset;
ssize_t fieldValueOffset = (fieldOffset < offset) ? 0 : (fieldOffset - offset);
ValueNumPair fieldStoreValue =
vnStore->VNPairForLoad(value, storeSize, fieldStoreType, offset, fieldStoreSize);
vnStore->VNPairForLoad(value, storeSize, fieldStoreType, fieldValueOffset, fieldStoreSize);

processDef(fieldLclNum, lclDefNode->GetSsaNum(this, index), fieldStoreOffset, fieldStoreSize,
fieldStoreValue);
Expand Down Expand Up @@ -8503,11 +8507,6 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree)

rhsVNPair.SetBoth(initObjVN);
}
else if (lhs->OperIs(GT_LCL_VAR) && lhsVarDsc->CanBeReplacedWithItsField(this))
{
// TODO-CQ: remove this zero-diff quirk.
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lvaGetDesc(lhsVarDsc->lvFieldLclStart)->TypeGet()));
}
else
{
assert(tree->OperIsCopyBlkOp());
Expand Down

0 comments on commit 6bae351

Please sign in to comment.