summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
authorBrian Bohe <brianbohe@gmail.com>2019-03-29 16:38:53 -0700
committerBruce Forstall <brucefo@microsoft.com>2019-03-29 16:38:53 -0700
commit493023e6e1bc03ec04ee4aa9f390e37dc4d0906e (patch)
treea7e0994c783217f0bcbc0fe23a2bbf5517a2b79d /src/jit
parent1df87c785e0e43392abf4bcba56e2bf4d9249fd4 (diff)
downloadcoreclr-493023e6e1bc03ec04ee4aa9f390e37dc4d0906e.tar.gz
coreclr-493023e6e1bc03ec04ee4aa9f390e37dc4d0906e.tar.bz2
coreclr-493023e6e1bc03ec04ee4aa9f390e37dc4d0906e.zip
A new way of tracking variables (#23373)
* Defining VariableLiveRange class * Adding some typedefs to avoid rewriting * Defining VariableLiveDescriptor class * Initializing VariableLiveRange structures before BasicBlock code is being generated * Getting a siVarLoc for variable homes from a given LclVarDsc and stack level * Defining VariableLiveKeeper class * Reporting VariableLiveRanges on changes of variable livenesss or variable homes * Adding USING_VARIABLE_LIVE_RANGE flag to enable disable VariableLiveRange * Send VariableLiveRanges to debugger * Reporting variable homes on prolog * Wrong argument * Miss to change variable homes count before sending them to debugger * Adding dumper of VariableLiveRanges for each blocks and end of code generation * Close all open VaribleLiveRanges on last BasicBlock * Changing order of properties initialization on VariableLiveRange constructor * Type error on assignation * Rephrasing comments, moving dumps and fixing typos * Changing const VARSET_TP* for VARSET_VALARG_TP on args * Variable home was variable location in VariableLiveRange context * Rephrase and rename of VariableLiveKeeper properties * Missing some renames * Adding const where BasicBlock should not be modified * siBeginBlock and siInit have support for debug code for VariableLiveRange and siScope info * Adding USING_VARIABLE_LIVE_RANGE flags on methods definition. * Variable home -> variable location * Renaming and rephrasing names and uses of VariableLiveRange * Moving LiveRangeDumper ctor to class declation * Removing destructors Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Removing blank spaces and reordering functions inside class definition Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Miss to increment the index after refactoring * Logic for keeping the last BasicBlock end IL offset is shared between siScope and VariableLiverange for debug code * Missing to print on debug the last block VariableLiveRanges * Avoid updating VariableLiveRange when unspilling and dying at the same assembly instruction * Rephrasing #ifs and #ifdefs * Calling VariableLiveKeeper in one line * Avoid copying siVarLoc on genSetScopeInfo * Removing unused args from eeSetLVinfo * Changing VariableLiveKeeper ctor * Typo Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Updating VariableLiveDescriptor ctor Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Error on first argument Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Changing reference for pointer Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Renaming assembly offset -> native offset * removing unnecesary comments and asserts Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Update VariableLiveRange dump message Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Moving VariableLiveRanges classes inside VariableLiveKeeper * Wrong flag name * Adding documentation about how we track variables for debug info Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Adding opened issues to doc file Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Changing dump tittle Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Renaming VariableLiveKeeper property Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Update documentation Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Updating comments on flags Signed-off-by: Brian Bohe <brianbohe@gmail.com> * Setting Scope Info as default way of tracking variables for debug info Signed-off-by: Brian Bohe <brianbohe@gmail.com>
Diffstat (limited to 'src/jit')
-rw-r--r--src/jit/block.cpp2
-rw-r--r--src/jit/block.h6
-rw-r--r--src/jit/codegen.h109
-rw-r--r--src/jit/codegenarmarch.cpp5
-rw-r--r--src/jit/codegencommon.cpp114
-rw-r--r--src/jit/codegeninterface.h23
-rw-r--r--src/jit/codegenlinear.cpp71
-rw-r--r--src/jit/codegenxarch.cpp5
-rw-r--r--src/jit/compiler.cpp760
-rw-r--r--src/jit/compiler.h198
-rw-r--r--src/jit/compmemkind.h1
-rw-r--r--src/jit/ee_il_dll.cpp9
-rw-r--r--src/jit/jitstd/list.h2
-rw-r--r--src/jit/lsra.cpp9
-rw-r--r--src/jit/scopeinfo.cpp525
-rw-r--r--src/jit/treelifeupdater.cpp6
16 files changed, 1620 insertions, 225 deletions
diff --git a/src/jit/block.cpp b/src/jit/block.cpp
index dfba5c6a1f..9c1b0cc664 100644
--- a/src/jit/block.cpp
+++ b/src/jit/block.cpp
@@ -215,7 +215,7 @@ flowList* Compiler::BlockPredsWithEH(BasicBlock* blk)
//------------------------------------------------------------------------
// dspBlockILRange(): Display the block's IL range as [XXX...YYY), where XXX and YYY might be "???" for BAD_IL_OFFSET.
//
-void BasicBlock::dspBlockILRange()
+void BasicBlock::dspBlockILRange() const
{
if (bbCodeOffs != BAD_IL_OFFSET)
{
diff --git a/src/jit/block.h b/src/jit/block.h
index d186a79176..0fae4de5e1 100644
--- a/src/jit/block.h
+++ b/src/jit/block.h
@@ -897,9 +897,9 @@ struct BasicBlock : private LIR::Range
// is bbCodeOffsEnd - bbCodeOffs, assuming neither are BAD_IL_OFFSET.
#ifdef DEBUG
- void dspBlockILRange(); // Display the block's IL range as [XXX...YYY), where XXX and YYY might be "???" for
- // BAD_IL_OFFSET.
-#endif // DEBUG
+ void dspBlockILRange() const; // Display the block's IL range as [XXX...YYY), where XXX and YYY might be "???" for
+ // BAD_IL_OFFSET.
+#endif // DEBUG
VARSET_TP bbVarUse; // variables used by block (before an assignment)
VARSET_TP bbVarDef; // variables assigned by block (before a use)
diff --git a/src/jit/codegen.h b/src/jit/codegen.h
index 1631480853..d490057374 100644
--- a/src/jit/codegen.h
+++ b/src/jit/codegen.h
@@ -558,38 +558,60 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
siVarLoc* varLoc);
void genSetScopeInfo();
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // Send VariableLiveRanges as debug info to the debugger
+ void genSetScopeInfoUsingVariableRanges();
+#endif // USING_VARIABLE_LIVE_RANGE
+
#ifdef USING_SCOPE_INFO
void genSetScopeInfoUsingsiScope();
- /*
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XX XX
- XX ScopeInfo XX
- XX XX
- XX Keeps track of the scopes during code-generation. XX
- XX This is used to translate the local-variable debugging information XX
- XX from IL offsets to native code offsets. XX
- XX XX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- */
+/*
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX ScopeInfo XX
+XX XX
+XX Keeps track of the scopes during code-generation. XX
+XX This is used to translate the local-variable debugging information XX
+XX from IL offsets to native code offsets. XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
- /*****************************************************************************/
- /*****************************************************************************
- * ScopeInfo
- *
- * This class is called during code gen at block-boundaries, and when the
- * set of live variables changes. It keeps track of the scope of the variables
- * in terms of the native code PC.
- */
+/*****************************************************************************/
+/*****************************************************************************
+ * ScopeInfo
+ *
+ * This class is called during code gen at block-boundaries, and when the
+ * set of live variables changes. It keeps track of the scope of the variables
+ * in terms of the native code PC.
+ */
+#endif // USING_VARIABLE_LIVE_RANGE
public:
void siInit();
+ void checkICodeDebugInfo();
+ // The logic used to report debug info on debug code is the same for ScopeInfo and
+ // VariableLiveRange
void siBeginBlock(BasicBlock* block);
-
void siEndBlock(BasicBlock* block);
+
+ // VariableLiveRange and siScope needs this method to report variables on debug code
+ void siOpenScopesForNonTrackedVars(const BasicBlock* block, unsigned int lastBlockILEndOffset);
+
+protected:
+#if FEATURE_EH_FUNCLETS
+ bool siInFuncletRegion; // Have we seen the start of the funclet region?
+#endif // FEATURE_EH_FUNCLETS
+
+ IL_OFFSET siLastEndOffs; // IL offset of the (exclusive) end of the last block processed
+
+#ifdef USING_SCOPE_INFO
+
+public:
// Closes the "ScopeInfo" of the tracked variables that has become dead.
virtual void siUpdate();
@@ -635,12 +657,6 @@ protected:
siScope** siLatestTrackedScopes;
- IL_OFFSET siLastEndOffs; // IL offset of the (exclusive) end of the last block processed
-
-#if FEATURE_EH_FUNCLETS
- bool siInFuncletRegion; // Have we seen the start of the funclet region?
-#endif // FEATURE_EH_FUNCLETS
-
// Functions
siScope* siNewScope(unsigned LVnum, unsigned varNum);
@@ -666,23 +682,26 @@ public:
const char* siStackVarName(size_t offs, size_t size, unsigned reg, unsigned stkOffs);
#endif // LATE_DISASM
- /*
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XX XX
- XX PrologScopeInfo XX
- XX XX
- XX We need special handling in the prolog block, as the parameter variables XX
- XX may not be in the same position described by genLclVarTable - they all XX
- XX start out on the stack XX
- XX XX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- */
-
+/*
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX PrologScopeInfo XX
+XX XX
+XX We need special handling in the prolog block, as the parameter variables XX
+XX may not be in the same position described by genLclVarTable - they all XX
+XX start out on the stack XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+#endif // USING_SCOPE_INFO
public:
void psiBegProlog();
+ void psiEndProlog();
+
+#ifdef USING_SCOPE_INFO
void psiAdjustStackLevel(unsigned size);
void psiMoveESPtoEBP();
@@ -691,8 +710,6 @@ public:
void psiMoveToStack(unsigned varNum);
- void psiEndProlog();
-
/**************************************************************************
* PROTECTED
*************************************************************************/
@@ -745,9 +762,11 @@ protected:
void psiEndPrologScope(psiScope* scope);
- void psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc1);
+ void psiSetScopeOffset(psiScope* newScope, const LclVarDsc* lclVarDsc) const;
#endif // USING_SCOPE_INFO
+ NATIVE_OFFSET psiGetVarStackOffset(const LclVarDsc* lclVarDsc) const;
+
/*****************************************************************************
* TrnslLocalVarInfo
*
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp
index 0c84bedb75..cdb818b934 100644
--- a/src/jit/codegenarmarch.cpp
+++ b/src/jit/codegenarmarch.cpp
@@ -2176,6 +2176,11 @@ void CodeGen::genRegCopy(GenTree* treeNode)
genUpdateVarReg(varDsc, treeNode);
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // Report the home change for this variable
+ compiler->getVariableLiveKeeper()->siUpdateVariableLiveRange(varDsc, lcl->gtLclNum);
+#endif // USING_VARIABLE_LIVE_RANGE
+
// The new location is going live
genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
}
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index e54342a1d5..781f0fc549 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -694,6 +694,11 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
VarSetOps::RemoveElemD(this, codeGen->gcInfo.gcVarPtrSetCur, deadVarIndex);
JITDUMP("\t\t\t\t\t\t\tV%02u becoming dead\n", varNum);
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ VariableLiveKeeper* varLiveKeeper = getVariableLiveKeeper();
+ varLiveKeeper->siEndVariableLiveRange(varNum);
+#endif // USING_VARIABLE_LIVE_RANGE
}
VarSetOps::Iter bornIter(this, bornSet);
@@ -731,7 +736,13 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
VarSetOps::AddElemD(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex);
JITDUMP("\t\t\t\t\t\t\tV%02u becoming live\n", varNum);
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ VariableLiveKeeper* varLiveKeeper = getVariableLiveKeeper();
+ varLiveKeeper->siStartVariableLiveRange(varDsc, varNum);
+#endif // USING_VARIABLE_LIVE_RANGE
}
+
#ifdef USING_SCOPE_INFO
codeGen->siUpdate();
#endif // USING_SCOPE_INFO
@@ -2284,6 +2295,13 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
genSetScopeInfo();
+#if defined(USING_VARIABLE_LIVE_RANGE) && defined(DEBUG)
+ if (compiler->verbose)
+ {
+ compiler->getVariableLiveKeeper()->dumpLvaVariableLiveRanges();
+ }
+#endif // defined(USING_VARIABLE_LIVE_RANGE) && defined(DEBUG)
+
#ifdef LATE_DISASM
unsigned finalHotCodeSize;
unsigned finalColdCodeSize;
@@ -7631,13 +7649,12 @@ void CodeGen::genFnProlog()
printf("\n__prolog:\n");
}
#endif
-#ifdef USING_SCOPE_INFO
+
if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
{
// Create new scopes for the method-parameters for the prolog-block.
psiBegProlog();
}
-#endif // USING_SCOPE_INFO
#ifdef DEBUG
@@ -8284,12 +8301,11 @@ void CodeGen::genFnProlog()
genPrologPadForReJit();
getEmitter()->emitMarkPrologEnd();
}
-#ifdef USING_SCOPE_INFO
if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
{
psiEndProlog();
}
-#endif // USING_SCOPE_INFO
+
if (hasGCRef)
{
getEmitter()->emitSetFrameRangeGCRs(GCrefLo, GCrefHi);
@@ -10478,7 +10494,22 @@ void CodeGen::genSetScopeInfo()
}
#endif
- if (compiler->info.compVarScopesCount == 0)
+ unsigned varsLocationsCount = 0;
+
+#ifdef USING_SCOPE_INFO
+ if (compiler->info.compVarScopesCount > 0)
+ {
+ varsLocationsCount = siScopeCnt + psiScopeCnt;
+ }
+#else // USING_SCOPE_INFO
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ varsLocationsCount = (unsigned int)compiler->getVariableLiveKeeper()->getLiveRangesCount();
+#endif // USING_VARIABLE_LIVE_RANGE
+
+#endif // USING_SCOPE_INFO
+
+ if (varsLocationsCount == 0)
{
compiler->eeSetLVcount(0);
compiler->eeSetLVdone();
@@ -10487,22 +10518,27 @@ void CodeGen::genSetScopeInfo()
noway_assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
- unsigned varsHomeCount = 0;
-#ifdef USING_SCOPE_INFO
- varsHomeCount = siScopeCnt + psiScopeCnt;
-#endif // USING_SCOPE_INFO
- compiler->eeSetLVcount(varsHomeCount);
+ compiler->eeSetLVcount(varsLocationsCount);
#ifdef DEBUG
- genTrnslLocalVarCount = varsHomeCount;
- if (varsHomeCount)
+ genTrnslLocalVarCount = varsLocationsCount;
+ if (varsLocationsCount)
{
- genTrnslLocalVarInfo = new (compiler, CMK_DebugOnly) TrnslLocalVarInfo[varsHomeCount];
+ genTrnslLocalVarInfo = new (compiler, CMK_DebugOnly) TrnslLocalVarInfo[varsLocationsCount];
}
#endif
#ifdef USING_SCOPE_INFO
genSetScopeInfoUsingsiScope();
+#else // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // We can have one of both flags defined, both, or none. Specially if we need to compare both
+ // both results. But we cannot report both to the debugger, since there would be overlapping
+ // intervals, and may not indicate the same variable location.
+
+ genSetScopeInfoUsingVariableRanges();
+
+#endif // USING_VARIABLE_LIVE_RANGE
#endif // USING_SCOPE_INFO
compiler->eeSetLVdone();
@@ -10579,6 +10615,53 @@ void CodeGen::genSetScopeInfoUsingsiScope()
}
#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+//------------------------------------------------------------------------
+// genSetScopeInfoUsingVariableRanges: Call "genSetScopeInfo" with the
+// "VariableLiveRanges" created for the arguments, special arguments and
+// IL local variables.
+//
+// Notes:
+// This function is called from "genSetScopeInfo" once the code is generated
+// and we want to send debug info to the debugger.
+//
+void CodeGen::genSetScopeInfoUsingVariableRanges()
+{
+ VariableLiveKeeper* varLiveKeeper = compiler->getVariableLiveKeeper();
+ unsigned int liveRangeIndex = 0;
+
+ for (unsigned int varNum = 0; varNum < compiler->info.compLocalsCount; varNum++)
+ {
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
+
+ if (compiler->compMap2ILvarNum(varNum) != (unsigned int)ICorDebugInfo::UNKNOWN_ILNUM)
+ {
+ VariableLiveKeeper::LiveRangeList* liveRanges = varLiveKeeper->getLiveRangesForVar(varNum);
+
+ for (VariableLiveKeeper::VariableLiveRange& liveRange : *liveRanges)
+ {
+ UNATIVE_OFFSET startOffs = liveRange.m_StartEmitLocation.CodeOffset(getEmitter());
+ UNATIVE_OFFSET endOffs = liveRange.m_EndEmitLocation.CodeOffset(getEmitter());
+
+ if (varDsc->lvIsParam && (startOffs == endOffs))
+ {
+ // If the length is zero, it means that the prolog is empty. In that case,
+ // CodeGen::genSetScopeInfo will report the liveness of all arguments
+ // as spanning the first instruction in the method, so that they can
+ // at least be inspected on entry to the method.
+ endOffs++;
+ }
+
+ genSetScopeInfo(liveRangeIndex, startOffs, endOffs - startOffs, varNum,
+ varNum /* I dont know what is the which in eeGetLvInfo */, true,
+ &liveRange.m_VarLocation);
+ liveRangeIndex++;
+ }
+ }
+ }
+}
+#endif // USING_VARIABLE_LIVE_RANGE
+
//------------------------------------------------------------------------
// genSetScopeInfo: Record scope information for debug info
//
@@ -10612,7 +10695,6 @@ void CodeGen::genSetScopeInfo(unsigned which,
// so we don't need this code.
// Is this a varargs function?
-
if (compiler->info.compIsVarArgs && varNum != compiler->lvaVarargsHandleArg &&
varNum < compiler->info.compArgsCount && !compiler->lvaTable[varNum].lvIsRegArg)
{
@@ -10673,7 +10755,7 @@ void CodeGen::genSetScopeInfo(unsigned which,
#endif // DEBUG
- compiler->eeSetLVinfo(which, startOffs, length, ilVarNum, LVnum, name, avail, varLoc);
+ compiler->eeSetLVinfo(which, startOffs, length, ilVarNum, *varLoc);
}
/*****************************************************************************/
@@ -10728,7 +10810,7 @@ const char* CodeGen::siStackVarName(size_t offs, size_t size, unsigned reg, unsi
for (unsigned i = 0; i < genTrnslLocalVarCount; i++)
{
- if ((genTrnslLocalVarInfo[i].tlviVarLoc.vlIsOnStk((regNumber)reg, stkOffs)) &&
+ if ((genTrnslLocalVarInfo[i].tlviVarLoc.vlIsOnStack((regNumber)reg, stkOffs)) &&
(genTrnslLocalVarInfo[i].tlviAvailable == true) && (genTrnslLocalVarInfo[i].tlviStartPC <= offs + size) &&
(genTrnslLocalVarInfo[i].tlviStartPC + genTrnslLocalVarInfo[i].tlviLength > offs))
{
diff --git a/src/jit/codegeninterface.h b/src/jit/codegeninterface.h
index 6935a77e3f..26d02f8f58 100644
--- a/src/jit/codegeninterface.h
+++ b/src/jit/codegeninterface.h
@@ -25,11 +25,16 @@
#include "jitgcinfo.h"
#include "treelifeupdater.h"
-// Disable this flag to avoid using psiScope/siScope info to report reporting
-// variables' home location during the method/prolog code.
#if 1
+// Enable USING_SCOPE_INFO flag to use psiScope/siScope info to report variables' locations.
#define USING_SCOPE_INFO
-#endif // USING_SCOPE_INFO
+#endif
+#if 0
+// Enable USING_VARIABLE_LIVE_RANGE flag to use VariableLiveRange info to report variables' locations.
+// Note: if both USING_SCOPE_INFO and USING_VARIABLE_LIVE_RANGE are defined, then USING_SCOPE_INFO
+// information is reported to the debugger.
+#define USING_VARIABLE_LIVE_RANGE
+#endif
// Forward reference types
@@ -521,7 +526,11 @@ public:
// Helper functions
bool vlIsInReg(regNumber reg) const;
- bool vlIsOnStk(regNumber reg, signed offset) const;
+ bool vlIsOnStack(regNumber reg, signed offset) const;
+ bool vlIsOnStack() const;
+
+ void storeVariableInRegisters(regNumber reg, regNumber otherReg);
+ void storeVariableOnStack(regNumber stackBaseReg, NATIVE_OFFSET variableStackOffset);
siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed);
siVarLoc(){};
@@ -543,6 +552,12 @@ public:
};
public:
+ siVarLoc getSiVarLoc(const LclVarDsc* varDsc, unsigned int stackLevel) const;
+
+#ifdef DEBUG
+ void dumpSiVarLoc(const siVarLoc* varLoc) const;
+#endif
+
unsigned getCurrentStackLevel() const;
protected:
diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp
index 6b16e30e89..9661d43bd4 100644
--- a/src/jit/codegenlinear.cpp
+++ b/src/jit/codegenlinear.cpp
@@ -84,13 +84,15 @@ void CodeGen::genInitializeRegisterState()
// iterated.
void CodeGen::genInitialize()
{
-#ifdef USING_SCOPE_INFO
// Initialize the line# tracking logic
if (compiler->opts.compScopeInfo)
{
siInit();
}
-#endif // USING_SCOPE_INFO
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ compiler->initializeVariableLiveKeeper();
+#endif // USING_VARIABLE_LIVE_RANGE
// The current implementation of switch tables requires the first block to have a label so it
// can generate offsets to the switch label targets.
@@ -355,9 +357,10 @@ void CodeGen::genCodeForBBlist()
/* Tell everyone which basic block we're working on */
compiler->compCurBB = block;
-#ifdef USING_SCOPE_INFO
+
+ // Needed when jitting debug code
siBeginBlock(block);
-#endif // USING_SCOPE_INFO
+
// BBF_INTERNAL blocks don't correspond to any single IL instruction.
if (compiler->opts.compDbgInfo && (block->bbFlags & BBF_INTERNAL) &&
!compiler->fgBBisScratch(block)) // If the block is the distinguished first scratch block, then no need to
@@ -510,19 +513,27 @@ void CodeGen::genCodeForBBlist()
// This can lead to problems when debugging the generated code. To prevent these issues, make sure
// we've generated code for the last IL offset we saw in the block.
genEnsureCodeEmitted(currentILOffset);
-#ifdef USING_SCOPE_INFO
- if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
+
+ /* Is this the last block, and are there any open scopes left ? */
+
+ bool isLastBlockProcessed = (block->bbNext == nullptr);
+ if (block->isBBCallAlwaysPair())
{
- siEndBlock(block);
+ isLastBlockProcessed = (block->bbNext->bbNext == nullptr);
+ }
- /* Is this the last block, and are there any open scopes left ? */
+#ifdef USING_VARIABLE_LIVE_RANGE
+ if (compiler->opts.compDbgInfo && isLastBlockProcessed)
+ {
+ compiler->getVariableLiveKeeper()->siEndAllVariableLiveRange(compiler->compCurLife);
+ }
+#endif // USING_VARIABLE_LIVE_RANGE
- bool isLastBlockProcessed = (block->bbNext == nullptr);
- if (block->isBBCallAlwaysPair())
- {
- isLastBlockProcessed = (block->bbNext->bbNext == nullptr);
- }
+ if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
+ {
+ siEndBlock(block);
+#ifdef USING_SCOPE_INFO
if (isLastBlockProcessed && siOpenScopeList.scNext)
{
/* This assert no longer holds, because we may insert a throw
@@ -534,8 +545,8 @@ void CodeGen::genCodeForBBlist()
siCloseAllOpenScopes();
}
- }
#endif // USING_SCOPE_INFO
+ }
SubtractStackLevel(savedStkLvl);
@@ -720,9 +731,14 @@ void CodeGen::genCodeForBBlist()
break;
}
-#ifdef DEBUG
- compiler->compCurBB = nullptr;
-#endif
+#if defined(DEBUG) && defined(USING_VARIABLE_LIVE_RANGE)
+ if (compiler->verbose)
+ {
+ compiler->getVariableLiveKeeper()->dumpBlockVariableLiveRanges(block);
+ }
+#endif // defined(DEBUG) && defined(USING_VARIABLE_LIVE_RANGE)
+
+ INDEBUG(compiler->compCurBB = nullptr);
} //------------------ END-FOR each block of the method -------------------
@@ -817,6 +833,15 @@ void CodeGen::genSpillVar(GenTree* tree)
{
varDsc->lvOtherReg = REG_STK;
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ if (needsSpill)
+ {
+ // We need this after "lvRegNum" has change because now we are sure that varDsc->lvIsInReg() is false.
+ // "SiVarLoc" constructor uses the "LclVarDsc" of the variable.
+ compiler->getVariableLiveKeeper()->siUpdateVariableLiveRange(varDsc, varNum);
+ }
+#endif // USING_VARIABLE_LIVE_RANGE
}
//------------------------------------------------------------------------
@@ -976,6 +1001,18 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
if ((unspillTree->gtFlags & GTF_SPILL) == 0)
{
genUpdateVarReg(varDsc, tree);
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // We want "VariableLiveRange" inclusive on the beginbing and exclusive on the ending.
+ // For that we shouldn't report an update of the variable location if is becoming dead
+ // on the same native offset.
+ if ((unspillTree->gtFlags & GTF_VAR_DEATH) == 0)
+ {
+ // Report the home change for this variable
+ compiler->getVariableLiveKeeper()->siUpdateVariableLiveRange(varDsc, lcl->gtLclNum);
+ }
+#endif // USING_VARIABLE_LIVE_RANGE
+
#ifdef DEBUG
if (VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex))
{
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 9256650978..16fb39dd91 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -4841,6 +4841,11 @@ void CodeGen::genRegCopy(GenTree* treeNode)
genUpdateVarReg(varDsc, treeNode);
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // Report the home change for this variable
+ compiler->getVariableLiveKeeper()->siUpdateVariableLiveRange(varDsc, lcl->gtLclNum);
+#endif // USING_VARIABLE_LIVE_RANGE
+
// The new location is going live
genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
}
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index c70e90da6a..e08a89aa12 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -10970,3 +10970,763 @@ bool Compiler::killGCRefs(GenTree* tree)
return false;
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+#ifdef DEBUG
+//------------------------------------------------------------------------
+// VariableLiveRanges dumpers
+//------------------------------------------------------------------------
+
+// Dump "VariableLiveRange" when code has not been generated and we don't have so the assembly native offset
+// but at least "emitLocation"s and "siVarLoc"
+void VariableLiveKeeper::VariableLiveRange::dumpVariableLiveRange(const CodeGenInterface* codeGen) const
+{
+ codeGen->dumpSiVarLoc(&m_VarLocation);
+ printf(" [ ");
+ m_StartEmitLocation.Print();
+ printf(", ");
+ if (m_EndEmitLocation.Valid())
+ {
+ m_EndEmitLocation.Print();
+ }
+ else
+ {
+ printf("NON_CLOSED_RANGE");
+ }
+ printf(" ]; ");
+}
+
+// Dump "VariableLiveRange" when code has been generated and we have the assembly native offset of each "emitLocation"
+void VariableLiveKeeper::VariableLiveRange::dumpVariableLiveRange(emitter* emit, const CodeGenInterface* codeGen) const
+{
+ assert(emit != nullptr);
+
+ // "VariableLiveRanges" are created setting its location ("m_VarLocation") and the initial native offset
+ // ("m_StartEmitLocation")
+ codeGen->dumpSiVarLoc(&m_VarLocation);
+
+ // If this is an open "VariableLiveRange", "m_EndEmitLocation" is non-valid and print -1
+ UNATIVE_OFFSET endAssemblyOffset = m_EndEmitLocation.Valid() ? m_EndEmitLocation.CodeOffset(emit) : -1;
+
+ printf(" [%X , %X )", m_StartEmitLocation.CodeOffset(emit), m_EndEmitLocation.CodeOffset(emit));
+}
+
+//------------------------------------------------------------------------
+// LiveRangeDumper
+//------------------------------------------------------------------------
+//------------------------------------------------------------------------
+// resetDumper: If the the "liveRange" has its last "VariableLiveRange" closed, it makes
+// the "LiveRangeDumper" points to end of "liveRange" (nullptr). In other case,
+// it makes the "LiveRangeDumper" points to the last "VariableLiveRange" of
+// "liveRange", which is opened.
+//
+// Arguments:
+// liveRanges - the "LiveRangeList" of the "VariableLiveDescriptor" we want to
+// udpate its "LiveRangeDumper".
+//
+// Notes:
+// This method is expected to be called once a the code for a BasicBlock has been
+// generated and all the new "VariableLiveRange"s of the variable during this block
+// has been dumped.
+void VariableLiveKeeper::LiveRangeDumper::resetDumper(const LiveRangeList* liveRanges)
+{
+ // There must have reported something in order to reset
+ assert(m_hasLiveRangestoDump);
+
+ if (liveRanges->back().m_EndEmitLocation.Valid())
+ {
+ // the last "VariableLiveRange" is closed and the variable
+ // is no longer alive
+ m_hasLiveRangestoDump = false;
+ }
+ else
+ {
+ // the last "VariableLiveRange" remains opened because it is
+ // live at "BasicBlock"s "bbLiveOut".
+ m_StartingLiveRange = liveRanges->backPosition();
+ }
+}
+
+//------------------------------------------------------------------------
+// setDumperStartAt: Make "LiveRangeDumper" instance points the last "VariableLiveRange"
+// added so we can starts dumping from there after the actual "BasicBlock"s code is generated.
+//
+// Arguments:
+// liveRangeIt - an iterator to a position in "VariableLiveDescriptor::m_VariableLiveRanges"
+//
+// Return Value:
+// A const pointer to the "LiveRangeList" containing all the "VariableLiveRange"s
+// of the variable with index "varNum".
+//
+// Notes:
+// "varNum" should be always a valid inde ("varnum" < "m_LiveDscCount")
+void VariableLiveKeeper::LiveRangeDumper::setDumperStartAt(const LiveRangeListIterator liveRangeIt)
+{
+ m_hasLiveRangestoDump = true;
+ m_StartingLiveRange = liveRangeIt;
+}
+
+//------------------------------------------------------------------------
+// getStartForDump: Return an iterator to the first "VariableLiveRange" edited/added
+// during the current "BasicBlock"
+//
+// Return Value:
+// A LiveRangeListIterator to the first "VariableLiveRange" in "LiveRangeList" which
+// was used during last "BasicBlock".
+//
+VariableLiveKeeper::LiveRangeListIterator VariableLiveKeeper::LiveRangeDumper::getStartForDump() const
+{
+ return m_StartingLiveRange;
+}
+
+//------------------------------------------------------------------------
+// hasLiveRangesToDump: Retutn wheter at least a "VariableLiveRange" was alive during
+// the current "BasicBlock"'s code generation
+//
+// Return Value:
+// A boolean indicating indicating if there is at least a "VariableLiveRange"
+// that has been used for the variable during last "BasicBlock".
+//
+bool VariableLiveKeeper::LiveRangeDumper::hasLiveRangesToDump() const
+{
+ return m_hasLiveRangestoDump;
+}
+#endif // DEBUG
+
+//------------------------------------------------------------------------
+// VariableLiveDescriptor
+//------------------------------------------------------------------------
+
+VariableLiveKeeper::VariableLiveDescriptor::VariableLiveDescriptor(CompAllocator allocator)
+{
+ // Initialize an empty list
+ m_VariableLiveRanges = new (allocator) LiveRangeList(allocator);
+
+ INDEBUG(m_VariableLifeBarrier = new (allocator) LiveRangeDumper(m_VariableLiveRanges));
+}
+
+//------------------------------------------------------------------------
+// hasVariableLiveRangeOpen: Return true if the variable is still alive,
+// false in other case.
+//
+bool VariableLiveKeeper::VariableLiveDescriptor::hasVariableLiveRangeOpen() const
+{
+ return !m_VariableLiveRanges->empty() && !m_VariableLiveRanges->back().m_EndEmitLocation.Valid();
+}
+
+//------------------------------------------------------------------------
+// getLiveRanges: Return the list of variable locations for this variable.
+//
+// Return Value:
+// A const LiveRangeList* pointing to the first variable location if it has
+// any or the end of the list in other case.
+//
+VariableLiveKeeper::LiveRangeList* VariableLiveKeeper::VariableLiveDescriptor::getLiveRanges() const
+{
+ return m_VariableLiveRanges;
+}
+
+//------------------------------------------------------------------------
+// startLiveRangeFromEmitter: Report this variable as being born in "varLocation"
+// since the instruction where "emit" is located.
+//
+// Arguments:
+// varLocation - the home of the variable.
+// emit - an emitter* instance located at the first instruction from
+// where "varLocation" becomes valid.
+//
+// Assumptions:
+// This variable is being born so it should be dead.
+//
+// Notes:
+// The position of "emit" matters to ensure intervals inclusive of the
+// beginning and exclusive of the end.
+//
+void VariableLiveKeeper::VariableLiveDescriptor::startLiveRangeFromEmitter(CodeGenInterface::siVarLoc varLocation,
+ emitter* emit) const
+{
+ noway_assert(emit != nullptr);
+
+ // Is the first "VariableLiveRange" or the previous one has been closed so its "m_EndEmitLocation" is valid
+ noway_assert(m_VariableLiveRanges->empty() || m_VariableLiveRanges->back().m_EndEmitLocation.Valid());
+
+ // Creates new live range with invalid end
+ m_VariableLiveRanges->emplace_back(varLocation, emitLocation(), emitLocation());
+ m_VariableLiveRanges->back().m_StartEmitLocation.CaptureLocation(emit);
+
+#ifdef DEBUG
+ if (!m_VariableLifeBarrier->hasLiveRangesToDump())
+ {
+ m_VariableLifeBarrier->setDumperStartAt(m_VariableLiveRanges->backPosition());
+ }
+#endif // DEBUG
+
+ // startEmitLocationendEmitLocation has to be Valid and endEmitLocationendEmitLocation not
+ noway_assert(m_VariableLiveRanges->back().m_StartEmitLocation.Valid());
+ noway_assert(!m_VariableLiveRanges->back().m_EndEmitLocation.Valid());
+}
+
+//------------------------------------------------------------------------
+// endLiveRangeAtEmitter: Report this variable as becoming dead since the
+// instruction where "emit" is located.
+//
+// Arguments:
+// emit - an emitter* instance located at the first instruction from
+// this variable becomes dead.
+//
+// Assumptions:
+// This variable is becoming dead so it should be alive.
+//
+// Notes:
+// The position of "emit" matters to ensure intervals inclusive of the
+// beginning and exclusive of the end.
+//
+void VariableLiveKeeper::VariableLiveDescriptor::endLiveRangeAtEmitter(emitter* emit) const
+{
+ noway_assert(emit != nullptr);
+ noway_assert(hasVariableLiveRangeOpen());
+
+ // Using [close, open) ranges so as to not compute the size of the last instruction
+ m_VariableLiveRanges->back().m_EndEmitLocation.CaptureLocation(emit);
+
+ // No m_EndEmitLocation has to be Valid
+ noway_assert(m_VariableLiveRanges->back().m_EndEmitLocation.Valid());
+}
+
+//------------------------------------------------------------------------
+// UpdateLiveRangeAtEmitter: Report this variable as changing its variable
+// home to "varLocation" since the instruction where "emit" is located.
+//
+// Arguments:
+// varLocation - the new variable location.
+// emit - an emitter* instance located at the first instruction from
+// where "varLocation" becomes valid.
+//
+// Assumptions:
+// This variable is being born so it should be dead.
+//
+// Notes:
+// The position of "emit" matters to ensure intervals inclusive of the
+// beginning and exclusive of the end.
+//
+void VariableLiveKeeper::VariableLiveDescriptor::updateLiveRangeAtEmitter(CodeGenInterface::siVarLoc varLocation,
+ emitter* emit) const
+{
+ // This variable is changing home so it has been started before during this block
+ noway_assert(m_VariableLiveRanges != nullptr && !m_VariableLiveRanges->empty());
+
+ // And its last m_EndEmitLocation has to be invalid
+ noway_assert(!m_VariableLiveRanges->back().m_EndEmitLocation.Valid());
+
+ // If we are reporting again the same home, that means we are doing something twice?
+ // noway_assert(! CodeGenInterface::siVarLoc::Equals(&m_VariableLiveRanges->back().m_VarLocation, varLocation));
+
+ // Close previous live range
+ endLiveRangeAtEmitter(emit);
+
+ startLiveRangeFromEmitter(varLocation, emit);
+}
+
+#ifdef DEBUG
+void VariableLiveKeeper::VariableLiveDescriptor::dumpAllRegisterLiveRangesForBlock(
+ emitter* emit, const CodeGenInterface* codeGen) const
+{
+ printf("[");
+ for (LiveRangeListIterator it = m_VariableLiveRanges->begin(); it != m_VariableLiveRanges->end(); it++)
+ {
+ it->dumpVariableLiveRange(emit, codeGen);
+ }
+ printf("]\n");
+}
+
+void VariableLiveKeeper::VariableLiveDescriptor::dumpRegisterLiveRangesForBlockBeforeCodeGenerated(
+ const CodeGenInterface* codeGen) const
+{
+ noway_assert(codeGen != nullptr);
+
+ printf("[");
+ for (LiveRangeListIterator it = m_VariableLifeBarrier->getStartForDump(); it != m_VariableLiveRanges->end(); it++)
+ {
+ it->dumpVariableLiveRange(codeGen);
+ }
+ printf("]\n");
+}
+
+// Returns true if a live range for this variable has been recorded
+bool VariableLiveKeeper::VariableLiveDescriptor::hasVarLiveRangesToDump() const
+{
+ return !m_VariableLiveRanges->empty();
+}
+
+// Returns true if a live range for this variable has been recorded from last call to EndBlock
+bool VariableLiveKeeper::VariableLiveDescriptor::hasVarLiverRangesFromLastBlockToDump() const
+{
+ return m_VariableLifeBarrier->hasLiveRangesToDump();
+}
+
+// Reset the barrier so as to dump only next block changes on next block
+void VariableLiveKeeper::VariableLiveDescriptor::endBlockLiveRanges()
+{
+ // make "m_VariableLifeBarrier->m_StartingLiveRange" now points to nullptr for printing purposes
+ m_VariableLifeBarrier->resetDumper(m_VariableLiveRanges);
+}
+#endif // DEBUG
+
+//------------------------------------------------------------------------
+// VariableLiveKeeper
+//------------------------------------------------------------------------
+// Initialize structures for VariableLiveRanges
+void Compiler::initializeVariableLiveKeeper()
+{
+ CompAllocator allocator = getAllocator(CMK_VariableLiveRanges);
+
+ int amountTrackedVariables = opts.compDbgInfo ? info.compLocalsCount : 0;
+ int amountTrackedArgs = opts.compDbgInfo ? info.compArgsCount : 0;
+
+ varLiveKeeper = new (allocator) VariableLiveKeeper(amountTrackedVariables, amountTrackedArgs, this, allocator);
+}
+
+VariableLiveKeeper* Compiler::getVariableLiveKeeper() const
+{
+ return varLiveKeeper;
+};
+
+//------------------------------------------------------------------------
+// VariableLiveKeeper: Create an instance of the object in charge of managing
+// VariableLiveRanges and intialize the array "m_vlrLiveDsc".
+//
+// Arguments:
+// totalLocalCount - the count of args, special args and IL Local
+// variables in the method.
+// argsCount - the count of args and special args in the method.
+// compiler - a compiler instance
+//
+VariableLiveKeeper::VariableLiveKeeper(unsigned int totalLocalCount,
+ unsigned int argsCount,
+ Compiler* comp,
+ CompAllocator allocator)
+ : m_LiveDscCount(totalLocalCount)
+ , m_LiveArgsCount(argsCount)
+ , m_Compiler(comp)
+ , m_LastBasicBlockHasBeenEmited(false)
+{
+ if (m_LiveDscCount > 0)
+ {
+ // Allocate memory for "m_vlrLiveDsc" and initialize each "VariableLiveDescriptor"
+ m_vlrLiveDsc = allocator.allocate<VariableLiveDescriptor>(m_LiveDscCount);
+
+ for (unsigned int varNum = 0; varNum < m_LiveDscCount; varNum++)
+ {
+ new (m_vlrLiveDsc + varNum, jitstd::placement_t()) VariableLiveDescriptor(allocator);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// siStartOrCloseVariableLiveRange: Reports the given variable as beign born
+// or becoming dead.
+//
+// Arguments:
+// varDsc - the variable for which a location changed will be reported
+// varNum - the index of the variable in the "compiler->lvaTable"
+// isBorn - whether the variable is being born from where the emitter is located.
+// isDying - whether the variable is dying from where the emitter is located.
+//
+// Assumptions:
+// The emitter should be located on the first instruction from where is true that
+// the variable becoming valid (when isBorn is true) or invalid (when isDying is true).
+//
+// Notes:
+// This method is being called from treeLifeUpdater when the variable is being born,
+// becoming dead, or both.
+//
+void VariableLiveKeeper::siStartOrCloseVariableLiveRange(const LclVarDsc* varDsc,
+ unsigned int varNum,
+ bool isBorn,
+ bool isDying)
+{
+ noway_assert(varDsc != nullptr);
+
+ // Only the variables that exists in the IL, "this", and special arguments
+ // are reported.
+ if (m_Compiler->opts.compDbgInfo && varNum < m_LiveDscCount)
+ {
+ if (isBorn && !isDying)
+ {
+ // "varDsc" is valid from this point
+ siStartVariableLiveRange(varDsc, varNum);
+ }
+ if (isDying && !isBorn)
+ {
+ // this variable live range is no longer valid from this point
+ siEndVariableLiveRange(varNum);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// siStartOrCloseVariableLiveRanges: Iterates the given set of variables
+// calling "siStartOrCloseVariableLiveRange" with each one.
+//
+// Arguments:
+// varsIndexSet - the set of variables to report start/end "VariableLiveRange"
+// isBorn - whether the set is being born from where the emitter is located.
+// isDying - whether the set is dying from where the emitter is located.
+//
+// Assumptions:
+// The emitter should be located on the first instruction from where is true that
+// the variable becoming valid (when isBorn is true) or invalid (when isDying is true).
+//
+// Notes:
+// This method is being called from treeLifeUpdater when a set of variables
+// is being born, becoming dead, or both.
+//
+void VariableLiveKeeper::siStartOrCloseVariableLiveRanges(VARSET_VALARG_TP varsIndexSet, bool isBorn, bool isDying)
+{
+ if (m_Compiler->opts.compDbgInfo)
+ {
+ VarSetOps::Iter iter(m_Compiler, varsIndexSet);
+ unsigned varIndex = 0;
+ while (iter.NextElem(&varIndex))
+ {
+ unsigned int varNum = m_Compiler->lvaTrackedToVarNum[varIndex];
+ const LclVarDsc* varDsc = m_Compiler->lvaGetDesc(varNum);
+ siStartOrCloseVariableLiveRange(varDsc, varNum, isBorn, isDying);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// siStartVariableLiveRange: Reports the given variable as being born.
+//
+// Arguments:
+// varDsc - the variable for which a location changed will be reported
+// varNum - the index of the variable to report home in lvLiveDsc
+//
+// Assumptions:
+// The emitter should be pointing to the first instruction from where the VariableLiveRange is
+// becoming valid.
+// The given "varDsc" should have its VariableRangeLists initialized.
+//
+// Notes:
+// This method should be called on every place a Variable is becoming alive.
+void VariableLiveKeeper::siStartVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum)
+{
+ noway_assert(varDsc != nullptr);
+
+ // Only the variables that exists in the IL, "this", and special arguments
+ // are reported.
+ if (m_Compiler->opts.compDbgInfo && varNum < m_LiveDscCount)
+ {
+ // Build siVarLoc for this born "varDsc"
+ CodeGenInterface::siVarLoc varLocation =
+ m_Compiler->codeGen->getSiVarLoc(varDsc, m_Compiler->codeGen->getCurrentStackLevel());
+
+ VariableLiveDescriptor* varLiveDsc = &m_vlrLiveDsc[varNum];
+ // this variable live range is valid from this point
+ varLiveDsc->startLiveRangeFromEmitter(varLocation, m_Compiler->getEmitter());
+ }
+}
+
+//------------------------------------------------------------------------
+// siEndVariableLiveRange: Reports the variable as becoming dead.
+//
+// Arguments:
+// varNum - the index of the variable at m_vlrLiveDsc or lvaTable in that
+// is becoming dead.
+//
+// Assumptions:
+// The given variable should be alive.
+// The emitter should be pointing to the first instruction from where the VariableLiveRange is
+// becoming invalid.
+//
+// Notes:
+// This method should be called on every place a Variable is becoming dead.
+void VariableLiveKeeper::siEndVariableLiveRange(unsigned int varNum)
+{
+ // Only the variables that exists in the IL, "this", and special arguments
+ // will be reported.
+
+ // This method is being called from genUpdateLife, and that one is called after
+ // code for BasicBlock have been generated, but the emitter has no longer
+ // a valid IG so we don't report the close of a "VariableLiveRange" after code is
+ // emitted.
+
+ if (m_Compiler->opts.compDbgInfo && varNum < m_LiveDscCount && !m_LastBasicBlockHasBeenEmited)
+ {
+ // this variable live range is no longer valid from this point
+ m_vlrLiveDsc[varNum].endLiveRangeAtEmitter(m_Compiler->getEmitter());
+ }
+}
+
+//------------------------------------------------------------------------
+// siUpdateVariableLiveRange: Reports the change of variable location for the
+// given variable.
+//
+// Arguments:
+// varDsc - the variable for which tis home has changed.
+// varNum - the index of the variable to report home in lvLiveDsc
+//
+// Assumptions:
+// The given variable should be alive.
+// The emitter should be pointing to the first instruction from where
+// the new variable location is becoming valid.
+//
+void VariableLiveKeeper::siUpdateVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum)
+{
+ noway_assert(varDsc != nullptr);
+
+ // Only the variables that exists in the IL, "this", and special arguments
+ // will be reported. This are locals and arguments, and are counted in
+ // "info.compLocalsCount".
+
+ // This method is being called when the prolog is being generated, and
+ // the emitter has no longer a valid IG so we don't report the close of
+ // a "VariableLiveRange" after code is emitted.
+ if (m_Compiler->opts.compDbgInfo && varNum < m_LiveDscCount && !m_LastBasicBlockHasBeenEmited)
+ {
+ // Build the location of the variable
+ CodeGenInterface::siVarLoc siVarLoc =
+ m_Compiler->codeGen->getSiVarLoc(varDsc, m_Compiler->codeGen->getCurrentStackLevel());
+
+ // Report the home change for this variable
+ VariableLiveDescriptor* varLiveDsc = &m_vlrLiveDsc[varNum];
+ varLiveDsc->updateLiveRangeAtEmitter(siVarLoc, m_Compiler->getEmitter());
+ }
+}
+
+//------------------------------------------------------------------------
+// siEndAllVariableLiveRange: Reports the set of variables as becoming dead.
+//
+// Arguments:
+// newLife - the set of variables that are becoming dead.
+//
+// Assumptions:
+// All the variables in the set are alive.
+//
+// Notes:
+// This method is called when the last block being generated to killed all
+// the live variables and set a flag to avoid reporting variable locations for
+// on next calls to method that update variable liveness.
+void VariableLiveKeeper::siEndAllVariableLiveRange(VARSET_VALARG_TP varsToClose)
+{
+ if (m_Compiler->opts.compDbgInfo)
+ {
+ if (m_Compiler->lvaTrackedCount > 0 || !m_Compiler->opts.OptimizationDisabled())
+ {
+ VarSetOps::Iter iter(m_Compiler, varsToClose);
+ unsigned varIndex = 0;
+ while (iter.NextElem(&varIndex))
+ {
+ unsigned int varNum = m_Compiler->lvaTrackedToVarNum[varIndex];
+ siEndVariableLiveRange(varNum);
+ }
+ }
+ else
+ {
+ // It seems we are jitting debug code, so we don't have variable
+ // liveness info
+ siEndAllVariableLiveRange();
+ }
+ }
+
+ m_LastBasicBlockHasBeenEmited = true;
+}
+
+//------------------------------------------------------------------------
+// siEndAllVariableLiveRange: Reports all live variables as dead.
+//
+// Notes:
+// This overload exists for the case we are jitting code compiled in
+// debug mode. When that happen we don't have variable liveness info
+// as "BaiscBlock::bbLiveIn" or "BaiscBlock::bbLiveOut" and there is no
+// tracked variable.
+//
+void VariableLiveKeeper::siEndAllVariableLiveRange()
+{
+ // TODO: we can improve this keeping a set for the variables with
+ // open VariableLiveRanges
+
+ for (unsigned int varNum = 0; varNum < m_LiveDscCount; varNum++)
+ {
+ const VariableLiveDescriptor* varLiveDsc = m_vlrLiveDsc + varNum;
+ if (varLiveDsc->hasVariableLiveRangeOpen())
+ {
+ siEndVariableLiveRange(varNum);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// getLiveRangesForVar: Return the "VariableLiveRange" that correspond to
+// the given "varNum".
+//
+// Arguments:
+// varNum - the index of the variable in m_vlrLiveDsc, which is the same as
+// in lvaTable.
+//
+// Return Value:
+// A const pointer to the list of variable locations reported for the variable.
+//
+// Assumtions:
+// This variable should be an argument, a special argument or an IL local
+// variable.
+VariableLiveKeeper::LiveRangeList* VariableLiveKeeper::getLiveRangesForVar(unsigned int varNum) const
+{
+ // There should be at least one variable for which its liveness is tracked
+ noway_assert(varNum < m_LiveDscCount);
+
+ return m_vlrLiveDsc[varNum].getLiveRanges();
+}
+
+//------------------------------------------------------------------------
+// getLiveRangesCount: Returns the count of variable locations reported for the tracked
+// variables, which are arguments, special arguments, and local IL variables.
+//
+// Return Value:
+// size_t - the count of variable locations
+//
+// Notes:
+// This method is being called from "genSetScopeInfo" to know the count of
+// "varResultInfo" that should be created on eeSetLVcount.
+//
+size_t VariableLiveKeeper::getLiveRangesCount() const
+{
+ size_t liveRangesCount = 0;
+
+ if (m_Compiler->opts.compDbgInfo)
+ {
+ for (unsigned int varNum = 0; varNum < m_LiveDscCount; varNum++)
+ {
+ VariableLiveDescriptor* varLiveDsc = m_vlrLiveDsc + varNum;
+
+ if (m_Compiler->compMap2ILvarNum(varNum) != (unsigned int)ICorDebugInfo::UNKNOWN_ILNUM)
+ {
+ liveRangesCount += varLiveDsc->getLiveRanges()->size();
+ }
+ }
+ }
+ return liveRangesCount;
+}
+
+//------------------------------------------------------------------------
+// psiStartVariableLiveRange: Reports the given variable as being born.
+//
+// Arguments:
+// varLcation - the variable location
+// varNum - the index of the variable in "compiler->lvaTable" or
+// "VariableLivekeeper->m_vlrLiveDsc"
+//
+// Notes:
+// This function is expected to be called from "psiBegProlog" during
+// prolog code generation.
+//
+void VariableLiveKeeper::psiStartVariableLiveRange(CodeGenInterface::siVarLoc varLocation, unsigned int varNum)
+{
+ // This descriptor has to correspond to a parameter. The first slots in lvaTable
+ // are arguments and special arguments.
+ noway_assert(varNum < m_LiveArgsCount);
+
+ VariableLiveDescriptor* varLiveDsc = &m_vlrLiveDsc[varNum];
+ varLiveDsc->startLiveRangeFromEmitter(varLocation, m_Compiler->getEmitter());
+}
+
+//------------------------------------------------------------------------
+// psiClosePrologVariableRanges: Report all the parameters as becoming dead.
+//
+// Notes:
+// This function is expected to be called from preffix "psiEndProlog" after
+// code for prolog has been generated.
+//
+void VariableLiveKeeper::psiClosePrologVariableRanges()
+{
+ noway_assert(m_LiveArgsCount <= m_LiveDscCount);
+
+ for (unsigned int varNum = 0; varNum < m_LiveArgsCount; varNum++)
+ {
+ VariableLiveDescriptor* varLiveDsc = m_vlrLiveDsc + varNum;
+
+ if (varLiveDsc->hasVariableLiveRangeOpen())
+ {
+ varLiveDsc->endLiveRangeAtEmitter(m_Compiler->getEmitter());
+ }
+ }
+}
+
+#ifdef DEBUG
+void VariableLiveKeeper::dumpBlockVariableLiveRanges(const BasicBlock* block)
+{
+ // "block" will be dereferenced
+ noway_assert(block != nullptr);
+
+ bool hasDumpedHistory = false;
+
+ if (m_Compiler->verbose)
+ {
+ printf("////////////////////////////////////////\n");
+ printf("////////////////////////////////////////\n");
+ printf("Variable Live Range History Dump for Block %d \n", block->bbNum);
+
+ if (m_Compiler->opts.compDbgInfo)
+ {
+ for (unsigned int varNum = 0; varNum < m_LiveDscCount; varNum++)
+ {
+ VariableLiveDescriptor* varLiveDsc = m_vlrLiveDsc + varNum;
+
+ if (varLiveDsc->hasVarLiverRangesFromLastBlockToDump())
+ {
+ hasDumpedHistory = true;
+ printf("IL Var Num %d:\n", m_Compiler->compMap2ILvarNum(varNum));
+ varLiveDsc->dumpRegisterLiveRangesForBlockBeforeCodeGenerated(m_Compiler->codeGen);
+ varLiveDsc->endBlockLiveRanges();
+ }
+ }
+ }
+
+ if (!hasDumpedHistory)
+ {
+ printf("..None..\n");
+ }
+
+ printf("////////////////////////////////////////\n");
+ printf("////////////////////////////////////////\n");
+ printf("End Generating code for Block %d \n", block->bbNum);
+ }
+}
+
+void VariableLiveKeeper::dumpLvaVariableLiveRanges() const
+{
+ bool hasDumpedHistory = false;
+
+ if (m_Compiler->verbose)
+ {
+ printf("////////////////////////////////////////\n");
+ printf("////////////////////////////////////////\n");
+ printf("PRINTING VARIABLE LIVE RANGES:\n");
+
+ if (m_Compiler->opts.compDbgInfo)
+ {
+ for (unsigned int varNum = 0; varNum < m_LiveDscCount; varNum++)
+ {
+ VariableLiveDescriptor* varLiveDsc = m_vlrLiveDsc + varNum;
+
+ if (varLiveDsc->hasVarLiveRangesToDump())
+ {
+ hasDumpedHistory = true;
+ printf("IL Var Num %d:\n", m_Compiler->compMap2ILvarNum(varNum));
+ varLiveDsc->dumpAllRegisterLiveRangesForBlock(m_Compiler->getEmitter(), m_Compiler->codeGen);
+ }
+ }
+ }
+
+ if (!hasDumpedHistory)
+ {
+ printf("..None..\n");
+ }
+
+ printf("////////////////////////////////////////\n");
+ printf("////////////////////////////////////////\n");
+ }
+}
+#endif // DEBUG
+#endif // USING_VARIABLE_LIVE_RANGE
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index 87581856dd..80b2e550f6 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -297,6 +297,191 @@ enum RefCountState
RCS_NORMAL, // normal ref counts (from lvaMarkRefs onward)
};
+#ifdef USING_VARIABLE_LIVE_RANGE
+//--------------------------------------------
+//
+// VariableLiveKeeper: Holds an array of "VariableLiveDescriptor", one for each variable
+// whose location we track. It provides start/end/update/count operations over the
+// "LiveRangeList" of any variable.
+//
+// Notes:
+// This method could be implemented on Compiler class too, but the intention is to move code
+// out of that class, which is huge. With this solution the only code needed in Compiler is
+// a getter and an initializer of this class.
+// The index of each variable in this array corresponds to the one in "compiler->lvaTable".
+// We care about tracking the variable locations of arguments, special arguments, and local IL
+// variables, and we ignore any other variable (like JIT temporary variables).
+//
+class VariableLiveKeeper
+{
+public:
+ //--------------------------------------------
+ //
+ // VariableLiveRange: Represent part of the life of a variable. A
+ // variable lives in a location (represented with struct "siVarLoc")
+ // between two native offsets.
+ //
+ // Notes:
+ // We use emitLocation and not NATTIVE_OFFSET because location
+ // is captured when code is being generated (genCodeForBBList
+ // and genGeneratePrologsAndEpilogs) but only after the whole
+ // method's code is generated can we obtain a final, fixed
+ // NATIVE_OFFSET representing the actual generated code offset.
+ // There is also a IL_OFFSET, but this is more accurate and the
+ // debugger is expecting assembly offsets.
+ // This class doesn't have behaviour attached to itself, it is
+ // just putting a name to a representation. It is used to build
+ // typedefs LiveRangeList and LiveRangeListIterator, which are
+ // basically a list of this class and a const_iterator of that
+ // list.
+ //
+ class VariableLiveRange
+ {
+ public:
+ emitLocation m_StartEmitLocation; // first position from where "m_VarLocation" becomes valid
+ emitLocation m_EndEmitLocation; // last position where "m_VarLocation" is valid
+ CodeGenInterface::siVarLoc m_VarLocation; // variable location
+
+ VariableLiveRange(CodeGenInterface::siVarLoc varLocation,
+ emitLocation startEmitLocation,
+ emitLocation endEmitLocation)
+ : m_StartEmitLocation(startEmitLocation), m_EndEmitLocation(endEmitLocation), m_VarLocation(varLocation)
+ {
+ }
+
+#ifdef DEBUG
+ // Dump "VariableLiveRange" when code has not been generated. We don't have the native code offset,
+ // but we do have "emitLocation"s and "siVarLoc".
+ void dumpVariableLiveRange(const CodeGenInterface* codeGen) const;
+
+ // Dump "VariableLiveRange" when code has been generated and we have the native code offset of each
+ // "emitLocation"
+ void dumpVariableLiveRange(emitter* emit, const CodeGenInterface* codeGen) const;
+#endif // DEBUG
+ };
+
+ typedef jitstd::list<VariableLiveRange> LiveRangeList;
+ typedef LiveRangeList::const_iterator LiveRangeListIterator;
+
+private:
+#ifdef DEBUG
+ //--------------------------------------------
+ //
+ // LiveRangeDumper: Used for debugging purposes during code
+ // generation on genCodeForBBList. Keeps an iterator to the first
+ // edited/added "VariableLiveRange" of a variable during the
+ // generation of code of one block.
+ //
+ // Notes:
+ // The first "VariableLiveRange" reported for a variable during
+ // a BasicBlock is sent to "setDumperStartAt" so we can dump all
+ // the "VariableLiveRange"s from that one.
+ // After we dump all the "VariableLiveRange"s we call "reset" with
+ // the "liveRangeList" to set the barrier to nullptr or the last
+ // "VariableLiveRange" if it is opened.
+ // If no "VariableLiveRange" was edited/added during block,
+ // the iterator points to the end of variable's LiveRangeList.
+ //
+ class LiveRangeDumper
+ {
+ // Iterator to the first edited/added position during actual block code generation. If last
+ // block had a closed "VariableLiveRange" (with a valid "m_EndEmitLocation") and not changes
+ // were applied to variable liveness, it points to the end of variable's LiveRangeList.
+ LiveRangeListIterator m_StartingLiveRange;
+ bool m_hasLiveRangestoDump; // True if a live range for this variable has been
+ // reported from last call to EndBlock
+
+ public:
+ LiveRangeDumper(const LiveRangeList* liveRanges)
+ : m_StartingLiveRange(liveRanges->end()), m_hasLiveRangestoDump(false){};
+
+ // Make the dumper point to the last "VariableLiveRange" opened or nullptr if all are closed
+ void resetDumper(const LiveRangeList* list);
+
+ // Make "LiveRangeDumper" instance points the last "VariableLiveRange" added so we can
+ // start dumping from there after the actual "BasicBlock"s code is generated.
+ void setDumperStartAt(const LiveRangeListIterator liveRangeIt);
+
+ // Return an iterator to the first "VariableLiveRange" edited/added during the current
+ // "BasicBlock"
+ LiveRangeListIterator getStartForDump() const;
+
+ // Return whether at least a "VariableLiveRange" was alive during the current "BasicBlock"'s
+ // code generation
+ bool hasLiveRangesToDump() const;
+ };
+#endif // DEBUG
+
+ //--------------------------------------------
+ //
+ // VariableLiveDescriptor: This class persist and update all the changes
+ // to the home of a variable. It has an instance of "LiveRangeList"
+ // and methods to report the start/end of a VariableLiveRange.
+ //
+ class VariableLiveDescriptor
+ {
+ LiveRangeList* m_VariableLiveRanges; // the variable locations of this variable
+ INDEBUG(LiveRangeDumper* m_VariableLifeBarrier);
+
+ public:
+ VariableLiveDescriptor(CompAllocator allocator);
+
+ bool hasVariableLiveRangeOpen() const;
+ LiveRangeList* getLiveRanges() const;
+
+ void startLiveRangeFromEmitter(CodeGenInterface::siVarLoc varLocation, emitter* emit) const;
+ void endLiveRangeAtEmitter(emitter* emit) const;
+ void updateLiveRangeAtEmitter(CodeGenInterface::siVarLoc varLocation, emitter* emit) const;
+
+#ifdef DEBUG
+ void dumpAllRegisterLiveRangesForBlock(emitter* emit, const CodeGenInterface* codeGen) const;
+ void dumpRegisterLiveRangesForBlockBeforeCodeGenerated(const CodeGenInterface* codeGen) const;
+ bool hasVarLiveRangesToDump() const;
+ bool hasVarLiverRangesFromLastBlockToDump() const;
+ void endBlockLiveRanges();
+#endif // DEBUG
+ };
+
+ unsigned int m_LiveDscCount; // count of args, special args, and IL local variables to report home
+ unsigned int m_LiveArgsCount; // count of arguments to report home
+
+ Compiler* m_Compiler;
+
+ VariableLiveDescriptor* m_vlrLiveDsc; // Array of descriptors that manage VariableLiveRanges.
+ // Its indices correspond to lvaTable indexes (or lvSlotNum).
+
+ bool m_LastBasicBlockHasBeenEmited; // When true no more siEndVariableLiveRange is considered.
+ // No update/start happens when code has been generated.
+
+public:
+ VariableLiveKeeper(unsigned int totalLocalCount,
+ unsigned int argsCount,
+ Compiler* compiler,
+ CompAllocator allocator);
+
+ // For tracking locations during code generation
+ void siStartOrCloseVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum, bool isBorn, bool isDying);
+ void siStartOrCloseVariableLiveRanges(VARSET_VALARG_TP varsIndexSet, bool isBorn, bool isDying);
+ void siStartVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum);
+ void siEndVariableLiveRange(unsigned int varNum);
+ void siUpdateVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum);
+ void siEndAllVariableLiveRange(VARSET_VALARG_TP varsToClose);
+ void siEndAllVariableLiveRange();
+
+ LiveRangeList* getLiveRangesForVar(unsigned int varNum) const;
+ size_t getLiveRangesCount() const;
+
+ // For parameters locations on prolog
+ void psiStartVariableLiveRange(CodeGenInterface::siVarLoc varLocation, unsigned int varNum);
+ void psiClosePrologVariableRanges();
+
+#ifdef DEBUG
+ void dumpBlockVariableLiveRanges(const BasicBlock* block);
+ void dumpLvaVariableLiveRanges() const;
+#endif // DEBUG
+};
+#endif // USING_VARIABLE_LIVE_RANGE
+
class LclVarDsc
{
public:
@@ -1895,6 +2080,14 @@ class Compiler
*/
public:
+#ifdef USING_VARIABLE_LIVE_RANGE
+ VariableLiveKeeper* varLiveKeeper; // Used to manage VariableLiveRanges of variables
+
+ void initializeVariableLiveKeeper();
+
+ VariableLiveKeeper* getVariableLiveKeeper() const;
+#endif // USING_VARIABLE_LIVE_RANGE
+
hashBvGlobalData hbvGlobalData; // Used by the hashBv bitvector package.
#ifdef DEBUG
@@ -6966,10 +7159,7 @@ public:
UNATIVE_OFFSET startOffs,
UNATIVE_OFFSET length,
unsigned varNum,
- unsigned LVnum,
- VarName namex,
- bool avail,
- const CodeGenInterface::siVarLoc* loc);
+ const CodeGenInterface::siVarLoc& loc);
void eeSetLVdone();
#ifdef DEBUG
diff --git a/src/jit/compmemkind.h b/src/jit/compmemkind.h
index 582a2a9e3a..244979872e 100644
--- a/src/jit/compmemkind.h
+++ b/src/jit/compmemkind.h
@@ -54,6 +54,7 @@ CompMemKindMacro(RangeCheck)
CompMemKindMacro(CopyProp)
CompMemKindMacro(SideEffects)
CompMemKindMacro(ObjectAllocator)
+CompMemKindMacro(VariableLiveRanges)
//clang-format on
#undef CompMemKindMacro
diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp
index ca3fce2aad..c5d499f3e0 100644
--- a/src/jit/ee_il_dll.cpp
+++ b/src/jit/ee_il_dll.cpp
@@ -652,10 +652,7 @@ void Compiler::eeSetLVinfo(unsigned which,
UNATIVE_OFFSET startOffs,
UNATIVE_OFFSET length,
unsigned varNum,
- unsigned LVnum,
- VarName name,
- bool avail,
- const CodeGenInterface::siVarLoc* varLoc)
+ const CodeGenInterface::siVarLoc& varLoc)
{
// ICorDebugInfo::VarLoc and CodeGenInterface::siVarLoc have to overlap
// This is checked in siInit()
@@ -669,7 +666,7 @@ void Compiler::eeSetLVinfo(unsigned which,
eeVars[which].startOffset = startOffs;
eeVars[which].endOffset = startOffs + length;
eeVars[which].varNumber = varNum;
- eeVars[which].loc = *varLoc;
+ eeVars[which].loc = varLoc;
}
}
@@ -917,7 +914,7 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var)
void Compiler::eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars)
{
printf("*************** Variable debug info\n");
- printf("%d vars\n", cVars);
+ printf("%d live ranges\n", cVars);
for (unsigned i = 0; i < cVars; i++)
{
eeDispVar(&vars[i]);
diff --git a/src/jit/jitstd/list.h b/src/jit/jitstd/list.h
index eaeb979190..a932df2dd5 100644
--- a/src/jit/jitstd/list.h
+++ b/src/jit/jitstd/list.h
@@ -52,8 +52,8 @@ public:
{
private:
const_iterator(Node* ptr);
- const_iterator();
public:
+ const_iterator();
const_iterator(const const_iterator& it);
const_iterator(const typename list<T, Allocator>::iterator& it);
diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp
index 053c425514..e46ac751ea 100644
--- a/src/jit/lsra.cpp
+++ b/src/jit/lsra.cpp
@@ -1256,6 +1256,15 @@ void LinearScan::recordVarLocationsAtStartOfBB(BasicBlock* bb)
compiler->compRegVarName(newRegNum));
varDsc->lvRegNum = newRegNum;
count++;
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ if (bb->bbPrev != nullptr && VarSetOps::IsMember(compiler, bb->bbPrev->bbLiveOut, varIndex))
+ {
+ // varDsc was alive on previous block end ("bb->bbPrev->bbLiveOut"), so it has an open
+ // "VariableLiveRange" which should change to be according "getInVarToRegMap"
+ compiler->getVariableLiveKeeper()->siUpdateVariableLiveRange(varDsc, varNum);
+ }
+#endif // USING_VARIABLE_LIVE_RANGE
}
else if (newRegNum != REG_STK)
{
diff --git a/src/jit/scopeinfo.cpp b/src/jit/scopeinfo.cpp
index 30189c82d2..be6fb8ce6c 100644
--- a/src/jit/scopeinfo.cpp
+++ b/src/jit/scopeinfo.cpp
@@ -82,7 +82,7 @@ bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg) const
}
}
-bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset) const
+bool CodeGenInterface::siVarLoc::vlIsOnStack(regNumber reg, signed offset) const
{
regNumber actualReg;
@@ -130,6 +130,61 @@ bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset) const
}
}
+bool CodeGenInterface::siVarLoc::vlIsOnStack() const
+{
+ switch (vlType)
+ {
+ case CodeGenInterface::VLT_STK:
+ case CodeGenInterface::VLT_STK2:
+ case CodeGenInterface::VLT_FPSTK:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+//------------------------------------------------------------------------
+// storeVariableInRegisters: Convert the siVarLoc instance in a regsiter
+// location using the given registers.
+//
+// Arguments:
+// reg - the first register where the variable is placed.
+// otherReg - the second register where the variable is placed
+// or REG_NA if does not apply.
+//
+void CodeGenInterface::siVarLoc::storeVariableInRegisters(regNumber reg, regNumber otherReg)
+{
+ if (otherReg == REG_NA)
+ {
+ // Only one register is used
+ vlType = VLT_REG;
+ vlReg.vlrReg = reg;
+ }
+ else
+ {
+ // Two register are used
+ vlType = VLT_REG_REG;
+ vlRegReg.vlrrReg1 = reg;
+ vlRegReg.vlrrReg2 = otherReg;
+ }
+}
+
+//------------------------------------------------------------------------
+// storeVariableOnStack: Convert the siVarLoc instance in a stack location
+// with the given base register and stack offset.
+//
+// Arguments:
+// stackBaseReg - the base of the stack.
+// varStackOffset - the offset from the base where the variable is placed.
+//
+void CodeGenInterface::siVarLoc::storeVariableOnStack(regNumber stackBaseReg, NATIVE_OFFSET varStackOffset)
+{
+ vlType = VLT_STK;
+ vlStk.vlsBaseReg = stackBaseReg;
+ vlStk.vlsOffset = varStackOffset;
+}
+
//------------------------------------------------------------------------
// Equals: Compares first reference and then values of the structures.
//
@@ -412,6 +467,120 @@ CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg,
}
}
+//------------------------------------------------------------------------
+// getSiVarLoc: Returns a "siVarLoc" instance representing the variable location.
+//
+// Arguments:
+// varDsc - the variable it is desired to build the "siVarLoc".
+// stackLevel - the current stack level. If the stack pointer changes in
+// the function, we must adjust stack pointer-based local
+// variable offsets to compensate.
+//
+// Return Value:
+// A "siVarLoc" representing the variable location, which could live
+// in a register, an stack position, or a combination of both.
+//
+CodeGenInterface::siVarLoc CodeGenInterface::getSiVarLoc(const LclVarDsc* varDsc, unsigned int stackLevel) const
+{
+ // For stack vars, find the base register, and offset
+
+ regNumber baseReg;
+ signed offset = varDsc->lvStkOffs;
+
+ if (!varDsc->lvFramePointerBased)
+ {
+ baseReg = REG_SPBASE;
+ offset += stackLevel;
+ }
+ else
+ {
+ baseReg = REG_FPBASE;
+ }
+
+ return CodeGenInterface::siVarLoc(varDsc, baseReg, offset, isFramePointerUsed());
+}
+
+#ifdef DEBUG
+void CodeGenInterface::dumpSiVarLoc(const siVarLoc* varLoc) const
+{
+ // "varLoc" cannot be null
+ noway_assert(varLoc != nullptr);
+
+ switch (varLoc->vlType)
+ {
+ case VLT_REG:
+ case VLT_REG_BYREF:
+ case VLT_REG_FP:
+ printf("%s", getRegName(varLoc->vlReg.vlrReg));
+ if (varLoc->vlType == VLT_REG_BYREF)
+ {
+ printf(" byref");
+ }
+ break;
+
+ case VLT_STK:
+ case VLT_STK_BYREF:
+ if ((int)varLoc->vlStk.vlsBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
+ {
+ printf("%s[%d] (1 slot)", getRegName(varLoc->vlStk.vlsBaseReg), varLoc->vlStk.vlsOffset);
+ }
+ else
+ {
+ printf(STR_SPBASE "'[%d] (1 slot)", varLoc->vlStk.vlsOffset);
+ }
+ if (varLoc->vlType == VLT_REG_BYREF)
+ {
+ printf(" byref");
+ }
+ break;
+
+#ifndef _TARGET_AMD64_
+ case VLT_REG_REG:
+ printf("%s-%s", getRegName(varLoc->vlRegReg.vlrrReg1), getRegName(varLoc->vlRegReg.vlrrReg2));
+ break;
+
+ case VLT_REG_STK:
+ if ((int)varLoc->vlRegStk.vlrsStk.vlrssBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
+ {
+ printf("%s-%s[%d]", getRegName(varLoc->vlRegStk.vlrsReg),
+ getRegName(varLoc->vlRegStk.vlrsStk.vlrssBaseReg), varLoc->vlRegStk.vlrsStk.vlrssOffset);
+ }
+ else
+ {
+ printf("%s-" STR_SPBASE "'[%d]", getRegName(varLoc->vlRegStk.vlrsReg),
+ varLoc->vlRegStk.vlrsStk.vlrssOffset);
+ }
+ break;
+
+ case VLT_STK_REG:
+ unreached();
+
+ case VLT_STK2:
+ if ((int)varLoc->vlStk2.vls2BaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
+ {
+ printf("%s[%d] (2 slots)", getRegName(varLoc->vlStk2.vls2BaseReg), varLoc->vlStk2.vls2Offset);
+ }
+ else
+ {
+ printf(STR_SPBASE "'[%d] (2 slots)", varLoc->vlStk2.vls2Offset);
+ }
+ break;
+
+ case VLT_FPSTK:
+ printf("ST(L-%d)", varLoc->vlFPstk.vlfReg);
+ break;
+
+ case VLT_FIXED_VA:
+ printf("fxd_va[%d]", varLoc->vlFixedVarArg.vlfvOffset);
+ break;
+#endif // !_TARGET_AMD64_
+
+ default:
+ unreached();
+ }
+}
+#endif
+
#ifdef USING_SCOPE_INFO
//------------------------------------------------------------------------
// getSiVarLoc: Returns a "siVarLoc" instance representing the place where the variable
@@ -671,14 +840,17 @@ bool CodeGen::siVerifyLocalVarTab()
return true;
}
-#endif
+#endif // DEBUG
+#endif // USING_SCOPE_INFO
/*============================================================================
* INTERFACE (public) Functions for ScopeInfo
*============================================================================
*/
-void CodeGen::siInit()
+// Check every CodeGenInterface::siVarLocType and CodeGenInterface::siVarLoc
+// are what ICodeDebugInfo is expetecting.
+void CodeGen::checkICodeDebugInfo()
{
#ifdef _TARGET_X86_
assert((unsigned)ICorDebugInfo::REGNUM_EAX == REG_EAX);
@@ -708,9 +880,24 @@ void CodeGen::siInit()
*/
assert(sizeof(ICorDebugInfo::VarLoc) == sizeof(CodeGenInterface::siVarLoc));
+}
+
+void CodeGen::siInit()
+{
+ checkICodeDebugInfo();
assert(compiler->opts.compScopeInfo);
+#if FEATURE_EH_FUNCLETS
+ if (compiler->info.compVarScopesCount > 0)
+ {
+ siInFuncletRegion = false;
+ }
+#endif // FEATURE_EH_FUNCLETS
+
+ siLastEndOffs = 0;
+
+#ifdef USING_SCOPE_INFO
siOpenScopeList.scNext = nullptr;
siOpenScopeLast = &siOpenScopeList;
siScopeLast = &siScopeList;
@@ -718,7 +905,6 @@ void CodeGen::siInit()
siScopeCnt = 0;
VarSetOps::AssignNoCopy(compiler, siLastLife, VarSetOps::MakeEmpty(compiler));
- siLastEndOffs = 0;
if (compiler->info.compVarScopesCount == 0)
{
@@ -726,10 +912,6 @@ void CodeGen::siInit()
}
else
{
-#if FEATURE_EH_FUNCLETS
- siInFuncletRegion = false;
-#endif // FEATURE_EH_FUNCLETS
-
unsigned scopeCount = compiler->lvaTrackedCount;
if (scopeCount == 0)
@@ -743,6 +925,7 @@ void CodeGen::siInit()
compiler->compResetScopeLists();
}
+#endif // USING_SCOPE_INFO
}
/*****************************************************************************
@@ -807,6 +990,7 @@ void CodeGen::siBeginBlock(BasicBlock* block)
// locals, untracked locals will fail to be reported.
if (compiler->lvaTrackedCount > 0)
{
+#ifdef USING_SCOPE_INFO
// End scope of variables which are not live for this block
siUpdate();
@@ -826,98 +1010,128 @@ void CodeGen::siBeginBlock(BasicBlock* block)
siCheckVarScope(varNum, beginOffs);
}
+#endif
}
else
{
- // There aren't any tracked locals.
- //
- // For debuggable or minopts code, scopes can begin only on block boundaries.
- // For other codegen modes (eg minopts/tier0) we currently won't report any
- // untracked locals.
- if (compiler->opts.OptimizationDisabled())
- {
- // Check if there are any scopes on the current block's start boundary.
- VarScopeDsc* varScope = nullptr;
+ siOpenScopesForNonTrackedVars(block, siLastEndOffs);
+ }
+
+#if defined(USING_SCOPE_INFO) && defined(DEBUG)
+ if (verbose)
+ {
+ siDispOpenScopes();
+ }
+#endif // defined(USING_SCOPE_INFO) && defined(DEBUG)
+}
+
+//------------------------------------------------------------------------
+// siOpenScopesForNonTrackedVars: If optimizations are disable, it will open
+// a "siScope" for each variable which has a "VarScopeDsc" (input of the JIT)
+// and is referenced at least once. If optimizations are applied, nothing is done.
+//
+// Arguments:
+// block - the block whose code is going to be generated.
+// lastBlockILEndOffset - the IL offset at the ending of the last generated basic block.
+//
+// Notes:
+// When there we are jitting methods compiled in debug mode, no variable is
+// tracked and there is no info that shows variable liveness like block->bbLiveIn.
+// On debug code variables are not enregistered the whole method so we can just
+// report them as beign born from here on the stack until the whole method is
+// generated.
+//
+void CodeGen::siOpenScopesForNonTrackedVars(const BasicBlock* block, unsigned int lastBlockILEndOffset)
+{
+ unsigned int beginOffs = block->bbCodeOffs;
+
+ // There aren't any tracked locals.
+ //
+ // For debuggable or minopts code, scopes can begin only on block boundaries.
+ // For other codegen modes (eg minopts/tier0) we currently won't report any
+ // untracked locals.
+ if (compiler->opts.OptimizationDisabled())
+ {
+ // Check if there are any scopes on the current block's start boundary.
+ VarScopeDsc* varScope = nullptr;
#if FEATURE_EH_FUNCLETS
- // If we find a spot where the code offset isn't what we expect, because
- // there is a gap, it might be because we've moved the funclets out of
- // line. Catch up with the enter and exit scopes of the current block.
- // Ignore the enter/exit scope changes of the missing scopes, which for
- // funclets must be matched.
- if (siLastEndOffs != beginOffs)
- {
- assert(beginOffs > 0);
- assert(siLastEndOffs < beginOffs);
+ // If we find a spot where the code offset isn't what we expect, because
+ // there is a gap, it might be because we've moved the funclets out of
+ // line. Catch up with the enter and exit scopes of the current block.
+ // Ignore the enter/exit scope changes of the missing scopes, which for
+ // funclets must be matched.
+ if (lastBlockILEndOffset != beginOffs)
+ {
+ assert(beginOffs > 0);
+ assert(lastBlockILEndOffset < beginOffs);
- JITDUMP("Scope info: found offset hole. lastOffs=%u, currOffs=%u\n", siLastEndOffs, beginOffs);
+ JITDUMP("Scope info: found offset hole. lastOffs=%u, currOffs=%u\n", lastBlockILEndOffset, beginOffs);
- // Skip enter scopes
- while ((varScope = compiler->compGetNextEnterScope(beginOffs - 1, true)) != nullptr)
- {
- /* do nothing */
- JITDUMP("Scope info: skipping enter scope, LVnum=%u\n", varScope->vsdLVnum);
- }
+ // Skip enter scopes
+ while ((varScope = compiler->compGetNextEnterScope(beginOffs - 1, true)) != nullptr)
+ {
+ /* do nothing */
+ JITDUMP("Scope info: skipping enter scope, LVnum=%u\n", varScope->vsdLVnum);
+ }
- // Skip exit scopes
- while ((varScope = compiler->compGetNextExitScope(beginOffs - 1, true)) != nullptr)
- {
- /* do nothing */
- JITDUMP("Scope info: skipping exit scope, LVnum=%u\n", varScope->vsdLVnum);
- }
+ // Skip exit scopes
+ while ((varScope = compiler->compGetNextExitScope(beginOffs - 1, true)) != nullptr)
+ {
+ /* do nothing */
+ JITDUMP("Scope info: skipping exit scope, LVnum=%u\n", varScope->vsdLVnum);
}
+ }
#else // FEATURE_EH_FUNCLETS
- if (siLastEndOffs != beginOffs)
- {
- assert(siLastEndOffs < beginOffs);
- return;
- }
+ if (lastBlockILEndOffset != beginOffs)
+ {
+ assert(lastBlockILEndOffset < beginOffs);
+ return;
+ }
#endif // FEATURE_EH_FUNCLETS
- while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != nullptr)
- {
- LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
+ while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != nullptr)
+ {
+ LclVarDsc* lclVarDsc = &compiler->lvaTable[varScope->vsdVarNum];
- // Only report locals that were referenced, if we're not doing debug codegen
- if (compiler->opts.compDbgCode || (lclVarDsc1->lvRefCnt() > 0))
- {
- // brace-matching editor workaround for following line: (
- JITDUMP("Scope info: opening scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum,
- varScope->vsdLifeBeg, varScope->vsdLifeEnd);
+ // Only report locals that were referenced, if we're not doing debug codegen
+ if (compiler->opts.compDbgCode || (lclVarDsc->lvRefCnt() > 0))
+ {
+ // brace-matching editor workaround for following line: (
+ JITDUMP("Scope info: opening scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg,
+ varScope->vsdLifeEnd);
- siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
+#ifdef USING_SCOPE_INFO
+ siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
+#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ compiler->getVariableLiveKeeper()->siStartVariableLiveRange(lclVarDsc, varScope->vsdVarNum);
+#endif // USING_VARIABLE_LIVE_RANGE
-#ifdef DEBUG
- if (VERBOSE)
- {
- printf("Scope info: >> new scope, VarNum=%u, tracked? %s, VarIndex=%u, bbLiveIn=%s ",
- varScope->vsdVarNum, lclVarDsc1->lvTracked ? "yes" : "no", lclVarDsc1->lvVarIndex,
- VarSetOps::ToString(compiler, block->bbLiveIn));
- dumpConvertedVarSet(compiler, block->bbLiveIn);
- printf("\n");
- }
- assert(!lclVarDsc1->lvTracked ||
- VarSetOps::IsMember(compiler, block->bbLiveIn, lclVarDsc1->lvVarIndex));
-#endif // DEBUG
- }
- else
+#if defined(DEBUG) && defined(USING_SCOPE_INFO)
+ if (VERBOSE)
{
- JITDUMP("Skipping open scope for V%02u, unreferenced\n", varScope->vsdVarNum);
+ printf("Scope info: >> new scope, VarNum=%u, tracked? %s, VarIndex=%u, bbLiveIn=%s ",
+ varScope->vsdVarNum, lclVarDsc->lvTracked ? "yes" : "no", lclVarDsc->lvVarIndex,
+ VarSetOps::ToString(compiler, block->bbLiveIn));
+ dumpConvertedVarSet(compiler, block->bbLiveIn);
+ printf("\n");
}
+#endif // defined(DEBUG) && defined(USING_SCOPE_INFO)
+
+ INDEBUG(assert(!lclVarDsc->lvTracked ||
+ VarSetOps::IsMember(compiler, block->bbLiveIn, lclVarDsc->lvVarIndex)));
+ }
+ else
+ {
+ JITDUMP("Skipping open scope for V%02u, unreferenced\n", varScope->vsdVarNum);
}
}
}
-
-#ifdef DEBUG
- if (verbose)
- {
- siDispOpenScopes();
- }
-#endif
}
/*****************************************************************************
@@ -939,14 +1153,14 @@ void CodeGen::siEndBlock(BasicBlock* block)
}
#endif // FEATURE_EH_FUNCLETS
-#ifdef DEBUG
+#if defined(USING_SCOPE_INFO) && defined(DEBUG)
if (verbose)
{
printf("\nScope info: end block " FMT_BB ", IL range ", block->bbNum);
block->dspBlockILRange();
printf("\n");
}
-#endif // DEBUG
+#endif // defined(USING_SCOPE_INFO) && defined(DEBUG)
unsigned endOffs = block->bbCodeOffsEnd;
@@ -956,6 +1170,7 @@ void CodeGen::siEndBlock(BasicBlock* block)
return;
}
+#ifdef USING_SCOPE_INFO
// If non-debuggable code, find all scopes which end over this block
// and close them. For debuggable code, scopes will only end on block
// boundaries.
@@ -981,17 +1196,19 @@ void CodeGen::siEndBlock(BasicBlock* block)
siEndScope(varNum);
}
}
+#endif // USING_SCOPE_INFO
siLastEndOffs = endOffs;
-#ifdef DEBUG
+#if defined(USING_SCOPE_INFO) && defined(DEBUG)
if (verbose)
{
siDispOpenScopes();
}
-#endif
+#endif // defined(USING_SCOPE_INFO) && defined(DEBUG)
}
+#ifdef USING_SCOPE_INFO
//------------------------------------------------------------------------
// siUpdate: Closes the "ScopeInfo" of the tracked variables that has become dead.
//
@@ -1244,34 +1461,49 @@ void CodeGen::psiEndPrologScope(psiScope* scope)
*/
//------------------------------------------------------------------------
-// psSetScopeOffset: Set the offset of the newScope to the offset of the LslVar
+// psiSetScopeOffset: Set the offset of the newScope to the offset of the LslVar
//
// Arguments:
// 'newScope' the new scope object whose offset is to be set to the lclVarDsc offset.
// 'lclVarDsc' is an op that will now be contained by its parent.
-//
-//
-void CodeGen::psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc)
+void CodeGen::psiSetScopeOffset(psiScope* newScope, const LclVarDsc* lclVarDsc) const
{
newScope->scRegister = false;
newScope->u2.scBaseReg = REG_SPBASE;
+ newScope->u2.scOffset = psiGetVarStackOffset(lclVarDsc);
+}
+#endif // USING_SCOPE_INFO
+
+//------------------------------------------------------------------------
+// psiGetVarStackOffset: Return the offset of the lclVarDsc on the stack.
+//
+// Arguments:
+// lclVarDsc - the LclVarDsc from whom the offset is asked.
+//
+NATIVE_OFFSET CodeGen::psiGetVarStackOffset(const LclVarDsc* lclVarDsc) const
+{
+ noway_assert(lclVarDsc != nullptr);
+
+ NATIVE_OFFSET stackOffset = 0;
#ifdef _TARGET_AMD64_
// scOffset = offset from caller SP - REGSIZE_BYTES
// TODO-Cleanup - scOffset needs to be understood. For now just matching with the existing definition.
- newScope->u2.scOffset =
+ stackOffset =
compiler->lvaToCallerSPRelativeOffset(lclVarDsc->lvStkOffs, lclVarDsc->lvFramePointerBased) + REGSIZE_BYTES;
#else // !_TARGET_AMD64_
if (doubleAlignOrFramePointerUsed())
{
// REGSIZE_BYTES - for the pushed value of EBP
- newScope->u2.scOffset = lclVarDsc->lvStkOffs - REGSIZE_BYTES;
+ stackOffset = lclVarDsc->lvStkOffs - REGSIZE_BYTES;
}
else
{
- newScope->u2.scOffset = lclVarDsc->lvStkOffs - genTotalFrameSize();
+ stackOffset = lclVarDsc->lvStkOffs - genTotalFrameSize();
}
#endif // !_TARGET_AMD64_
+
+ return stackOffset;
}
/*============================================================================
@@ -1279,45 +1511,48 @@ void CodeGen::psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc)
*============================================================================
*/
-/*****************************************************************************
- * psiBegProlog
- *
- * Initializes the PrologScopeInfo, and creates open scopes for all the
- * parameters of the method.
- */
-
+//------------------------------------------------------------------------
+// psiBegProlog: Initializes the PrologScopeInfo creating open psiScopes or
+// VariableLiveRanges for all the parameters of the method depending on which
+// flag is being used.
+//
void CodeGen::psiBegProlog()
{
assert(compiler->compGeneratingProlog);
- VarScopeDsc* varScope;
-
+#ifdef USING_SCOPE_INFO
psiOpenScopeList.scNext = nullptr;
psiOpenScopeLast = &psiOpenScopeList;
psiScopeLast = &psiScopeList;
psiScopeCnt = 0;
+#endif // USING_SCOPE_INFO
compiler->compResetScopeLists();
+ VarScopeDsc* varScope;
while ((varScope = compiler->compGetNextEnterScope(0)) != nullptr)
{
- LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
+ LclVarDsc* lclVarDsc = &compiler->lvaTable[varScope->vsdVarNum];
- if (!lclVarDsc1->lvIsParam)
+ if (!lclVarDsc->lvIsParam)
{
continue;
}
-
+#ifdef USING_SCOPE_INFO
psiScope* newScope = psiNewPrologScope(varScope->vsdLVnum, varScope->vsdVarNum);
+#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ siVarLoc varLocation;
+#endif // USING_VARIABLE_LIVE_RANGE
- if (lclVarDsc1->lvIsRegArg)
+ if (lclVarDsc->lvIsRegArg)
{
bool isStructHandled = false;
#if defined(UNIX_AMD64_ABI)
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
- if (varTypeIsStruct(lclVarDsc1))
+ if (varTypeIsStruct(lclVarDsc))
{
- CORINFO_CLASS_HANDLE typeHnd = lclVarDsc1->lvVerTypeInfo.GetClassHandle();
+ CORINFO_CLASS_HANDLE typeHnd = lclVarDsc->lvVerTypeInfo.GetClassHandle();
assert(typeHnd != nullptr);
compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
if (structDesc.passedInRegisters)
@@ -1330,11 +1565,11 @@ void CodeGen::psiBegProlog()
if (nCnt == 0)
{
- regNum = lclVarDsc1->lvArgReg;
+ regNum = lclVarDsc->lvArgReg;
}
else if (nCnt == 1)
{
- otherRegNum = lclVarDsc1->lvOtherArgReg;
+ otherRegNum = lclVarDsc->lvOtherArgReg;
}
else
{
@@ -1347,15 +1582,25 @@ void CodeGen::psiBegProlog()
assert(genMapRegNumToRegArgNum((nCnt == 0 ? regNum : otherRegNum), regType) != (unsigned)-1);
#endif // DEBUG
}
-
+#ifdef USING_SCOPE_INFO
newScope->scRegister = true;
newScope->u1.scRegNum = (regNumberSmall)regNum;
newScope->u1.scOtherReg = (regNumberSmall)otherRegNum;
+#endif // USING_SCOPE_INFO
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ varLocation.storeVariableInRegisters(regNum, otherRegNum);
+#endif // USING_VARIABLE_LIVE_RANGE
}
else
{
- // Stack passed argument. Get the offset from the caller's frame.
- psSetScopeOffset(newScope, lclVarDsc1);
+// Stack passed argument. Get the offset from the caller's frame.
+#ifdef USING_SCOPE_INFO
+ psiSetScopeOffset(newScope, lclVarDsc);
+#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ varLocation.storeVariableOnStack(REG_SPBASE, psiGetVarStackOffset(lclVarDsc));
+#endif // USING_VARIABLE_LIVE_RANGE
}
isStructHandled = true;
@@ -1364,25 +1609,64 @@ void CodeGen::psiBegProlog()
if (!isStructHandled)
{
#ifdef DEBUG
- var_types regType = compiler->mangleVarArgsType(lclVarDsc1->TypeGet());
- if (lclVarDsc1->lvIsHfaRegArg())
+ var_types regType = compiler->mangleVarArgsType(lclVarDsc->TypeGet());
+ if (lclVarDsc->lvIsHfaRegArg())
{
- regType = lclVarDsc1->GetHfaType();
+ regType = lclVarDsc->GetHfaType();
}
- assert(genMapRegNumToRegArgNum(lclVarDsc1->lvArgReg, regType) != (unsigned)-1);
+ assert(genMapRegNumToRegArgNum(lclVarDsc->lvArgReg, regType) != (unsigned)-1);
#endif // DEBUG
+#ifdef USING_SCOPE_INFO
newScope->scRegister = true;
- newScope->u1.scRegNum = (regNumberSmall)lclVarDsc1->lvArgReg;
+ newScope->u1.scRegNum = (regNumberSmall)lclVarDsc->lvArgReg;
+#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ varLocation.storeVariableInRegisters(lclVarDsc->lvArgReg, REG_NA);
+#endif // USING_VARIABLE_LIVE_RANGE
}
}
else
{
- psSetScopeOffset(newScope, lclVarDsc1);
+#ifdef USING_SCOPE_INFO
+ psiSetScopeOffset(newScope, lclVarDsc);
+#endif // USING_SCOPE_INFO
+#ifdef USING_VARIABLE_LIVE_RANGE
+ varLocation.storeVariableOnStack(REG_SPBASE, psiGetVarStackOffset(lclVarDsc));
+#endif // USING_VARIABLE_LIVE_RANGE
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // Start a VariableLiveRange for this LclVarDsc on the built location
+ compiler->getVariableLiveKeeper()->psiStartVariableLiveRange(varLocation, varScope->vsdVarNum);
+#endif // USING_VARIABLE_LIVE_RANGE
}
}
+//------------------------------------------------------------------------
+// psiEndProlog: Close all the open "psiScope" or "VariableLiveRanges"
+// after prolog has been generated.
+//
+// Notes:
+// This function is expected to be called after prolog code has been generated.
+//
+void CodeGen::psiEndProlog()
+{
+ assert(compiler->compGeneratingProlog);
+#ifdef USING_SCOPE_INFO
+ for (psiScope* scope = psiOpenScopeList.scNext; scope; scope = psiOpenScopeList.scNext)
+ {
+ psiEndPrologScope(scope);
+ }
+#endif
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ compiler->getVariableLiveKeeper()->psiClosePrologVariableRanges();
+#endif // USING_VARIABLE_LIVE_RANGE
+}
+
+#ifdef USING_SCOPE_INFO
+
/*****************************************************************************
Enable this macro to get accurate prolog information for every instruction
in the prolog. However, this is overkill as nobody steps through the
@@ -1598,19 +1882,4 @@ void CodeGen::psiMoveToStack(unsigned varNum)
#endif // ACCURATE_PROLOG_DEBUG_INFO
}
-
-/*****************************************************************************
- * psiEndProlog
- */
-
-void CodeGen::psiEndProlog()
-{
- assert(compiler->compGeneratingProlog);
- psiScope* scope;
-
- for (scope = psiOpenScopeList.scNext; scope; scope = psiOpenScopeList.scNext)
- {
- psiEndPrologScope(scope);
- }
-}
#endif // USING_SCOPE_INFO
diff --git a/src/jit/treelifeupdater.cpp b/src/jit/treelifeupdater.cpp
index d5e3ad9a0b..1dbd5ed3cf 100644
--- a/src/jit/treelifeupdater.cpp
+++ b/src/jit/treelifeupdater.cpp
@@ -245,6 +245,12 @@ void TreeLifeUpdater<ForCodeGen>::UpdateLifeVar(GenTree* tree)
}
#endif // DEBUG
}
+
+#ifdef USING_VARIABLE_LIVE_RANGE
+ // For each of the LclVarDsc that are reporting change, variable or fields
+ compiler->getVariableLiveKeeper()->siStartOrCloseVariableLiveRanges(varDeltaSet, isBorn, isDying);
+#endif // USING_VARIABLE_LIVE_RANGE
+
#ifdef USING_SCOPE_INFO
compiler->codeGen->siUpdate();
#endif // USING_SCOPE_INFO