diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp index a15ffb4be65985..2b48b4dd21309d 100644 --- a/src/coreclr/jit/treelifeupdater.cpp +++ b/src/coreclr/jit/treelifeupdater.cpp @@ -8,13 +8,10 @@ template TreeLifeUpdater::TreeLifeUpdater(Compiler* compiler) : compiler(compiler) - , newLife(VarSetOps::MakeEmpty(compiler)) - , stackVarDeltaSet(VarSetOps::MakeEmpty(compiler)) - , varDeltaSet(VarSetOps::MakeEmpty(compiler)) - , gcTrkStkDeltaSet(VarSetOps::MakeEmpty(compiler)) #ifdef DEBUG - , gcVarPtrSetNew(VarSetOps::MakeEmpty(compiler)) , epoch(compiler->GetCurLVEpoch()) + , oldLife(VarSetOps::MakeEmpty(compiler)) + , oldStackPtrsLife(VarSetOps::MakeEmpty(compiler)) #endif // DEBUG { } @@ -43,24 +40,22 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns unsigned fieldVarNum = parentVarDsc->lvFieldLclStart + multiRegIndex; LclVarDsc* fldVarDsc = compiler->lvaGetDesc(fieldVarNum); assert(fldVarDsc->lvTracked); - unsigned fldVarIndex = fldVarDsc->lvVarIndex; assert((lclNode->gtFlags & GTF_VAR_USEASG) == 0); - VarSetOps::Assign(compiler, newLife, compiler->compCurLife); + StoreCurrentLifeForDump(); bool isBorn = ((lclNode->gtFlags & GTF_VAR_DEF) != 0); bool isDying = !isBorn && lclNode->IsLastUse(multiRegIndex); - // GTF_SPILL will be set if any registers need to be spilled. - GenTreeFlags spillFlags = (lclNode->gtFlags & lclNode->GetRegSpillFlagByIdx(multiRegIndex)); - bool spill = ((spillFlags & GTF_SPILL) != 0); - bool isInMemory = false; if (isBorn || isDying) { + bool previouslyLive = VarSetOps::IsMember(compiler, compiler->compCurLife, fldVarDsc->lvVarIndex); + UpdateLifeBit(compiler->compCurLife, fldVarDsc, isBorn, isDying); + if (ForCodeGen) { - regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex); - bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); + regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex); + bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; + bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); if (isInReg) { if (isBorn) @@ -69,81 +64,30 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns } compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(lclNode)); } - } - // First, update the live set - if (isDying) - { - VarSetOps::RemoveElemD(compiler, newLife, fldVarIndex); - } - else - { - VarSetOps::AddElemD(compiler, newLife, fldVarIndex); - } - } - if (!VarSetOps::Equal(compiler, compiler->compCurLife, newLife)) - { -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tLive vars: "); - dumpConvertedVarSet(compiler, compiler->compCurLife); - printf(" => "); - dumpConvertedVarSet(compiler, newLife); - printf("\n"); - } -#endif // DEBUG - - VarSetOps::Assign(compiler, compiler->compCurLife, newLife); - - if (ForCodeGen) - { - // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the - // gcInfo.gcTrkStkPtrLcls - // includes all TRACKED vars that EVER live on the stack (i.e. are not always in a register). - VarSetOps::Assign(compiler, gcTrkStkDeltaSet, compiler->codeGen->gcInfo.gcTrkStkPtrLcls); - if (isInMemory && VarSetOps::IsMember(compiler, gcTrkStkDeltaSet, fldVarIndex)) + if (isInMemory && + VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, fldVarDsc->lvVarIndex)) { -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tGCvars: "); - dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); - printf(" => "); - } -#endif // DEBUG - - if (isBorn) - { - VarSetOps::AddElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarIndex); - } - else - { - VarSetOps::RemoveElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarIndex); - } - -#ifdef DEBUG - if (compiler->verbose) - { - dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); - printf("\n"); - } -#endif // DEBUG + UpdateLifeBit(compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarDsc, isBorn, isDying); } - // For each of the LclVarDsc that are reporting change, variable or fields - compiler->codeGen->getVariableLiveKeeper()->siStartOrCloseVariableLiveRange(fldVarDsc, fieldVarNum, isBorn, - isDying); + if (previouslyLive != isBorn) + { + compiler->codeGen->getVariableLiveKeeper()->siStartOrCloseVariableLiveRange(fldVarDsc, fieldVarNum, + isBorn, isDying); + } } } - if (ForCodeGen && spill) + bool spill = false; + // GTF_SPILL will be set if any registers need to be spilled. + if (ForCodeGen && ((lclNode->gtFlags & lclNode->GetRegSpillFlagByIdx(multiRegIndex) & GTF_SPILL) != 0)) { - if (VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, fldVarIndex)) + if (VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, fldVarDsc->lvVarIndex)) { - if (!VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarIndex)) + if (!VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarDsc->lvVarIndex)) { - VarSetOps::AddElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarIndex); + VarSetOps::AddElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarDsc->lvVarIndex); #ifdef DEBUG if (compiler->verbose) { @@ -152,9 +96,13 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns #endif // DEBUG } } - return true; + + spill = true; } - return false; + + DumpLifeDelta(); + + return spill; } //------------------------------------------------------------------------ @@ -178,7 +126,6 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree, GenTreeLclVarComm LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum); compiler->compCurLifeTree = tree; - VarSetOps::Assign(compiler, newLife, compiler->compCurLife); // By codegen, a struct may not be TYP_STRUCT, so we have to // check lvPromoted, for the case where the fields are being @@ -188,240 +135,166 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree, GenTreeLclVarComm return; } - // if it's a partial definition then variable "x" must have had a previous, original, site to be born. - bool isBorn; - bool isDying; - // GTF_SPILL will be set on a MultiRegLclVar if any registers need to be spilled. - bool spill = ((lclVarTree->gtFlags & GTF_SPILL) != 0); - bool isMultiRegLocal = lclVarTree->IsMultiRegLclVar(); - if (isMultiRegLocal) - { - // We should never have an indirect reference for a multi-reg. - assert(lclVarTree == tree); - assert((lclVarTree->gtFlags & GTF_VAR_USEASG) == 0); - isBorn = ((lclVarTree->gtFlags & GTF_VAR_DEF) != 0); - // Note that for multireg locals we can have definitions for which some of those are last uses. - // We don't want to add those to the varDeltaSet because otherwise they will be added as newly - // live. - isDying = !isBorn && lclVarTree->HasLastUse(); - } - else - { - isBorn = ((lclVarTree->gtFlags & GTF_VAR_DEF) != 0 && (lclVarTree->gtFlags & GTF_VAR_USEASG) == 0); - isDying = ((lclVarTree->gtFlags & GTF_VAR_DEATH) != 0); - } + StoreCurrentLifeForDump(); - // Since all tracked vars are register candidates, but not all are in registers at all times, - // we maintain two separate sets of variables - the total set of variables that are either - // born or dying here, and the subset of those that are on the stack - VarSetOps::ClearD(compiler, stackVarDeltaSet); + bool isBorn = ((lclVarTree->gtFlags & GTF_VAR_DEF) != 0) && ((lclVarTree->gtFlags & GTF_VAR_USEASG) == 0); - if (isBorn || isDying) + if (varDsc->lvTracked) { - VarSetOps::ClearD(compiler, varDeltaSet); + assert(!varDsc->lvPromoted && !lclVarTree->IsMultiRegLclVar()); + + bool isDying = (lclVarTree->gtFlags & GTF_VAR_DEATH) != 0; - if (varDsc->lvTracked) + if (isBorn || isDying) { - VarSetOps::AddElemD(compiler, varDeltaSet, varDsc->lvVarIndex); + bool previouslyLive = VarSetOps::IsMember(compiler, compiler->compCurLife, varDsc->lvVarIndex); + UpdateLifeBit(compiler->compCurLife, varDsc, isBorn, isDying); + if (ForCodeGen) { if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg(compiler)) { compiler->codeGen->genUpdateVarReg(varDsc, tree); } - bool isInReg = varDsc->lvIsInReg() && tree->GetRegNum() != REG_NA; + + bool isInReg = varDsc->lvIsInReg() && (tree->GetRegNum() != REG_NA); bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); if (isInReg) { compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree)); } - if (isInMemory) + + if (isInMemory && + VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex)) + { + UpdateLifeBit(compiler->codeGen->gcInfo.gcVarPtrSetCur, varDsc, isBorn, isDying); + } + + if ((isDying != isBorn) && (isBorn != previouslyLive)) { - VarSetOps::AddElemD(compiler, stackVarDeltaSet, varDsc->lvVarIndex); + compiler->codeGen->getVariableLiveKeeper()->siStartOrCloseVariableLiveRange(varDsc, lclNum, isBorn, + isDying); } } } - else if (ForCodeGen && lclVarTree->IsMultiRegLclVar()) + + if (ForCodeGen && ((lclVarTree->gtFlags & GTF_SPILL) != 0)) { - assert(varDsc->lvPromoted && compiler->lvaEnregMultiRegVars); - unsigned firstFieldVarNum = varDsc->lvFieldLclStart; - for (unsigned i = 0; i < varDsc->lvFieldCnt; ++i) + compiler->codeGen->genSpillVar(tree); + + if (VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex)) { - bool fieldIsSpilled = spill && ((lclVarTree->GetRegSpillFlagByIdx(i) & GTF_SPILL) != 0); - LclVarDsc* fldVarDsc = compiler->lvaGetDesc(firstFieldVarNum + i); - noway_assert(fldVarDsc->lvIsStructField); - assert(fldVarDsc->lvTracked); - unsigned fldVarIndex = fldVarDsc->lvVarIndex; - regNumber reg = lclVarTree->AsLclVar()->GetRegNumByIdx(i); - bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); - bool isFieldDying = lclVarTree->AsLclVar()->IsLastUse(i); - if ((isBorn && !isFieldDying) || (!isBorn && isFieldDying)) - { - VarSetOps::AddElemD(compiler, varDeltaSet, fldVarIndex); - if (isInMemory) - { - VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex); - } - } - if (isInReg) + if (!VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) { - if (isBorn) - { - compiler->codeGen->genUpdateVarReg(fldVarDsc, tree, i); - } - compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isFieldDying DEBUGARG(tree)); - // If this was marked for spill, genProduceReg should already have spilled it. - assert(!fieldIsSpilled); + VarSetOps::AddElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex); + JITDUMP("\t\t\t\t\t\t\tVar V%02u becoming live\n", lclNum); } } - spill = false; } - else if (varDsc->lvPromoted) + } + else if (varDsc->lvPromoted) + { + bool isMultiRegLocal = lclVarTree->IsMultiRegLclVar(); +#ifdef DEBUG + if (isMultiRegLocal) { - // If hasDeadTrackedFieldVars is true, then, for a LDOBJ(ADDR()), - // *deadTrackedFieldVars indicates which tracked field vars are dying. - bool hasDeadTrackedFieldVars = false; + // We should never have an indirect reference for a multi-reg. + assert(lclVarTree == tree); + assert((lclVarTree->gtFlags & GTF_VAR_USEASG) == 0); + } +#endif + + // TODO: Fields can die even at a def. + bool isAnyFieldDying = + isMultiRegLocal ? !isBorn && lclVarTree->HasLastUse() : ((lclVarTree->gtFlags & GTF_VAR_DEATH) != 0); + if (isBorn || isAnyFieldDying) + { + VARSET_TP* deadTrackedFieldVars = nullptr; + bool hasDeadTrackedFieldVars = false; // TODO-Review: the code below does not look right. We can have last uses for simple LCL_VARs // as well as indirect uses. - if ((tree != lclVarTree) && isDying) + if (!isMultiRegLocal && (tree != lclVarTree) && isAnyFieldDying) { assert(!isBorn); // GTF_VAR_DEATH only set for non-partial last use. - VARSET_TP* deadTrackedFieldVars = nullptr; hasDeadTrackedFieldVars = compiler->LookupPromotedStructDeathVars(lclVarTree, &deadTrackedFieldVars); - if (hasDeadTrackedFieldVars) - { - VarSetOps::Assign(compiler, varDeltaSet, *deadTrackedFieldVars); - } } unsigned firstFieldVarNum = varDsc->lvFieldLclStart; for (unsigned i = 0; i < varDsc->lvFieldCnt; ++i) { - LclVarDsc* fldVarDsc = compiler->lvaGetDesc(firstFieldVarNum + i); - noway_assert(fldVarDsc->lvIsStructField); - if (fldVarDsc->lvTracked) + unsigned fldLclNum = firstFieldVarNum + i; + LclVarDsc* fldVarDsc = compiler->lvaGetDesc(fldLclNum); + assert(fldVarDsc->lvIsStructField); + if (!fldVarDsc->lvTracked) { - unsigned fldVarIndex = fldVarDsc->lvVarIndex; - // We should never see enregistered fields in a struct local unless - // IsMultiRegLclVar() returns true, in which case we've handled this above. - assert(!fldVarDsc->lvIsInReg()); - noway_assert(fldVarIndex < compiler->lvaTrackedCount); - if (!hasDeadTrackedFieldVars) - { - VarSetOps::AddElemD(compiler, varDeltaSet, fldVarIndex); - if (ForCodeGen) - { - // We repeat this call here and below to avoid the VarSetOps::IsMember - // test in this, the common case, where we have no deadTrackedFieldVars. - VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex); - } - } - else if (ForCodeGen && VarSetOps::IsMember(compiler, varDeltaSet, fldVarIndex)) - { - VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex); - } + // multi-reg locals are expected to have all fields tracked so that they are register candidates. + assert(!isMultiRegLocal); + continue; } - } - } - // First, update the live set - if (isDying) - { - // We'd like to be able to assert the following, however if we are walking - // through a qmark/colon tree, we may encounter multiple last-use nodes. - // assert (VarSetOps::IsSubset(compiler, regVarDeltaSet, newLife)); - VarSetOps::DiffD(compiler, newLife, varDeltaSet); - } - else - { - // This shouldn't be in newLife, unless this is debug code, in which - // case we keep vars live everywhere, OR the variable is address-exposed, - // OR this block is part of a try block, in which case it may be live at the handler - // Could add a check that, if it's in newLife, that it's also in - // fgGetHandlerLiveVars(compCurBB), but seems excessive - // - // For a dead store, it can be the case that we set both isBorn and isDying to true. - // (We don't eliminate dead stores under MinOpts, so we can't assume they're always - // eliminated.) If it's both, we handled it above. - VarSetOps::UnionD(compiler, newLife, varDeltaSet); - } - } + // We should never see enregistered fields in a struct local unless + // IsMultiRegLclVar() returns true. + assert(isMultiRegLocal || !fldVarDsc->lvIsInReg()); - if (!VarSetOps::Equal(compiler, compiler->compCurLife, newLife)) - { -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tLive vars: "); - dumpConvertedVarSet(compiler, compiler->compCurLife); - printf(" => "); - dumpConvertedVarSet(compiler, newLife); - printf("\n"); - } -#endif // DEBUG + bool isInReg = fldVarDsc->lvIsInReg() && (lclVarTree->AsLclVar()->GetRegNumByIdx(i) != REG_NA); + bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); + bool previouslyLive = VarSetOps::IsMember(compiler, compiler->compCurLife, fldVarDsc->lvVarIndex); - VarSetOps::Assign(compiler, compiler->compCurLife, newLife); + bool isFieldDying; - if (ForCodeGen) - { - // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the - // gcInfo.gcTrkStkPtrLcls - // includes all TRACKED vars that EVER live on the stack (i.e. are not always in a register). - VarSetOps::Assign(compiler, gcTrkStkDeltaSet, compiler->codeGen->gcInfo.gcTrkStkPtrLcls); - VarSetOps::IntersectionD(compiler, gcTrkStkDeltaSet, stackVarDeltaSet); - if (!VarSetOps::IsEmpty(compiler, gcTrkStkDeltaSet)) - { -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tGCvars: "); - dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); - printf(" => "); - } -#endif // DEBUG - - if (isBorn) + if (isMultiRegLocal) { - VarSetOps::UnionD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet); + isFieldDying = lclVarTree->IsLastUse(i); + // TODO: Remove this condition which disallows marking + // some fields as dead even though they are dying when other + // fields are defined. + if ((isBorn && !isFieldDying) || (!isBorn && isFieldDying)) + { + UpdateLifeBit(compiler->compCurLife, fldVarDsc, isBorn, isFieldDying); + } } else { - VarSetOps::DiffD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet); + isFieldDying = isAnyFieldDying && (!hasDeadTrackedFieldVars || + VarSetOps::IsMember(compiler, *deadTrackedFieldVars, i)); + UpdateLifeBit(compiler->compCurLife, fldVarDsc, isBorn, isFieldDying); } -#ifdef DEBUG - if (compiler->verbose) + if (ForCodeGen) { - dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); - printf("\n"); - } -#endif // DEBUG - } - // For each of the LclVarDsc that are reporting change, variable or fields - compiler->codeGen->getVariableLiveKeeper()->siStartOrCloseVariableLiveRanges(varDeltaSet, isBorn, isDying); - } - } + if (isInReg) + { + if (isBorn) + { + compiler->codeGen->genUpdateVarReg(fldVarDsc, tree, i); + } - if (ForCodeGen && spill) - { - assert(!varDsc->lvPromoted); - compiler->codeGen->genSpillVar(tree); - if (VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex)) - { - if (!VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) - { - VarSetOps::AddElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex); -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", compiler->lvaGetLclNum(varDsc)); + compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isFieldDying DEBUGARG(tree)); + // If this was marked for spill genProduceReg should already have spilled it. + bool fieldNeedsSpill = ((lclVarTree->gtFlags & GTF_SPILL) != 0) && + ((lclVarTree->GetRegSpillFlagByIdx(i) & GTF_SPILL) != 0); + assert(!fieldNeedsSpill); + } + + if (isInMemory && + VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, fldVarDsc->lvVarIndex)) + { + UpdateLifeBit(compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarDsc, isBorn, isFieldDying); + } + + if ((isFieldDying != isBorn) && (isBorn != previouslyLive)) + { + compiler->codeGen->getVariableLiveKeeper()->siStartOrCloseVariableLiveRange(fldVarDsc, + fldLclNum, isBorn, + isFieldDying); + } } -#endif // DEBUG } } } + + DumpLifeDelta(); } //------------------------------------------------------------------------ @@ -458,5 +331,76 @@ void TreeLifeUpdater::UpdateLife(GenTree* tree) } } +//------------------------------------------------------------------------ +// UpdateLifeBit: Update a liveness set for a specific local depending on whether it is being born or dying. +// +// Arguments: +// set - The life set +// dsc - The local's description +// isBorn - Whether the local is being born now +// isDying - Whether the local is dying now +// +template +void TreeLifeUpdater::UpdateLifeBit(VARSET_TP& set, LclVarDsc* dsc, bool isBorn, bool isDying) +{ + if (isDying) + { + VarSetOps::RemoveElemD(compiler, set, dsc->lvVarIndex); + } + else if (isBorn) + { + VarSetOps::AddElemD(compiler, set, dsc->lvVarIndex); + } +} + +//------------------------------------------------------------------------ +// StoreCurrentLifeForDump: Store current liveness information so that deltas +// can be dumped after potential updates. +// +template +void TreeLifeUpdater::StoreCurrentLifeForDump() +{ +#ifdef DEBUG + if (compiler->verbose) + { + VarSetOps::Assign(compiler, oldLife, compiler->compCurLife); + + if (ForCodeGen) + { + VarSetOps::Assign(compiler, oldStackPtrsLife, compiler->codeGen->gcInfo.gcVarPtrSetCur); + } + } +#endif +} + +//------------------------------------------------------------------------ +// DumpLifeDelta: Dump the delta of liveness changes that happened since +// StoreCurrentLifeForDump was called. +// +template +void TreeLifeUpdater::DumpLifeDelta() +{ +#ifdef DEBUG + if (compiler->verbose && !VarSetOps::Equal(compiler, oldLife, compiler->compCurLife)) + { + printf("\t\t\t\t\t\t\tLive vars: "); + dumpConvertedVarSet(compiler, oldLife); + printf(" => "); + dumpConvertedVarSet(compiler, compiler->compCurLife); + printf("\n"); + } + + if (ForCodeGen && compiler->verbose && + !VarSetOps::Equal(compiler, oldStackPtrsLife, compiler->codeGen->gcInfo.gcVarPtrSetCur)) + { + printf("\t\t\t\t\t\t\tGC vars: "); + dumpConvertedVarSet(compiler, oldStackPtrsLife); + printf(" => "); + dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); + printf("\n"); + } +#endif // DEBUG +} + template class TreeLifeUpdater; template class TreeLifeUpdater; diff --git a/src/coreclr/jit/treelifeupdater.h b/src/coreclr/jit/treelifeupdater.h index eed34689d5cc54..0b07fe860cd55a 100644 --- a/src/coreclr/jit/treelifeupdater.h +++ b/src/coreclr/jit/treelifeupdater.h @@ -18,15 +18,15 @@ class TreeLifeUpdater private: void UpdateLifeVar(GenTree* tree, GenTreeLclVarCommon* lclVarTree); + void UpdateLifeBit(VARSET_TP& set, LclVarDsc* dsc, bool isBorn, bool isDying); + void StoreCurrentLifeForDump(); + void DumpLifeDelta(); private: Compiler* compiler; - VARSET_TP newLife; // a live set after processing an argument tree. - VARSET_TP stackVarDeltaSet; // a live set of tracked stack ptr lcls. - VARSET_TP varDeltaSet; // a set of variables that changed their liveness. - VARSET_TP gcTrkStkDeltaSet; // // a set of gc tracked stack variables that changed their liveness.. #ifdef DEBUG - VARSET_TP gcVarPtrSetNew; // a set to print changes to live part of tracked stack ptr lcls (gcVarPtrSetCur). - unsigned epoch; // VarSets epoch when the class was created, must stay the same during its using. -#endif // DEBUG + unsigned epoch; // VarSets epoch when the class was created, must stay the same during its using. + VARSET_TP oldLife; + VARSET_TP oldStackPtrsLife; +#endif // DEBUG };