diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index d5bf9bf745541d..8e86ee947e91f9 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -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 diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 608b4410cd9421..6891192bf34689 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -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. // diff --git a/src/coreclr/src/jit/liveness.cpp b/src/coreclr/src/jit/liveness.cpp index 3368e6567043d2..96d623c221d5dc 100644 --- a/src/coreclr/src/jit/liveness.cpp +++ b/src/coreclr/src/jit/liveness.cpp @@ -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. + + 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; } @@ -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; } diff --git a/src/coreclr/src/jit/optimizer.cpp b/src/coreclr/src/jit/optimizer.cpp index 2fe04e7a8b0606..9f4f0c22e758fe 100644 --- a/src/coreclr/src/jit/optimizer.cpp +++ b/src/coreclr/src/jit/optimizer.cpp @@ -9210,8 +9210,8 @@ typedef JitHashTable, 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() { @@ -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) { @@ -9297,6 +9300,7 @@ void Compiler::optRemoveRedundantZeroInits() removedExplicitZeroInit = true; *pRefCount = 0; lclDsc->lvSuppressedZeroInit = 1; + lclDsc->setLvRefCnt(lclDsc->lvRefCnt() - 1); } } }