Do not allocate memory in compUpdateTreeLife. (#17055)
authorSergey Andreenko <seandree@microsoft.com>
Tue, 15 May 2018 22:57:08 +0000 (15:57 -0700)
committerGitHub <noreply@github.com>
Tue, 15 May 2018 22:57:08 +0000 (15:57 -0700)
* move compUpdateLifeVar and compUpdateTreeLife to separate files for legacy and non-legacy case

* create TreeLifeUpdater class

src/jit/CMakeLists.txt
src/jit/codegencommon.cpp
src/jit/codegeninterface.h
src/jit/codegenlegacy.cpp
src/jit/compiler.h
src/jit/compiler.hpp
src/jit/copyprop.cpp
src/jit/treelifeupdater.cpp [new file with mode: 0644]
src/jit/treelifeupdater.h [new file with mode: 0644]

index ed25156..7fd58a8 100644 (file)
@@ -76,6 +76,7 @@ set( JIT_SOURCES
   utils.cpp
   valuenum.cpp
   stacklevelsetter.cpp
+  treelifeupdater.cpp
 )
 
 # Add header files to Visual Studio vcxproj, not required for unixes
@@ -191,6 +192,7 @@ if (WIN32)
     varset.h
     vartype.h
     x86_instrs.h
+    treelifeupdater.h
   )
 endif(WIN32)
 
index f17d2a8..bab4f07 100644 (file)
@@ -87,7 +87,7 @@ CodeGenInterface* getCodeGenerator(Compiler* comp)
 
 // CodeGen constructor
 CodeGenInterface::CodeGenInterface(Compiler* theCompiler)
-    : gcInfo(theCompiler), regSet(theCompiler, gcInfo), compiler(theCompiler)
+    : gcInfo(theCompiler), regSet(theCompiler, gcInfo), compiler(theCompiler), treeLifeUpdater(nullptr)
 {
 }
 
