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