summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/design-docs/variabletracking.md344
-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
17 files changed, 1964 insertions, 225 deletions
diff --git a/Documentation/design-docs/variabletracking.md b/Documentation/design-docs/variabletracking.md
new file mode 100644
index 0000000000..db46291f41
--- /dev/null
+++ b/Documentation/design-docs/variabletracking.md
@@ -0,0 +1,344 @@
+Reporting Variable Location
+===================
+Table of Contents
+-----------------
+
+[Debug Info](#debug-info)
+
+[Context](#context)
+
+[Variable Number](#variable-number)
+
+[Debug Code vs Optimized Code](#debug-code-vs-optimized-code)
+
+[siScope / psiScope Structures](#siscope-psiscope-structure)
+
+[Generating siScope / psiScope info](#siscope-psiscope)
+
+[Variable Live Range Structure](#variable-live-range-structure)
+
+[Generating Variable Live Range](#variable-live-range)
+
+[Turning On Debug Info](#debug-indo-flags)
+
+[Dumps and Debugging Support](#dumps-and-debugging-support)
+
+[Future Extensions and Enhancements](#future-extensions-and-enhancements)
+
+Debug Info
+--------
+
+The debugger expects to receive an array of `VarResultInfo` which indicates:
+
+- IL variable number
+
+- Location
+
+- First native offset this location is valid.
+
+- First native offset this location is no longer valid
+
+There could be more than one per variable.
+There should be no overlap in terms of starting/ending native offsets for the same variable.
+Given a native offset, no two variables should share the same location.
+
+Context
+--------
+
+We generate variables debug info while we generate the assembly intructions of the code. This is happening in `genCodeForBBList()` and `genFnProlog()`.
+
+If we are jitting release code, information about variable liveness will be included in `GenTree`s and `BasicBlock` when `genCodeForBBList` is reached.
+For each `BasicBlock`:
+
+- `bbLiveIn`: alive at the beginning
+
+- `bbLiveOut`: alive at the end
+
+Also each `GenTree` has a mask `gtFlags` indicating will include if a variable:
+
+- is being born
+
+- is becoming dead
+
+- is being spilled
+
+- has been spilled
+
+When generating each instruction, these flags are used to know if we need to start/end tracking a variable or update its location (which means in practice "end and start").
+The blocks set of live variables are also used to start/end variables tracking information.
+
+Once the code is done we know the native offset of every assembly instruction, which is required for the debug info.
+While we are generating code we don't work with native offsets but `emitLocation`s that the `emitter` can generate, and once the code is done we can get the native offset corresponding to each of them.
+
+Variable Number
+--------
+
+If we compile a method and inspect the generated IL we can see there is a "locals" section which holds the local variables of you C# code named with a Vxx pattern.
+When we report debug info to the debugger, we also have to report arguments and special arguments.
+Those are included at the beginning, so if we have three locals named as V00, V01 and V02, two arguments named as arg00, arg01 and one special argument named as sarg00, we would end with these variable numbers.
+
+Variable Name: arg00 arg01 sarg00 V00 V01 V02
+Variable Number: 0 1 2 3 4 5
+
+This correspond to the index in `Compiler::lvaTable` and `VariableLiveKeeeper::m_vlrLiveDsc`.
+
+If any change is applied to this, probably changes on `CordbJITILFrame::ILVariableToNative` are needed.
+
+Debug Code vs Optimized Code
+--------
+
+Variables on debug code:
+- are alive during the whole method
+
+- live in a fixed location on the stack the whole method
+
+- `bbLiveIn` and `bbLiveOut` sets are empty
+
+- there is no flag for spill/unspill variables
+
+Variables on optimized code:
+
+- could change their location during code execution
+
+- could change their liveness during code execution
+
+siScope / psiScope Structures
+--------
+
+#### siScope
+
+This struct is used to represent the ranges where a variable is alive during code generation.
+
+The struct has two fields to denote the native offset where a variable is valid:
+
+- `scStartLoc`: an emitLocation indicating from which instruction
+
+- `scEndLoc`: an emitLocation indicating to which instruction
+
+and more fields to indicate from which variable is this scope.
+
+It doesn't have information about the location of the variable, only the stack level of the variable in the case it was on the stack.
+
+#### psiScope
+
+This struct is used to represent the ranges where a variable is alive during the prolog.
+It holds the same information than siScope with the addition of a way of describing the location.
+It describes the variable location with two registers or a base register and the offset.
+
+Generating siScope / psiScope info
+--------
+
+In order to use Scope Info for tracking variables location, the Flag `USING_SCOPE_INFO` in codegeninterface.h should be defined.
+
+#### For Prolog
+
+We open a `psiScope` for every parameter indicating its location at the beginning of the prolog. We then close them all at the end of the prolog.
+
+#### For Method Code
+
+We start generating code for every `BasicBlock`:
+
+- checking at the beginning of each one if there is a variable which does not have an open siScope and has a `varScope` with an starting IL offset lower or equal to the beginning of the block.
+
+- closing a siScope if a variable is last used.
+
+and we close all the open `siScope`s when the last block is reached.
+
+##### Reporting Information
+
+Once the code for the block and each of the `BasicBlock` is done, `genSetScopeInfo()` is called.
+In the case of having the flag `USING_SCOPE_INFO`,
+
+All the `psiScope` list is iterated filling the debug info structure.
+Then the list of `siScope` created is iterated, using the `LclVarDsc` class to get the location of the variable, which would hold the description of the last position it has.
+This is not a problem for debug code because variables live in a fixed location the whole method.
+
+This is done in `CodeGen::genSetScopeInfoUsingsiScope()`
+
+Variable Live Range Structure
+--------
+
+### VariableLiveRange
+
+This class is used to represent an uninterruptible portion of variable live in one location.
+Owns two emitLocations indicating the first instructions that make it valid and invalid respectively.
+It also has the location of the variable in this range, which is stored at the moment of being created.
+
+To save space, we save the ending emitLocation only when the variable is being moved or its liveness change, which could happen many blocks after it was being born.
+This means a `VariableLiveRange` native offsets range could be longer than a `BasicBlock`.
+
+### VariableLiveDescriptor
+
+This class is used to represent the liveness of a variable.
+It has a list of `VariableLiveRange`s and common operations to update the variable liveness.
+
+### VariableLiveKeeper
+
+It holds an array of `VariableLiveDescriptor`s, one per variable that is being tracked.
+The index of each variable inside this array is the same as in `Compiler::lvaTable`.
+
+We could have `VariableLiveDescriptor` inside each LclVarDsc or an array of `VariableLiveDescriptor` inside `Compiler`, but the intention is to move code out of `Compiler` and not increase it.
+
+Generating Variable Live Range
+--------
+
+In order to use Variable Live Range for tracking variables location, the Flag `USING_VARIABLE_LIVE_RANGE` in codegeninterface.h should be defined.
+
+### For optimized code
+
+In `genCodeForBBList()`, we start generating code for each block dealing with some cases.
+
+On `BasicBlock` boundaries:
+
+- `BasicBlock`s beginning:
+
+ - If a variable has an open `VariableLiveRange` but the location is different than what is expected to be in this block we update it (close the existing one and create another).
+ This could happen because a variable could be in the `bbLiveOut` of a block and in the `BasicBlock`s `bbLiveOut` of the next one, but that doesn't mean that the execution thread would go from one immediately to the next one.
+ For this kind of cases another block that moves the variable from its original to the expected position is created.
+ This is handled in `LinearScan::recordVarLocationsAtStartOfBB(BasicBlock* bb)`.
+
+ - If a variable doesn't have an open `VariableLiveRange` and is in `bbLiveIn`, we open one.
+ This is done in `genUpdateLife` immediately after the the previous method is called.
+
+ - If a variable has an open `VariableLiveRange` and is not in `bbLiveIn`, we close it.
+ This is handled in `genUpdateLife` too.
+
+- last `BasicBlock`s ending:
+
+ - We close every open `VariableLiveRange`.
+ This is handled in `genCodeForBBList` when iterating the blocks, after the code for each block is done.
+
+For each instruction in `BasicBlock`:
+- a `VariableLiveRange` is opened for each variable that is being born and is not becoming dead at the same instruction
+ Handled in `TreeLifeUpdater::UpdateLifeVar(GenTree* tree)`.
+
+- a `VariableLiveRange` is closed and another one opened for each variable that is being spilled, unspilled or copied and is not dying at the same instruction.
+ Spills and copies are handled in `TreeLifeUpdater::UpdateLifeVar(GenTree* tree)`, unspills in `CodeGen::genUnspillRegIfNeeded(GenTree* tree)`.
+
+- a `VariableLiveRange` is closed for each variable that is dying.
+ Handled in `TreeLifeUpdater::UpdateLifeVar(GenTree* tree)`
+
+We are not reporting the cases where a variable liveness is being modified and also becoming dead for some reasons:
+- We are using inclusive native offset for the beginning and exclusive native offset for the ending of a VariableLiveRange.
+ This property is used when the debugger is looking the location of a variable in `FindNativeInfoInILVariableArray`.
+ So a `VariableLiveRange` with exactly the same native offset for both would represent an empty live range and will not been found by the debugger.
+
+- Each C# line commonly end being more than one assembly instruction, if it exist on the assembly code.
+ When you put a breakpoint in one C# line, you are stoping at the first of this group of instruction.
+ A `VariableLiveRange` of just on assembly instruction seems unlikely to affect user debugging experience.
+
+- Less memory used to save VariabeLiveRanges
+
+- We are just changing the variable location.
+ We are not changing the variable value and the "old" variable location is not being modified.
+ So during that only assembly instruction, both `VariableLiveRange`s are valid, and we can increase the previous `VariableLiveRange` ending native offset a few bytes (we are not doing that now) and avoid creating a new `VariableLiveRange`.
+
+
+### For debug code
+
+As no flag is being added `GenTree` that we can consume and no variable is included in `bbLiveIn` or `bbLiveOut`, we are currently reporting a variable as being born the same way is done for siScope info in `siBeginBlock` each time before `BasicBlock`s code is generated.
+The death of a variable is handled at the end of the last `BasicBlock` as variable live during the whole method.
+
+### Reporting Information
+
+We just iterate throught all the `VariableLiveRange`s of all the variables that are tracked in `CodeGen::genSetScopeInfoUsingVariableRanges()`.
+
+Turning On Debug Info
+--------
+
+There is a flag to turn on each of this ways of tracking variable debug info:
+- : for `siScope` and `psiScope`
+
+- : for `VariableLiveRange`
+
+In case only one of them is defined, that one will be sent to the debugger.
+If both are defined, Scope info is sent to the debugger.
+If none is defined, no info is sent to the debugger.
+
+Both flags can be found at the beginnig of `codegeninterface.h`
+
+Dumps and Debugging Support
+--------
+
+#### Variable Live Ranges activity during a BasicBlock
+
+If we have the flag for `VariableLiveRange`s we would get on the jitdump a verbose message after each `BasicBlock` is done indicating the changes for each variable.
+For example:
+
+```
+////////////////////////////////////////
+////////////////////////////////////////
+Var History Dump for Block 44
+Var 1:
+[esi [ (G_M8533_IG29,ins#2,ofs#3), NON_CLOSED_RANGE ]; ]
+Var 12:
+[ebp[-44] (1 slot) [ (G_M8533_IG29,ins#2,ofs#3), NON_CLOSED_RANGE ]; ]
+Var 17:
+[ebp[-56] (1 slot) [ (G_M8533_IG32,ins#2,ofs#7), (G_M8533_IG33,ins#10,ofs#32) ]; edx [ (G_M8533_IG33,ins#10,ofs#32), (G_M8533_IG33,ins#10,ofs#32) ]; ]
+////////////////////////////////////////
+////////////////////////////////////////
+```
+
+indicating that:
+
+- Variable with index number 1 is living in register esi since instruction group 29 and it is still living there at the end of the block.
+
+- Variable with index number 12 in a similar situation as 1 but living on the stack.
+
+- Variable with index number 17 was living on the stack since instruction group 32 and it was unspill on ig 33, which is the instruction group of this BasicBlock, and it isn't alive at the end of the block.
+
+- Those are the only variables that are being tracked in this method and were alive during part or the whole method.
+
+Each `VariableLiveRange` is dumped as:
+```
+Location [ starting_emit_location, ending_emit_location )
+```
+and a list of them for a variable.
+
+Something to consider is that as we don't have the final native offsets while we are generating code, we are just dumping `emitLocation`s.
+
+#### All the Variable Live Ranges
+
+We also get all the `VariableLiveRange`s dumped for all the variables once the code for the whole method is done with the native offsets in place of `emitLocation`s.
+
+The information follows the same pattern as before.
+
+```
+////////////////////////////////////////
+////////////////////////////////////////
+PRINTING REGISTER LIVE RANGES:
+[esi [3C , 270 )esi [275 , 2BE )esi [2DA , 390 )]
+IL Var Num 12:
+[ebp[-44] (1 slot) [200 , 270 )ebp[-44] (1 slot) [275 , 28A )ebp[-44] (1 slot) [292 , 2BE )ebp[-44] (1 slot) [2DA , 401 )ebp[-44] (1 slot) [406 , 449 )ebp[-44] (1 slot) [465 , 468 )edi [468 , 468 )]
+IL Var Num 17:
+[ebp[-56] (1 slot) [331 , 373 )edx [373 , 373 )]
+////////////////////////////////////////
+////////////////////////////////////////
+```
+
+#### Debug Info sent to the debugger
+
+The information sent to the debugger is dumped to as:
+```
+*************** In genSetScopeInfo()
+VarLocInfo count is 95
+*************** Variable debug info
+3 live ranges
+ 0( UNKNOWN) : From 00000000h to 0000001Ah, in ecx
+ 1( UNKNOWN) : From 0000003Ch to 00000270h, in esi
+ 1( UNKNOWN) : From 00000275h to 000002BEh, in esi
+```
+
+Future Extensions and Enhancements
+--------
+
+There are many things we can do to improve optimized debugging:
+
+- Inline functions: If you crash inside one, you get no info of your variables.
+ Currently we don't have the IL offset of them.
+ And this is broadly used to improve code performance.
+
+- [Promoted structs](https://github.com/dotnet/coreclr/issues/23542): There is no debug support for fields of promoted structs, we just report the struct itself.
+
+- [Reduce space used for VariableLiveDescriptor](https://github.com/dotnet/coreclr/issues/23544): we are currently using a `jitstd::list`, which is a double linked list.
+ We could use a simple single linked list with push_back(), head(), tail(), size() operations and an iterator and we would be saving memory.
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