@@ -343,8 +343,7 @@ bool CodeGen::genShouldRoundFP()
 
 void CodeGen::genPrepForCompiler()
 {
-    unsigned   varNum;
-    LclVarDsc* varDsc;
+    treeLifeUpdater = new (compiler, CMK_bitset) TreeLifeUpdater<true>(compiler);
 
     /* Figure out which non-register variables hold pointers */
 
@@ -357,6 +356,8 @@ void CodeGen::genPrepForCompiler()
 
     VarSetOps::AssignNoCopy(compiler, compiler->raRegVarsMask, VarSetOps::MakeEmpty(compiler));
 
+    unsigned   varNum;
+    LclVarDsc* varDsc;
     for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
     {
         if (varDsc->lvTracked
@@ -468,7 +469,7 @@ void CodeGen::genPrepForEHCodegen()
 
 void CodeGenInterface::genUpdateLife(GenTree* tree)
 {
-    compiler->compUpdateLife</*ForCodeGen*/ true>(tree);
+    treeLifeUpdater->UpdateLife(tree);
 }
 
 void CodeGenInterface::genUpdateLife(VARSET_VALARG_TP newLife)
@@ -773,330 +774,6 @@ regMaskTP Compiler::compNoGCHelperCallKillSet(CorInfoHelpFunc helper)
     }
 }
 
-// Update liveness (always var liveness, i.e., compCurLife, and also, if "ForCodeGen" is true, reg liveness, i.e.,
-// regSet.rsMaskVars as well)
-// if the given lclVar (or indir(addr(local)))/regVar node is going live (being born) or dying.
-template <bool ForCodeGen>
-void Compiler::compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars)
-{
-    GenTree* indirAddrLocal = fgIsIndirOfAddrOfLocal(tree);
-    assert(tree->OperIsNonPhiLocal() || indirAddrLocal != nullptr);
-
-    // Get the local var tree -- if "tree" is "Ldobj(addr(x))", or "ind(addr(x))" this is "x", else it's "tree".
-    GenTree* lclVarTree = indirAddrLocal;
-    if (lclVarTree == nullptr)
-    {
-        lclVarTree = tree;
-    }
-    unsigned int lclNum = lclVarTree->gtLclVarCommon.gtLclNum;
-    LclVarDsc*   varDsc = lvaTable + lclNum;
-
-#ifdef DEBUG
-#if !defined(_TARGET_AMD64_)
-    // There are no addr nodes on ARM and we are experimenting with encountering vars in 'random' order.
-    // Struct fields are not traversed in a consistent order, so ignore them when
-    // verifying that we see the var nodes in execution order
-    if (ForCodeGen)
-    {
-        if (tree->OperIsIndir())
-        {
-            assert(indirAddrLocal != NULL);
-        }
-        else if (tree->gtNext != NULL && tree->gtNext->gtOper == GT_ADDR &&
-                 ((tree->gtNext->gtNext == NULL || !tree->gtNext->gtNext->OperIsIndir())))
-        {
-            assert(tree->IsLocal()); // Can only take the address of a local.
-            // The ADDR might occur in a context where the address it contributes is eventually
-            // dereferenced, so we can't say that this is not a use or def.
-        }
-#if 0   
-        // TODO-ARM64-Bug?: These asserts don't seem right for ARM64: I don't understand why we have to assert 
-        // two consecutive lclvars (in execution order) can only be observed if the first one is a struct field.
-        // It seems to me this is code only applicable to the legacy JIT and not RyuJIT (and therefore why it was 
-        // ifdef'ed out for AMD64).
-        else if (!varDsc->lvIsStructField)
-        {
-            GenTree* prevTree;
-            for (prevTree = tree->gtPrev;
-                 prevTree != NULL && prevTree != compCurLifeTree;
-                 prevTree = prevTree->gtPrev)
-            {
-                if ((prevTree->gtOper == GT_LCL_VAR) || (prevTree->gtOper == GT_REG_VAR))
-                {
-                    LclVarDsc * prevVarDsc = lvaTable + prevTree->gtLclVarCommon.gtLclNum;
-
-                    // These are the only things for which this method MUST be called
-                    assert(prevVarDsc->lvIsStructField);
-                }
-            }
-            assert(prevTree == compCurLifeTree);
-        }
-#endif // 0
-    }
-#endif // !_TARGET_AMD64_
-#endif // DEBUG
-
-    compCurLifeTree = tree;
-    VARSET_TP newLife(VarSetOps::MakeCopy(this, compCurLife));
-
-    // By codegen, a struct may not be TYP_STRUCT, so we have to
-    // check lvPromoted, for the case where the fields are being
-    // tracked.
-    if (!varDsc->lvTracked && !varDsc->lvPromoted)
-    {
-        return;
-    }
-
-    bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0); // if it's "x <op>=
-                                                                                                 // ..." then variable
-                                                                                                 // "x" must have had a
-                                                                                                 // previous, original,
-                                                                                                 // site to be born.
-    bool isDying = ((tree->gtFlags & GTF_VAR_DEATH) != 0);
-#ifndef LEGACY_BACKEND
-    bool spill = ((tree->gtFlags & GTF_SPILL) != 0);
-#endif // !LEGACY_BACKEND
-
-#ifndef LEGACY_BACKEND
-    // For RyuJIT backend, 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
-    VARSET_TP stackVarDeltaSet(VarSetOps::MakeEmpty(this));
-#endif // !LEGACY_BACKEND
-
-    if (isBorn || isDying)
-    {
-        bool hasDeadTrackedFieldVars = false; // If this is true, then, for a LDOBJ(ADDR(<promoted struct local>)),
-        VARSET_TP* deadTrackedFieldVars =
-            nullptr; // *deadTrackedFieldVars indicates which tracked field vars are dying.
-        VARSET_TP varDeltaSet(VarSetOps::MakeEmpty(this));
-
-        if (varDsc->lvTracked)
-        {
-            VarSetOps::AddElemD(this, varDeltaSet, varDsc->lvVarIndex);
-            if (ForCodeGen)
-            {
-#ifndef LEGACY_BACKEND
-                if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg())
-                {
-                    codeGen->genUpdateVarReg(varDsc, tree);
-                }
-#endif // !LEGACY_BACKEND
-                if (varDsc->lvIsInReg()
-#ifndef LEGACY_BACKEND
-                    && tree->gtRegNum != REG_NA
-#endif // !LEGACY_BACKEND
-                    )
-                {
-                    codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree));
-                }
-#ifndef LEGACY_BACKEND
-                else
-                {
-                    VarSetOps::AddElemD(this, stackVarDeltaSet, varDsc->lvVarIndex);
-                }
-#endif // !LEGACY_BACKEND
-            }
-        }
-        else if (varDsc->lvPromoted)
-        {
-            if (indirAddrLocal != nullptr && isDying)
-            {
-                assert(!isBorn); // GTF_VAR_DEATH only set for LDOBJ last use.
-                hasDeadTrackedFieldVars = GetPromotedStructDeathVars()->Lookup(indirAddrLocal, &deadTrackedFieldVars);
-                if (hasDeadTrackedFieldVars)
-                {
-                    VarSetOps::Assign(this, varDeltaSet, *deadTrackedFieldVars);
-                }
-            }
-
-            for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
-            {
-                LclVarDsc* fldVarDsc = &(lvaTable[i]);
-                noway_assert(fldVarDsc->lvIsStructField);
-                if (fldVarDsc->lvTracked)
-                {
-                    unsigned fldVarIndex = fldVarDsc->lvVarIndex;
-                    noway_assert(fldVarIndex < lvaTrackedCount);
-                    if (!hasDeadTrackedFieldVars)
-                    {
-                        VarSetOps::AddElemD(this, 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.
-                            if (fldVarDsc->lvIsInReg())
-                            {
-#ifndef LEGACY_BACKEND
-                                if (isBorn)
-                                {
-                                    codeGen->genUpdateVarReg(fldVarDsc, tree);
-                                }
-#endif // !LEGACY_BACKEND
-                                codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
-                            }
-#ifndef LEGACY_BACKEND
-                            else
-                            {
-                                VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex);
-                            }
-#endif // !LEGACY_BACKEND
-                        }
-                    }
-                    else if (ForCodeGen && VarSetOps::IsMember(this, varDeltaSet, fldVarIndex))
-                    {
-                        if (lvaTable[i].lvIsInReg())
-                        {
-#ifndef LEGACY_BACKEND
-                            if (isBorn)
-                            {
-                                codeGen->genUpdateVarReg(fldVarDsc, tree);
-                            }
-#endif // !LEGACY_BACKEND
-                            codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
-                        }
-#ifndef LEGACY_BACKEND
-                        else
-                        {
-                            VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex);
-                        }
-#endif // !LEGACY_BACKEND
-                    }
-                }
-            }
-        }
-
-        // 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(this, newLife, varDeltaSet);
-            if (pLastUseVars != nullptr)
-            {
-                VarSetOps::Assign(this, *pLastUseVars, 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(this, newLife, varDeltaSet);
-        }
-    }
-
-    if (!VarSetOps::Equal(this, compCurLife, newLife))
-    {
-#ifdef DEBUG
-        if (verbose)
-        {
-            printf("\t\t\t\t\t\t\tLive vars: ");
-            dumpConvertedVarSet(this, compCurLife);
-            printf(" => ");
-            dumpConvertedVarSet(this, newLife);
-            printf("\n");
-        }
-#endif // DEBUG
-
-        VarSetOps::Assign(this, compCurLife, newLife);
-
-        if (ForCodeGen)
-        {
-#ifndef LEGACY_BACKEND
-
-            // 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).
-            VARSET_TP gcTrkStkDeltaSet(
-                VarSetOps::Intersection(this, codeGen->gcInfo.gcTrkStkPtrLcls, stackVarDeltaSet));
-            if (!VarSetOps::IsEmpty(this, gcTrkStkDeltaSet))
-            {
-#ifdef DEBUG
-                if (verbose)
-                {
-                    printf("\t\t\t\t\t\t\tGCvars: ");
-                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
-                    printf(" => ");
-                }
-#endif // DEBUG
-
-                if (isBorn)
-                {
-                    VarSetOps::UnionD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
-                }
-                else
-                {
-                    VarSetOps::DiffD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
-                }
-
-#ifdef DEBUG
-                if (verbose)
-                {
-                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
-                    printf("\n");
-                }
-#endif // DEBUG
-            }
-
-#else // LEGACY_BACKEND
-
-#ifdef DEBUG
-            if (verbose)
-            {
-                VARSET_TP gcVarPtrSetNew(VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls));
-                if (!VarSetOps::Equal(this, codeGen->gcInfo.gcVarPtrSetCur, gcVarPtrSetNew))
-                {
-                    printf("\t\t\t\t\t\t\tGCvars: ");
-                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
-                    printf(" => ");
-                    dumpConvertedVarSet(this, gcVarPtrSetNew);
-                    printf("\n");
-                }
-            }
-#endif // DEBUG
-
-            VarSetOps::AssignNoCopy(this, codeGen->gcInfo.gcVarPtrSetCur,
-                                    VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls));
-
-#endif // LEGACY_BACKEND
-
-            codeGen->siUpdate();
-        }
-    }
-
-#ifndef LEGACY_BACKEND
-    if (ForCodeGen && spill)
-    {
-        assert(!varDsc->lvPromoted);
-        codeGen->genSpillVar(tree);
-        if (VarSetOps::IsMember(this, codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex))
-        {
-            if (!VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex))
-            {
-                VarSetOps::AddElemD(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex);
-#ifdef DEBUG
-                if (verbose)
-                {
-                    printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", varDsc - lvaTable);
-                }
-#endif // DEBUG
-            }
-        }
-    }
-#endif // !LEGACY_BACKEND
-}
-
-// Need an explicit instantiation.
-template void Compiler::compUpdateLifeVar<false>(GenTree* tree, VARSET_TP* pLastUseVars);
-
 template <bool ForCodeGen>
 void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
 {
index 604e3ae..371bb1e 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "regset.h"
 #include "jitgcinfo.h"
+#include "treelifeupdater.h"
 
 // Forward reference types
 
@@ -151,6 +152,8 @@ protected:
     regMaskTP genLiveMask(VARSET_VALARG_TP liveSet);
 #endif
 
+    TreeLifeUpdater<true>* treeLifeUpdater;
+
 public:
     bool genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf);
     bool genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal);
