diff options
author | Pat Gavlin <pagavlin@microsoft.com> | 2017-01-09 10:41:16 -0800 |
---|---|---|
committer | Pat Gavlin <pagavlin@microsoft.com> | 2017-01-09 10:41:16 -0800 |
commit | 474e27a5ac4ee651e2456a7503d4a8cf2ce7b00c (patch) | |
tree | d8f712ac085211e51c2ed2373547a2c8019bb665 /src | |
parent | 583733b399edb423719e4754a3184913495e7575 (diff) | |
download | coreclr-474e27a5ac4ee651e2456a7503d4a8cf2ce7b00c.tar.gz coreclr-474e27a5ac4ee651e2456a7503d4a8cf2ce7b00c.tar.bz2 coreclr-474e27a5ac4ee651e2456a7503d4a8cf2ce7b00c.zip |
Encapsulate live var analysis in its own class.
This change moves the implementation of live variable analysis from a
single function into a class in which the per-block portion of the
algorithm is contained in its own function. There is no functional
change.
Diffstat (limited to 'src')
-rw-r--r-- | src/jit/liveness.cpp | 271 |
1 files changed, 146 insertions, 125 deletions
diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp index 1b19785375..965c6fa773 100644 --- a/src/jit/liveness.cpp +++ b/src/jit/liveness.cpp @@ -1070,181 +1070,202 @@ VARSET_VALRET_TP Compiler::fgGetHandlerLiveVars(BasicBlock* block) return liveVars; } -/***************************************************************************** - * - * This is the classic algorithm for Live Variable Analysis. - * If updateInternalOnly==true, only update BBF_INTERNAL blocks. - */ - -void Compiler::fgLiveVarAnalysis(bool updateInternalOnly) +class LiveVarAnalysis { - BasicBlock* block; - bool change; -#ifdef DEBUG - VARSET_TP VARSET_INIT_NOCOPY(extraLiveOutFromFinally, VarSetOps::MakeEmpty(this)); -#endif // DEBUG - bool keepAliveThis = lvaKeepAliveAndReportThis() && lvaTable[info.compThisArg].lvTracked; + Compiler* m_compiler; - /* Live Variable Analysis - Backward dataflow */ + bool m_changed; + bool m_hasPossibleBackEdge; - bool hasPossibleBackEdge = false; + bool m_heapLiveIn; + bool m_heapLiveOut; + VARSET_TP m_liveIn; + VARSET_TP m_liveOut; - do + LiveVarAnalysis(Compiler* compiler) + : m_compiler(compiler), m_changed(false), m_hasPossibleBackEdge(false), m_heapLiveIn(false), m_heapLiveOut(false), m_liveIn(VarSetOps::MakeEmpty(compiler)), m_liveOut(VarSetOps::MakeEmpty(compiler)) { - change = false; - - /* Visit all blocks and compute new data flow values */ - - VARSET_TP VARSET_INIT_NOCOPY(liveIn, VarSetOps::MakeEmpty(this)); - VARSET_TP VARSET_INIT_NOCOPY(liveOut, VarSetOps::MakeEmpty(this)); - - bool heapLiveIn = false; - bool heapLiveOut = false; + } - for (block = fgLastBB; block; block = block->bbPrev) + void PerBlock(BasicBlock* block, bool updateInternalOnly, bool keepAliveThis) + { + /* Compute the 'liveOut' set */ + VarSetOps::ClearD(m_compiler, m_liveOut); + m_heapLiveOut = false; + if (block->endsWithJmpMethod(m_compiler)) { - // sometimes block numbers are not monotonically increasing which - // would cause us not to identify backedges - if (block->bbNext && block->bbNext->bbNum <= block->bbNum) + // A JMP uses all the arguments, so mark them all + // as live at the JMP instruction + // + const LclVarDsc* varDscEndParams = m_compiler->lvaTable + m_compiler->info.compArgsCount; + for (LclVarDsc* varDsc = m_compiler->lvaTable; varDsc < varDscEndParams; varDsc++) { - hasPossibleBackEdge = true; + noway_assert(!varDsc->lvPromoted); + if (varDsc->lvTracked) + { + VarSetOps::AddElemD(m_compiler, m_liveOut, varDsc->lvVarIndex); + } } + } - if (updateInternalOnly) + // Additionally, union in all the live-in tracked vars of successors. + AllSuccessorIter succsEnd = block->GetAllSuccs(m_compiler).end(); + for (AllSuccessorIter succs = block->GetAllSuccs(m_compiler).begin(); succs != succsEnd; ++succs) + { + BasicBlock* succ = (*succs); + VarSetOps::UnionD(m_compiler, m_liveOut, succ->bbLiveIn); + m_heapLiveOut = m_heapLiveOut || (*succs)->bbHeapLiveIn; + if (succ->bbNum <= block->bbNum) { - /* Only update BBF_INTERNAL blocks as they may be - syntactically out of sequence. */ + m_hasPossibleBackEdge = true; + } + } - noway_assert(opts.compDbgCode && (info.compVarScopesCount > 0)); + /* For lvaKeepAliveAndReportThis methods, "m_compiler" has to be kept alive everywhere + Note that a function may end in a throw on an infinite loop (as opposed to a return). + "m_compiler" has to be alive everywhere even in such methods. */ - if (!(block->bbFlags & BBF_INTERNAL)) - { - continue; - } - } + if (keepAliveThis) + { + VarSetOps::AddElemD(m_compiler, m_liveOut, m_compiler->lvaTable[m_compiler->info.compThisArg].lvVarIndex); + } - /* Compute the 'liveOut' set */ + /* Compute the 'm_liveIn' set */ + VarSetOps::Assign(m_compiler, m_liveIn, m_liveOut); + VarSetOps::DiffD(m_compiler, m_liveIn, block->bbVarDef); + VarSetOps::UnionD(m_compiler, m_liveIn, block->bbVarUse); + + m_heapLiveIn = (m_heapLiveOut && !block->bbHeapDef) || block->bbHeapUse; + + /* Can exceptions from m_compiler block be handled (in m_compiler function)? */ + + if (m_compiler->ehBlockHasExnFlowDsc(block)) + { + VARSET_TP VARSET_INIT_NOCOPY(liveVars, m_compiler->fgGetHandlerLiveVars(block)); + + VarSetOps::UnionD(m_compiler, m_liveIn, liveVars); + VarSetOps::UnionD(m_compiler, m_liveOut, liveVars); + } - VarSetOps::ClearD(this, liveOut); - heapLiveOut = false; - if (block->endsWithJmpMethod(this)) + /* Has there been any change in either live set? */ + + if (!VarSetOps::Equal(m_compiler, block->bbLiveIn, m_liveIn) || !VarSetOps::Equal(m_compiler, block->bbLiveOut, m_liveOut)) + { + if (updateInternalOnly) { - // A JMP uses all the arguments, so mark them all - // as live at the JMP instruction - // - const LclVarDsc* varDscEndParams = lvaTable + info.compArgsCount; - for (LclVarDsc* varDsc = lvaTable; varDsc < varDscEndParams; varDsc++) + // Only "extend" liveness over BBF_INTERNAL blocks + + noway_assert(block->bbFlags & BBF_INTERNAL); + + if (!VarSetOps::Equal(m_compiler, VarSetOps::Intersection(m_compiler, block->bbLiveIn, m_liveIn), m_liveIn) || + !VarSetOps::Equal(m_compiler, VarSetOps::Intersection(m_compiler, block->bbLiveOut, m_liveOut), m_liveOut)) { - noway_assert(!varDsc->lvPromoted); - if (varDsc->lvTracked) +#ifdef DEBUG + if (m_compiler->verbose) { - VarSetOps::AddElemD(this, liveOut, varDsc->lvVarIndex); + printf("Scope info: block BB%02u LiveIn+ ", block->bbNum); + dumpConvertedVarSet(m_compiler, VarSetOps::Diff(m_compiler, m_liveIn, block->bbLiveIn)); + printf(", LiveOut+ "); + dumpConvertedVarSet(m_compiler, VarSetOps::Diff(m_compiler, m_liveOut, block->bbLiveOut)); + printf("\n"); } - } - } +#endif // DEBUG - // Additionally, union in all the live-in tracked vars of successors. - AllSuccessorIter succsEnd = block->GetAllSuccs(this).end(); - for (AllSuccessorIter succs = block->GetAllSuccs(this).begin(); succs != succsEnd; ++succs) - { - BasicBlock* succ = (*succs); - VarSetOps::UnionD(this, liveOut, succ->bbLiveIn); - heapLiveOut = heapLiveOut || (*succs)->bbHeapLiveIn; - if (succ->bbNum <= block->bbNum) - { - hasPossibleBackEdge = true; + VarSetOps::UnionD(m_compiler, block->bbLiveIn, m_liveIn); + VarSetOps::UnionD(m_compiler, block->bbLiveOut, m_liveOut); + m_changed = true; } } - - /* For lvaKeepAliveAndReportThis methods, "this" has to be kept alive everywhere - Note that a function may end in a throw on an infinite loop (as opposed to a return). - "this" has to be alive everywhere even in such methods. */ - - if (keepAliveThis) + else { - VarSetOps::AddElemD(this, liveOut, lvaTable[info.compThisArg].lvVarIndex); + VarSetOps::Assign(m_compiler, block->bbLiveIn, m_liveIn); + VarSetOps::Assign(m_compiler, block->bbLiveOut, m_liveOut); + m_changed = true; } + } - /* Compute the 'liveIn' set */ - - VarSetOps::Assign(this, liveIn, liveOut); - VarSetOps::DiffD(this, liveIn, block->bbVarDef); - VarSetOps::UnionD(this, liveIn, block->bbVarUse); + if ((block->bbHeapLiveIn == 1) != m_heapLiveIn || (block->bbHeapLiveOut == 1) != m_heapLiveOut) + { + block->bbHeapLiveIn = m_heapLiveIn; + block->bbHeapLiveOut = m_heapLiveOut; + m_changed = true; + } + } - heapLiveIn = (heapLiveOut && !block->bbHeapDef) || block->bbHeapUse; + void Run(bool updateInternalOnly) + { + const bool keepAliveThis = m_compiler->lvaKeepAliveAndReportThis() && m_compiler->lvaTable[m_compiler->info.compThisArg].lvTracked; - /* Can exceptions from this block be handled (in this function)? */ + /* Live Variable Analysis - Backward dataflow */ + do + { + m_changed = false; - if (ehBlockHasExnFlowDsc(block)) - { - VARSET_TP VARSET_INIT_NOCOPY(liveVars, fgGetHandlerLiveVars(block)); + /* Visit all blocks and compute new data flow values */ - VarSetOps::UnionD(this, liveIn, liveVars); - VarSetOps::UnionD(this, liveOut, liveVars); - } + VarSetOps::ClearD(m_compiler, m_liveIn); + VarSetOps::ClearD(m_compiler, m_liveOut); - /* Has there been any change in either live set? */ + m_heapLiveIn = false; + m_heapLiveOut = false; - if (!VarSetOps::Equal(this, block->bbLiveIn, liveIn) || !VarSetOps::Equal(this, block->bbLiveOut, liveOut)) + for (BasicBlock* block = m_compiler->fgLastBB; block; block = block->bbPrev) { + // sometimes block numbers are not monotonically increasing which + // would cause us not to identify backedges + if (block->bbNext && block->bbNext->bbNum <= block->bbNum) + { + m_hasPossibleBackEdge = true; + } + if (updateInternalOnly) { - // Only "extend" liveness over BBF_INTERNAL blocks + /* Only update BBF_INTERNAL blocks as they may be + syntactically out of sequence. */ - noway_assert(block->bbFlags & BBF_INTERNAL); + noway_assert(m_compiler->opts.compDbgCode && (m_compiler->info.compVarScopesCount > 0)); - if (!VarSetOps::Equal(this, VarSetOps::Intersection(this, block->bbLiveIn, liveIn), liveIn) || - !VarSetOps::Equal(this, VarSetOps::Intersection(this, block->bbLiveOut, liveOut), liveOut)) + if (!(block->bbFlags & BBF_INTERNAL)) { -#ifdef DEBUG - if (verbose) - { - printf("Scope info: block BB%02u LiveIn+ ", block->bbNum); - dumpConvertedVarSet(this, VarSetOps::Diff(this, liveIn, block->bbLiveIn)); - printf(", LiveOut+ "); - dumpConvertedVarSet(this, VarSetOps::Diff(this, liveOut, block->bbLiveOut)); - printf("\n"); - } -#endif // DEBUG - - VarSetOps::UnionD(this, block->bbLiveIn, liveIn); - VarSetOps::UnionD(this, block->bbLiveOut, liveOut); - change = true; + continue; } } - else - { - VarSetOps::Assign(this, block->bbLiveIn, liveIn); - VarSetOps::Assign(this, block->bbLiveOut, liveOut); - change = true; - } - } - if ((block->bbHeapLiveIn == 1) != heapLiveIn || (block->bbHeapLiveOut == 1) != heapLiveOut) + PerBlock(block, updateInternalOnly, keepAliveThis); + } + // if there is no way we could have processed a block without seeing all of its predecessors + // then there is no need to iterate + if (!m_hasPossibleBackEdge) { - block->bbHeapLiveIn = heapLiveIn; - block->bbHeapLiveOut = heapLiveOut; - change = true; + break; } - } - // if there is no way we could have processed a block without seeing all of its predecessors - // then there is no need to iterate - if (!hasPossibleBackEdge) - { - break; - } - } while (change); + } while (m_changed); + } -//------------------------------------------------------------------------- +public: + static void Run(Compiler* compiler, bool updateInternalOnly) + { + LiveVarAnalysis analysis(compiler); + analysis.Run(updateInternalOnly); + } +}; -#ifdef DEBUG +/***************************************************************************** + * + * This is the classic algorithm for Live Variable Analysis. + * If updateInternalOnly==true, only update BBF_INTERNAL blocks. + */ +void Compiler::fgLiveVarAnalysis(bool updateInternalOnly) +{ + LiveVarAnalysis::Run(this, updateInternalOnly); + +#ifdef DEBUG if (verbose && !updateInternalOnly) { printf("\nBB liveness after fgLiveVarAnalysis():\n\n"); fgDispBBLiveness(); } - #endif // DEBUG } |