Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13886,7 +13886,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn))
{
// Append a tree to zero-out the temp
newObjThisPtr = gtNewLclvNode(lclNum, lvaTable[lclNum].TypeGet());
newObjThisPtr = gtNewLclvNode(lclNum, lclDsc->TypeGet());

newObjThisPtr = gtNewBlkOpNode(newObjThisPtr, // Dest
gtNewIconNode(0), // Value
Expand Down
8 changes: 7 additions & 1 deletion src/coreclr/src/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3387,13 +3387,19 @@ void Compiler::lvaSortByRefCount()
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
}
}
else if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
else if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT) &&
(lvaGetDesc(varDsc->lvParentLcl)->lvRefCnt() > 1))
{
// SSA must exclude struct fields that are not independently promoted
// as dependent fields could be assigned using a CopyBlock
// resulting in a single node causing multiple SSA definitions
// which isn't currently supported by SSA
//
// If the parent struct local ref count is less than 2, then either the struct is no longer
// referenced or the field is no longer referenced: we increment the struct local ref count in incRefCnts
// for each field use when the struct is dependently promoted. This can happen, e.g, if we've removed
// a block initialization for the struct. In that case we can still track the local field.
//
// TODO-CQ: Consider using lvLclBlockOpAddr and only marking these LclVars
// untracked when a blockOp is used to assign the struct.
//
Expand Down
21 changes: 11 additions & 10 deletions src/coreclr/src/jit/liveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2435,24 +2435,25 @@ void Compiler::fgInterBlockLocalVarLiveness()

for (varNum = 0, varDsc = lvaTable; varNum < lvaCount; varNum++, varDsc++)
{
/* Ignore the variable if it's not tracked */
// Ignore the variable if it's not tracked

if (!varDsc->lvTracked)
{
continue;
}

if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
{
continue;
}
// Fields of dependently promoted structs may be tracked. We shouldn't set lvMustInit on them since
// the whole parent struct will be initialized; however, lvLiveInOutOfHndlr should be set on them
// as appropriate.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the field is dependently promoted (i.e. the whole struct is forced to live on the stack), then do we still need to mark them as lvLiveInOutOfHndlr since they'll always be defined/used to/from the stack?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change we may enregister a field of a dependently promoted struct if the struct is no longer used. See the change in lclvars.cpp: we don't set lvaSetVarDoNotEnregister on such fields.


bool fieldOfDependentlyPromotedStruct = lvaIsFieldOfDependentlyPromotedStruct(varDsc);

/* Un-init locals may need auto-initialization. Note that the
liveness of such locals will bubble to the top (fgFirstBB)
in fgInterBlockLocalVarLiveness() */
// Un-init locals may need auto-initialization. Note that the
// liveness of such locals will bubble to the top (fgFirstBB)
// in fgInterBlockLocalVarLiveness()

if (!varDsc->lvIsParam && VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, varDsc->lvVarIndex) &&
(info.compInitMem || varTypeIsGC(varDsc->TypeGet())))
(info.compInitMem || varTypeIsGC(varDsc->TypeGet())) && !fieldOfDependentlyPromotedStruct)
{
varDsc->lvMustInit = true;
}
Expand All @@ -2472,7 +2473,7 @@ void Compiler::fgInterBlockLocalVarLiveness()
if (isFinallyVar)
{
// Set lvMustInit only if we have a non-arg, GC pointer.
if (!varDsc->lvIsParam && varTypeIsGC(varDsc->TypeGet()))
if (!varDsc->lvIsParam && varTypeIsGC(varDsc->TypeGet()) && !fieldOfDependentlyPromotedStruct)
{
varDsc->lvMustInit = true;
}
Expand Down
8 changes: 6 additions & 2 deletions src/coreclr/src/jit/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9210,8 +9210,8 @@ typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, unsigned> Lc
// the assignment is to a local (and not a field),
// the local is not lvLiveInOutOfHndlr or no exceptions can be thrown between the prolog and the assignment,
// either the local has no gc pointers or there are no gc-safe points between the prolog and the assignment,
// then the local with lvHasExplicitInit which tells the codegen not to insert zero initialization for this
// local in the prolog.
// then the local is marked with lvHasExplicitInit which tells the codegen not to insert zero initialization
// for this local in the prolog.

void Compiler::optRemoveRedundantZeroInits()
{
Expand Down Expand Up @@ -9276,6 +9276,9 @@ void Compiler::optRemoveRedundantZeroInits()
unsigned lclNum = treeOp->gtOp1->AsLclVarCommon()->GetLclNum();
LclVarDsc* const lclDsc = lvaGetDesc(lclNum);
unsigned* pRefCount = refCounts.LookupPointer(lclNum);

// pRefCount can't be null because the local node on the lhs of the assignment
// must have already been seen.
assert(pRefCount != nullptr);
if (*pRefCount == 1)
{
Expand All @@ -9297,6 +9300,7 @@ void Compiler::optRemoveRedundantZeroInits()
removedExplicitZeroInit = true;
*pRefCount = 0;
lclDsc->lvSuppressedZeroInit = 1;
lclDsc->setLvRefCnt(lclDsc->lvRefCnt() - 1);
}
}
}
Expand Down