index a2aeb5e..ec6ed7f 100644 (file)
@@ -21925,4 +21925,354 @@ bool CodeGen::genContainsVarDeath(GenTree* from, GenTree* to, unsigned varNum)
     return false;
 }
 
+// Update liveness (always var liveness, i.e., compCurLife, and also, if "ForCodeGen" is true, reg liveness, i.e.,
+// regSet.rsMaskVars as well)
+// if the given lclVar (or indir(addr(local)))/regVar node is going live (being born) or dying.
+template <bool ForCodeGen>
+void Compiler::compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars)
+{
+    GenTree* indirAddrLocal = fgIsIndirOfAddrOfLocal(tree);
+    assert(tree->OperIsNonPhiLocal() || indirAddrLocal != nullptr);
+
+    // Get the local var tree -- if "tree" is "Ldobj(addr(x))", or "ind(addr(x))" this is "x", else it's "tree".
+    GenTree* lclVarTree = indirAddrLocal;
+    if (lclVarTree == nullptr)
+    {
+        lclVarTree = tree;
+    }
+    unsigned int lclNum = lclVarTree->gtLclVarCommon.gtLclNum;
+    LclVarDsc*   varDsc = lvaTable + lclNum;
+
+#ifdef DEBUG
+#if !defined(_TARGET_AMD64_)
+    // There are no addr nodes on ARM and we are experimenting with encountering vars in 'random' order.
+    // Struct fields are not traversed in a consistent order, so ignore them when
+    // verifying that we see the var nodes in execution order
+    if (ForCodeGen)
+    {
+        if (tree->OperIsIndir())
+        {
+            assert(indirAddrLocal != NULL);
+        }
+        else if (tree->gtNext != NULL && tree->gtNext->gtOper == GT_ADDR &&
+                 ((tree->gtNext->gtNext == NULL || !tree->gtNext->gtNext->OperIsIndir())))
+        {
+            assert(tree->IsLocal()); // Can only take the address of a local.
+            // The ADDR might occur in a context where the address it contributes is eventually
+            // dereferenced, so we can't say that this is not a use or def.
+        }
+#if 0   
+        // TODO-ARM64-Bug?: These asserts don't seem right for ARM64: I don't understand why we have to assert 
+        // two consecutive lclvars (in execution order) can only be observed if the first one is a struct field.
+        // It seems to me this is code only applicable to the legacy JIT and not RyuJIT (and therefore why it was 
+        // ifdef'ed out for AMD64).
+        else if (!varDsc->lvIsStructField)
+        {
+            GenTree* prevTree;
+            for (prevTree = tree->gtPrev;
+                 prevTree != NULL && prevTree != compCurLifeTree;
+                 prevTree = prevTree->gtPrev)
+            {
+                if ((prevTree->gtOper == GT_LCL_VAR) || (prevTree->gtOper == GT_REG_VAR))
+                {
+                    LclVarDsc * prevVarDsc = lvaTable + prevTree->gtLclVarCommon.gtLclNum;
+
+                    // These are the only things for which this method MUST be called
+                    assert(prevVarDsc->lvIsStructField);
+                }
+            }
+            assert(prevTree == compCurLifeTree);
+        }
+#endif // 0
+    }
+#endif // !_TARGET_AMD64_
+#endif // DEBUG
+
+    compCurLifeTree = tree;
+    VARSET_TP newLife(VarSetOps::MakeCopy(this, compCurLife));
+
+    // By codegen, a struct may not be TYP_STRUCT, so we have to
+    // check lvPromoted, for the case where the fields are being
+    // tracked.
+    if (!varDsc->lvTracked && !varDsc->lvPromoted)
+    {
+        return;
+    }
+
+    bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0); // if it's "x <op>=
+                                                                                                 // ..." then variable
+                                                                                                 // "x" must have had a
+                                                                                                 // previous, original,
+                                                                                                 // site to be born.
+    bool isDying = ((tree->gtFlags & GTF_VAR_DEATH) != 0);
+#ifndef LEGACY_BACKEND
+    bool spill = ((tree->gtFlags & GTF_SPILL) != 0);
+#endif // !LEGACY_BACKEND
+
+#ifndef LEGACY_BACKEND
+    // For RyuJIT backend, 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
+    VARSET_TP stackVarDeltaSet(VarSetOps::MakeEmpty(this));
+#endif // !LEGACY_BACKEND
+
+    if (isBorn || isDying)
+    {
+        bool hasDeadTrackedFieldVars = false; // If this is true, then, for a LDOBJ(ADDR(<promoted struct local>)),
+        VARSET_TP* deadTrackedFieldVars =
+            nullptr; // *deadTrackedFieldVars indicates which tracked field vars are dying.
+        VARSET_TP varDeltaSet(VarSetOps::MakeEmpty(this));
+
+        if (varDsc->lvTracked)
+        {
+            VarSetOps::AddElemD(this, varDeltaSet, varDsc->lvVarIndex);
+            if (ForCodeGen)
+            {
+#ifndef LEGACY_BACKEND
+                if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg())
+                {
+                    codeGen->genUpdateVarReg(varDsc, tree);
+                }
+#endif // !LEGACY_BACKEND
+                if (varDsc->lvIsInReg()
+#ifndef LEGACY_BACKEND
+                    && tree->gtRegNum != REG_NA
+#endif // !LEGACY_BACKEND
+                    )
+                {
+                    codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree));
+                }
+#ifndef LEGACY_BACKEND
+                else
+                {
+                    VarSetOps::AddElemD(this, stackVarDeltaSet, varDsc->lvVarIndex);
+                }
+#endif // !LEGACY_BACKEND
+            }
+        }
+        else if (varDsc->lvPromoted)
+        {
+            if (indirAddrLocal != nullptr && isDying)
+            {
+                assert(!isBorn); // GTF_VAR_DEATH only set for LDOBJ last use.
+                hasDeadTrackedFieldVars = GetPromotedStructDeathVars()->Lookup(indirAddrLocal, &deadTrackedFieldVars);
+                if (hasDeadTrackedFieldVars)
+                {
+                    VarSetOps::Assign(this, varDeltaSet, *deadTrackedFieldVars);
+                }
+            }
+
+            for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
+            {
+                LclVarDsc* fldVarDsc = &(lvaTable[i]);
+                noway_assert(fldVarDsc->lvIsStructField);
+                if (fldVarDsc->lvTracked)
+                {
+                    unsigned fldVarIndex = fldVarDsc->lvVarIndex;
+                    noway_assert(fldVarIndex < lvaTrackedCount);
+                    if (!hasDeadTrackedFieldVars)
+                    {
+                        VarSetOps::AddElemD(this, 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.
+                            if (fldVarDsc->lvIsInReg())
+                            {
+#ifndef LEGACY_BACKEND
+                                if (isBorn)
+                                {
+                                    codeGen->genUpdateVarReg(fldVarDsc, tree);
+                                }
+#endif // !LEGACY_BACKEND
+                                codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
+                            }
+#ifndef LEGACY_BACKEND
+                            else
+                            {
+                                VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex);
+                            }
+#endif // !LEGACY_BACKEND
+                        }
+                    }
+                    else if (ForCodeGen && VarSetOps::IsMember(this, varDeltaSet, fldVarIndex))
+                    {
+                        if (lvaTable[i].lvIsInReg())
+                        {
+#ifndef LEGACY_BACKEND
+                            if (isBorn)
+                            {
+                                codeGen->genUpdateVarReg(fldVarDsc, tree);
+                            }
+#endif // !LEGACY_BACKEND
+                            codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
+                        }
+#ifndef LEGACY_BACKEND
+                        else
+                        {
+                            VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex);
+                        }
+#endif // !LEGACY_BACKEND
+                    }
+                }
+            }
+        }
+
+        // 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(this, newLife, varDeltaSet);
+            if (pLastUseVars != nullptr)
+            {
+                VarSetOps::Assign(this, *pLastUseVars, 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(this, newLife, varDeltaSet);
+        }
+    }
+
+    if (!VarSetOps::Equal(this, compCurLife, newLife))
+    {
+#ifdef DEBUG
+        if (verbose)
+        {
+            printf("\t\t\t\t\t\t\tLive vars: ");
+            dumpConvertedVarSet(this, compCurLife);
+            printf(" => ");
+            dumpConvertedVarSet(this, newLife);
+            printf("\n");
+        }
+#endif // DEBUG
+
+        VarSetOps::Assign(this, compCurLife, newLife);
+
+        if (ForCodeGen)
+        {
+#ifndef LEGACY_BACKEND
+
+            // 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).
+            VARSET_TP gcTrkStkDeltaSet(
+                VarSetOps::Intersection(this, codeGen->gcInfo.gcTrkStkPtrLcls, stackVarDeltaSet));
+            if (!VarSetOps::IsEmpty(this, gcTrkStkDeltaSet))
+            {
+#ifdef DEBUG
+                if (verbose)
+                {
+                    printf("\t\t\t\t\t\t\tGCvars: ");
+                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
+                    printf(" => ");
+                }
+#endif // DEBUG
+
+                if (isBorn)
+                {
+                    VarSetOps::UnionD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
+                }
+                else
+                {
+                    VarSetOps::DiffD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
+                }
+
+#ifdef DEBUG
+                if (verbose)
+                {
+                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
+                    printf("\n");
+                }
+#endif // DEBUG
+            }
+
+#else // LEGACY_BACKEND
+
+#ifdef DEBUG
+            if (verbose)
+            {
+                VARSET_TP gcVarPtrSetNew(VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls));
+                if (!VarSetOps::Equal(this, codeGen->gcInfo.gcVarPtrSetCur, gcVarPtrSetNew))
+                {
+                    printf("\t\t\t\t\t\t\tGCvars: ");
+                    dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur);
+                    printf(" => ");
+                    dumpConvertedVarSet(this, gcVarPtrSetNew);
+                    printf("\n");
+                }
+            }
+#endif // DEBUG
+
+            VarSetOps::AssignNoCopy(this, codeGen->gcInfo.gcVarPtrSetCur,
+                                    VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls));
+
+#endif // LEGACY_BACKEND
+
+            codeGen->siUpdate();
+        }
+    }
+
+#ifndef LEGACY_BACKEND
+    if (ForCodeGen && spill)
+    {
+        assert(!varDsc->lvPromoted);
+        codeGen->genSpillVar(tree);
+        if (VarSetOps::IsMember(this, codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex))
+        {
+            if (!VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex))
+            {
+                VarSetOps::AddElemD(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex);
+#ifdef DEBUG
+                if (verbose)
+                {
+                    printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", varDsc - lvaTable);
+                }
+#endif // DEBUG
+            }
+        }
+    }
+#endif // !LEGACY_BACKEND
+}
+
+// Need an explicit instantiation.
+template void Compiler::compUpdateLifeVar<true>(GenTree* tree, VARSET_TP* pLastUseVars);
+template void Compiler::compUpdateLifeVar<false>(GenTree* tree, VARSET_TP* pLastUseVars);
+
+/*****************************************************************************
+ *
+ *  Update the current set of live variables based on the life set recorded
+ *  in the given expression tree node.
+ */
+template <bool ForCodeGen>
+inline void Compiler::compUpdateLife(GenTree* tree)
+{
+    // TODO-Cleanup: We shouldn't really be calling this more than once
+    if (tree == compCurLifeTree)
+    {
+        return;
+    }
+
+    if (!tree->OperIsNonPhiLocal() && fgIsIndirOfAddrOfLocal(tree) == nullptr)
+    {
+        return;
+    }
+
+    compUpdateLifeVar<ForCodeGen>(tree);
+}
+
+template void Compiler::compUpdateLife<false>(GenTree* tree);
+template void Compiler::compUpdateLife<true>(GenTree* tree);
+
 #endif // LEGACY_BACKEND
index 64f54c0..5898a86 100644 (file)
@@ -7263,6 +7263,8 @@ public:
         compChangeLife</*ForCodeGen*/ true>(newLife);
     }
 
+#ifdef LEGACY_BACKEND
+
     template <bool ForCodeGen>
     void compUpdateLife(GenTree* tree);
 
@@ -7272,6 +7274,8 @@ public:
     template <bool ForCodeGen>
     void compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars = nullptr);
 
+#endif // LEGACY_BACKEND
+
     template <bool ForCodeGen>
     inline void compUpdateLife(VARSET_VALARG_TP newLife);
 
index d77173c..4380ea3 100644 (file)
@@ -3575,29 +3575,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 */
 
-/*****************************************************************************
- *
- *  Update the current set of live variables based on the life set recorded
- *  in the given expression tree node.
- */
-
-template <bool ForCodeGen>
-inline void Compiler::compUpdateLife(GenTree* tree)
-{
-    // TODO-Cleanup: We shouldn't really be calling this more than once
-    if (tree == compCurLifeTree)
-    {
-        return;
-    }
-
-    if (!tree->OperIsNonPhiLocal() && fgIsIndirOfAddrOfLocal(tree) == nullptr)
-    {
-        return;
-    }
-
-    compUpdateLifeVar<ForCodeGen>(tree);
-}
-
 template <bool ForCodeGen>
 inline void Compiler::compUpdateLife(VARSET_VALARG_TP newLife)
 {
index aaa0c24..6c21d45 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "jitpch.h"
 #include "ssabuilder.h"
+#include "treelifeupdater.h"
 
 template <typename T>
 inline static T* allocate_any(jitstd::allocator<void>& alloc, size_t count = 1)
@@ -317,6 +318,8 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
     }
 #endif
 
+    TreeLifeUpdater<false> treeLifeUpdater(this);
+
     // There are no definitions at the start of the block. So clear it.
     compCurLifeTree = nullptr;
     VarSetOps::Assign(this, compCurLife, block->bbLiveIn);
@@ -327,7 +330,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
         // Walk the tree to find if any local variable can be replaced with current live definitions.
         for (GenTree* tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
         {
-            compUpdateLife</*ForCodeGen*/ false>(tree);
+            treeLifeUpdater.UpdateLife(tree);
 
             optCopyProp(block, stmt, tree, curSsaName);
 
diff --git a/src/jit/treelifeupdater.cpp b/src/jit/treelifeupdater.cpp
new file mode 100644 (file)
index 0000000..6cefb8a
--- /dev/null
@@ -0,0 +1,366 @@
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+
+#include "treelifeupdater.h"
+
+template <bool ForCodeGen>
+TreeLifeUpdater<ForCodeGen>::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())
+#endif // DEBUG
+{
+}
+
+//------------------------------------------------------------------------
+// UpdateLifeVar: Update live sets for a given tree.
+//
+// Arguments:
+//    tree - the tree which affects liveness.
+//
+template <bool ForCodeGen>
+void TreeLifeUpdater<ForCodeGen>::UpdateLifeVar(GenTree* tree)
+{
+    GenTree* indirAddrLocal = compiler->fgIsIndirOfAddrOfLocal(tree);
+    assert(tree->OperIsNonPhiLocal() || indirAddrLocal != nullptr);
+
+    // Get the local var tree -- if "tree" is "Ldobj(addr(x))", or "ind(addr(x))" this is "x", else it's "tree".
+    GenTree* lclVarTree = indirAddrLocal;
+    if (lclVarTree == nullptr)
+    {
+        lclVarTree = tree;
+    }
+    unsigned int lclNum = lclVarTree->gtLclVarCommon.gtLclNum;
+    LclVarDsc*   varDsc = compiler->lvaTable + lclNum;
+
+#ifdef DEBUG
+#if !defined(_TARGET_AMD64_)
+    // There are no addr nodes on ARM and we are experimenting with encountering vars in 'random' order.
+    // Struct fields are not traversed in a consistent order, so ignore them when
+    // verifying that we see the var nodes in execution order
+    if (ForCodeGen)
+    {
+        if (tree->OperIsIndir())
+        {
+            assert(indirAddrLocal != NULL);
+        }
+        else if (tree->gtNext != NULL && tree->gtNext->gtOper == GT_ADDR &&
+                 ((tree->gtNext->gtNext == NULL || !tree->gtNext->gtNext->OperIsIndir())))
+        {
+            assert(tree->IsLocal()); // Can only take the address of a local.
+            // The ADDR might occur in a context where the address it contributes is eventually
+            // dereferenced, so we can't say that this is not a use or def.
+        }
+#if 0   
+        // TODO-ARM64-Bug?: These asserts don't seem right for ARM64: I don't understand why we have to assert 
+        // two consecutive lclvars (in execution order) can only be observed if the first one is a struct field.
+        // It seems to me this is code only applicable to the legacy JIT and not RyuJIT (and therefore why it was 
+        // ifdef'ed out for AMD64).
+        else if (!varDsc->lvIsStructField)
+        {
+            GenTree* prevTree;
+            for (prevTree = tree->gtPrev;
+                 prevTree != NULL && prevTree != compCurLifeTree;
+                 prevTree = prevTree->gtPrev)
+            {
+                if ((prevTree->gtOper == GT_LCL_VAR) || (prevTree->gtOper == GT_REG_VAR))
+                {
+                    LclVarDsc * prevVarDsc = compiler->lvaTable + prevTree->gtLclVarCommon.gtLclNum;
+
+                    // These are the only things for which this method MUST be called
+                    assert(prevVarDsc->lvIsStructField);
+                }
+            }
+            assert(prevTree == compCurLifeTree);
+        }
+#endif // 0
+    }
+#endif // !_TARGET_AMD64_
+#endif // DEBUG
+
+    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
+    // tracked.
+    if (!varDsc->lvTracked && !varDsc->lvPromoted)
+    {
+        return;
+    }
+
+    // if it's "x <op>=..." then variable "x" must have had a previous, original, site to be born.
+    bool isBorn  = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0);
+    bool isDying = ((tree->gtFlags & GTF_VAR_DEATH) != 0);
+#ifndef LEGACY_BACKEND
+    bool spill = ((tree->gtFlags & GTF_SPILL) != 0);
+#endif // !LEGACY_BACKEND
+
+#ifndef LEGACY_BACKEND
+    // For RyuJIT backend, 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);
+#endif // !LEGACY_BACKEND
+
+    if (isBorn || isDying)
+    {
+        bool hasDeadTrackedFieldVars = false; // If this is true, then, for a LDOBJ(ADDR(<promoted struct local>)),
+        VARSET_TP* deadTrackedFieldVars =
+            nullptr; // *deadTrackedFieldVars indicates which tracked field vars are dying.
+        VarSetOps::ClearD(compiler, varDeltaSet);
+
+        if (varDsc->lvTracked)
+        {
+            VarSetOps::AddElemD(compiler, varDeltaSet, varDsc->lvVarIndex);
+            if (ForCodeGen)
+            {
+#ifndef LEGACY_BACKEND
+                if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg())
+                {
+                    compiler->codeGen->genUpdateVarReg(varDsc, tree);
+                }
+#endif // !LEGACY_BACKEND
+                if (varDsc->lvIsInReg()
+#ifndef LEGACY_BACKEND
+                    && tree->gtRegNum != REG_NA
+#endif // !LEGACY_BACKEND
+                    )
+                {
+                    compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree));
+                }
+#ifndef LEGACY_BACKEND
+                else
+                {
+                    VarSetOps::AddElemD(compiler, stackVarDeltaSet, varDsc->lvVarIndex);
+                }
+#endif // !LEGACY_BACKEND
+            }
+        }
+        else if (varDsc->lvPromoted)
+        {
+            if (indirAddrLocal != nullptr && isDying)
+            {
+                assert(!isBorn); // GTF_VAR_DEATH only set for LDOBJ last use.
+                hasDeadTrackedFieldVars =
+                    compiler->GetPromotedStructDeathVars()->Lookup(indirAddrLocal, &deadTrackedFieldVars);
+                if (hasDeadTrackedFieldVars)
+                {
+                    VarSetOps::Assign(compiler, varDeltaSet, *deadTrackedFieldVars);
+                }
+            }
+
+            for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
+            {
+                LclVarDsc* fldVarDsc = &(compiler->lvaTable[i]);
+                noway_assert(fldVarDsc->lvIsStructField);
+                if (fldVarDsc->lvTracked)
+                {
+                    unsigned fldVarIndex = fldVarDsc->lvVarIndex;
+                    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.
+                            if (fldVarDsc->lvIsInReg())
+                            {
+#ifndef LEGACY_BACKEND
+                                if (isBorn)
+                                {
+                                    compiler->codeGen->genUpdateVarReg(fldVarDsc, tree);
+                                }
+#endif // !LEGACY_BACKEND
+                                compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
+                            }
+#ifndef LEGACY_BACKEND
+                            else
+                            {
+                                VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex);
+                            }
+#endif // !LEGACY_BACKEND
+                        }
+                    }
+                    else if (ForCodeGen && VarSetOps::IsMember(compiler, varDeltaSet, fldVarIndex))
+                    {
+                        if (compiler->lvaTable[i].lvIsInReg())
+                        {
+#ifndef LEGACY_BACKEND
+                            if (isBorn)
+                            {
+                                compiler->codeGen->genUpdateVarReg(fldVarDsc, tree);
+                            }
+#endif // !LEGACY_BACKEND
+                            compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree));
+                        }
+#ifndef LEGACY_BACKEND
+                        else
+                        {
+                            VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex);
+                        }
+#endif // !LEGACY_BACKEND
+                    }
+                }
+            }
+        }
+
+        // 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);
+        }
+    }
+
+    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)
+        {
+#ifndef LEGACY_BACKEND
+
+            // 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)
+                {
+                    VarSetOps::UnionD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
+                }
+                else
+                {
+                    VarSetOps::DiffD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet);
+                }
+
+#ifdef DEBUG
+                if (compiler->verbose)
+                {
+                    dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur);
+                    printf("\n");
+                }
+#endif // DEBUG
+            }
+
+#else // LEGACY_BACKEND
+
+#ifdef DEBUG
+            if (compiler->verbose)
+            {
+                VarSetOps::Assign(compiler, gcVarPtrSetNew, newLife);
+                VarSetOps::IntersectionD(compiler, gcVarPtrSetNew, compiler->codeGen->gcInfo.gcTrkStkPtrLcls);
+                if (!VarSetOps::Equal(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcVarPtrSetNew))
+                {
+                    printf("\t\t\t\t\t\t\tGCvars: ");
+                    dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur);
+                    printf(" => ");
+                    dumpConvertedVarSet(compiler, gcVarPtrSetNew);
+                    printf("\n");
+                }
+            }
+#endif // DEBUG
+            VarSetOps::Assign(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur,
+                              compiler->codeGen->gcInfo.gcTrkStkPtrLcls);
+            VarSetOps::IntersectionD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, newLife);
+
+#endif // LEGACY_BACKEND
+
+            compiler->codeGen->siUpdate();
+        }
+    }
+
+#ifndef LEGACY_BACKEND
+    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", varDsc - compiler->lvaTable);
+                }
+#endif // DEBUG
+            }
+        }
+    }
+#endif // !LEGACY_BACKEND
+}
+
+//------------------------------------------------------------------------
+// UpdateLife: Determine whether the tree affects liveness, and update liveness sets accordingly.
+//
+// Arguments:
+//    tree - the tree which effect on liveness is processed.
+//
+template <bool ForCodeGen>
+void TreeLifeUpdater<ForCodeGen>::UpdateLife(GenTree* tree)
+{
+    assert(compiler->GetCurLVEpoch() == epoch);
+    // TODO-Cleanup: We shouldn't really be calling this more than once
+    if (tree == compiler->compCurLifeTree)
+    {
+        return;
+    }
+
+    if (!tree->OperIsNonPhiLocal() && compiler->fgIsIndirOfAddrOfLocal(tree) == nullptr)
+    {
+        return;
+    }
+
+    UpdateLifeVar(tree);
+}
+
+template class TreeLifeUpdater<true>;
+template class TreeLifeUpdater<false>;
diff --git a/src/jit/treelifeupdater.h b/src/jit/treelifeupdater.h
new file mode 100644 (file)
index 0000000..21a7f92
--- /dev/null
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include "compiler.h"
+
+//------------------------------------------------------------------------
+// TreeLifeUpdater: class that handles changes in variable liveness from a given tree.
+// Keeps set of temporary VARSET_TP during its lifetime to avoid unnecessary memory allocations.
+template <bool ForCodeGen>
+class TreeLifeUpdater
+{
+public:
+    TreeLifeUpdater(Compiler* compiler);
+    void UpdateLife(GenTree* tree);
+
+private:
+    void UpdateLifeVar(GenTree* tree);
+
+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).
+    int       epoch;          // VarSets epoch when the class was created, must stay the same during its using.
+#endif                        // DEBUG
+};