From: mikedn Date: Fri, 8 Mar 2019 18:29:07 +0000 (+0200) Subject: Improve SSA renaming memory usage (dotnet/coreclr#15000) X-Git-Tag: submit/tizen/20210909.063632~11030^2~2240 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e083eb94e63ee16f9fe7281e9f2c42599bc431e0;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Improve SSA renaming memory usage (dotnet/coreclr#15000) * Cleanup DumpStacks It's not exactly useful to dump all the stacks after pushing to a stack. Nor is it useful to dump all the stack after popping only some, perhaps none, in PopBlockStacks. Also dump stack from top to bottom, makes it easier to find the top, which is usually what you care about during SSA renaming. * Stop passing null block to SsaRenameState::Push It makes no difference if the definition is in the "block before any real blocks..." or at the start of the first block, it's just an unnecessary complication. * Stop handling byrefStatesMatchGcHeapStates in SsaRenameState SsaBuilder already handles that, doing it again in SsaRenameState just duplicates logic. * Stop using "count" as a name for "SSA number" Worst name ever. Also use "block" consistently, instead of a mix of "bb" and "block". * Delete "ConstructedArray", not needed * Various cleanup - Change SsaRenameState to a class - Cleanup remaining function comments - Move SsaRenameStateForBlock & SsaRenameStateLocDef inside SsaRenameState - Make EnsureStacks private - Reorder data members - Use m_ prefix consistently * Replace jitstd::list with a custom stack std::list has a few drawbacks: - It's a doubly linked list but a singly linked list suffices so every node wastes 8 bytes for an extra pointer. - The list object itself is relatively large 2 head/tail pointers, node count and memory allocator. There can be hundreds of such objects (one for each local variable) so the smaller the better. Replace with a simple singly linked, intrusive list based stack. * Share push code between lclvar and memory It's pretty much the same logic (the only difference is that in the memory case "top" can't ever be null so by sharing the code we get a redundant null check). Commit migrated from https://github.com/dotnet/coreclr/commit/fb583867ae37e75cf9f5c4f556627283a257d67b --- diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 2888839..ef3bca2 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -4995,10 +4995,10 @@ struct GenTreePhiArg : public GenTreeLclVarCommon { BasicBlock* gtPredBB; - GenTreePhiArg(var_types type, unsigned lclNum, unsigned snum, BasicBlock* block) + GenTreePhiArg(var_types type, unsigned lclNum, unsigned ssaNum, BasicBlock* block) : GenTreeLclVarCommon(GT_PHI_ARG, type, lclNum), gtPredBB(block) { - SetSsaNum(snum); + SetSsaNum(ssaNum); } #if DEBUGGABLE_GENTREE diff --git a/src/coreclr/src/jit/ssabuilder.cpp b/src/coreclr/src/jit/ssabuilder.cpp index d7b8b76..7fdc37b 100644 --- a/src/coreclr/src/jit/ssabuilder.cpp +++ b/src/coreclr/src/jit/ssabuilder.cpp @@ -881,7 +881,6 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename { // GcHeap and ByrefExposed share the same stacks, SsaMap, and phis assert(!hasByrefHavoc); - assert(pRenameState->CountForMemoryUse(GcHeap) == ssaNum); assert(*m_pCompiler->GetMemorySsaMap(GcHeap)->LookupPointer(tree) == ssaNum); assert(block->bbMemorySsaPhiFunc[GcHeap] == block->bbMemorySsaPhiFunc[ByrefExposed]); } @@ -926,7 +925,7 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename // This is a partial definition of a variable. The node records only the SSA number // of the use that is implied by this partial definition. The SSA number of the new // definition will be recorded in the m_opAsgnVarDefSsaNums map. - tree->AsLclVarCommon()->SetSsaNum(pRenameState->CountForUse(lclNum)); + tree->AsLclVarCommon()->SetSsaNum(pRenameState->Top(lclNum)); m_pCompiler->GetOpAsgnVarDefSsaNums()->Set(tree, ssaNum); } @@ -937,7 +936,7 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename pRenameState->Push(block, lclNum, ssaNum); - // If necessary, add "lclNum/count" to the arg list of a phi def in any + // If necessary, add "lclNum/ssaNum" to the arg list of a phi def in any // handlers for try blocks that "block" is within. (But only do this for "real" definitions, // not phi definitions.) if (!isPhiDefn) @@ -965,13 +964,12 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename return; } } - // Give the count as top of stack. - unsigned count = pRenameState->CountForUse(lclNum); - tree->gtLclVarCommon.SetSsaNum(count); + + tree->AsLclVarCommon()->SetSsaNum(pRenameState->Top(lclNum)); } } -void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigned count) +void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigned ssaNum) { assert(m_pCompiler->lvaTable[lclNum].lvTracked); // Precondition. unsigned lclIndex = m_pCompiler->lvaTable[lclNum].lvVarIndex; @@ -981,7 +979,7 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne { DBG_SSA_JITDUMP("Definition of local V%02u/d:%d in block " FMT_BB " has exn handler; adding as phi arg to handlers.\n", - lclNum, count, block->bbNum); + lclNum, ssaNum, block->bbNum); while (true) { BasicBlock* handler = tryBlk->ExFlowBlock(); @@ -1007,7 +1005,7 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne if (tree->gtOp.gtOp1->gtLclVar.gtLclNum == lclNum) { - // It's the definition for the right local. Add "count" to the RHS. + // It's the definition for the right local. Add "ssaNum" to the RHS. GenTree* phi = tree->gtOp.gtOp2; GenTreeArgList* args = nullptr; if (phi->gtOp.gtOp1 != nullptr) @@ -1019,12 +1017,12 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne for (GenTreeArgList* curArgs = args; curArgs != nullptr; curArgs = curArgs->Rest()) { GenTreePhiArg* phiArg = curArgs->Current()->AsPhiArg(); - assert(phiArg->gtSsaNum != count); + assert(phiArg->gtSsaNum != ssaNum); } #endif var_types typ = m_pCompiler->lvaTable[lclNum].TypeGet(); GenTreePhiArg* newPhiArg = - new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(typ, lclNum, count, block); + new (m_pCompiler, GT_PHI_ARG) GenTreePhiArg(typ, lclNum, ssaNum, block); phi->gtOp.gtOp1 = new (m_pCompiler, GT_LIST) GenTreeArgList(newPhiArg, args); m_pCompiler->gtSetStmtInfo(stmt); @@ -1033,7 +1031,7 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne phiFound = true; #endif DBG_SSA_JITDUMP(" Added phi arg u:%d for V%02u to phi defn in handler block " FMT_BB ".\n", - count, lclNum, handler->bbNum); + ssaNum, lclNum, handler->bbNum); break; } } @@ -1051,7 +1049,7 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne } } -void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned count) +void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned ssaNum) { if (m_pCompiler->ehBlockHasExnFlowDsc(block)) { @@ -1063,7 +1061,7 @@ void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* bl // Otherwise... DBG_SSA_JITDUMP("Definition of %s/d:%d in block " FMT_BB " has exn handler; adding as phi arg to handlers.\n", - memoryKindNames[memoryKind], count, block->bbNum); + memoryKindNames[memoryKind], ssaNum, block->bbNum); EHblkDsc* tryBlk = m_pCompiler->ehGetBlockExnFlowDsc(block); while (true) { @@ -1074,7 +1072,7 @@ void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* bl { assert(handler->bbMemorySsaPhiFunc != nullptr); - // Add "count" to the phi args of memoryKind. + // Add "ssaNum" to the phi args of memoryKind. BasicBlock::MemoryPhiArg*& handlerMemoryPhi = handler->bbMemorySsaPhiFunc[memoryKind]; #if DEBUG @@ -1093,7 +1091,7 @@ void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* bl if (handlerMemoryPhi == BasicBlock::EmptyMemoryPhiDef) { - handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(count); + handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(ssaNum); } else { @@ -1101,14 +1099,14 @@ void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* bl BasicBlock::MemoryPhiArg* curArg = handler->bbMemorySsaPhiFunc[memoryKind]; while (curArg != nullptr) { - assert(curArg->GetSsaNum() != count); + assert(curArg->GetSsaNum() != ssaNum); curArg = curArg->m_nextArg; } #endif // DEBUG - handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(count, handlerMemoryPhi); + handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(ssaNum, handlerMemoryPhi); } - DBG_SSA_JITDUMP(" Added phi arg u:%d for %s to phi defn in handler block " FMT_BB ".\n", count, + DBG_SSA_JITDUMP(" Added phi arg u:%d for %s to phi defn in handler block " FMT_BB ".\n", ssaNum, memoryKindNames[memoryKind], memoryKind, handler->bbNum); if ((memoryKind == ByrefExposed) && m_pCompiler->byrefStatesMatchGcHeapStates) @@ -1148,7 +1146,8 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename assert(block->bbMemorySsaPhiFunc[memoryKind] == block->bbMemorySsaPhiFunc[ByrefExposed]); // so we will have already allocated a defnum for it if needed. assert(memoryKind > ByrefExposed); - assert(pRenameState->CountForMemoryUse(memoryKind) == pRenameState->CountForMemoryUse(ByrefExposed)); + + block->bbMemorySsaNumIn[memoryKind] = pRenameState->TopMemory(ByrefExposed); } else { @@ -1160,11 +1159,14 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename DBG_SSA_JITDUMP("Ssa # for %s phi on entry to " FMT_BB " is %d.\n", memoryKindNames[memoryKind], block->bbNum, ssaNum); + + block->bbMemorySsaNumIn[memoryKind] = ssaNum; + } + else + { + block->bbMemorySsaNumIn[memoryKind] = pRenameState->TopMemory(memoryKind); } } - - // Record the "in" Ssa # for memoryKind. - block->bbMemorySsaNumIn[memoryKind] = pRenameState->CountForMemoryUse(memoryKind); } // We need to iterate over phi definitions, to give them SSA names, but we need @@ -1199,7 +1201,8 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename assert(memoryKind > ByrefExposed); assert(((block->bbMemoryDef & memorySet) != 0) == ((block->bbMemoryDef & memoryKindSet(ByrefExposed)) != 0)); - assert(pRenameState->CountForMemoryUse(memoryKind) == pRenameState->CountForMemoryUse(ByrefExposed)); + + block->bbMemorySsaNumOut[memoryKind] = pRenameState->TopMemory(ByrefExposed); } else { @@ -1208,12 +1211,15 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename unsigned ssaNum = m_pCompiler->lvMemoryPerSsaData.AllocSsaNum(m_allocator); pRenameState->PushMemory(memoryKind, block, ssaNum); AddMemoryDefToHandlerPhis(memoryKind, block, ssaNum); + + block->bbMemorySsaNumOut[memoryKind] = ssaNum; + } + else + { + block->bbMemorySsaNumOut[memoryKind] = pRenameState->TopMemory(memoryKind); } } - // Record the "out" Ssa" # for memoryKind. - block->bbMemorySsaNumOut[memoryKind] = pRenameState->CountForMemoryUse(memoryKind); - DBG_SSA_JITDUMP("Ssa # for %s on entry to " FMT_BB " is %d; on exit is %d.\n", memoryKindNames[memoryKind], block->bbNum, block->bbMemorySsaNumIn[memoryKind], block->bbMemorySsaNumOut[memoryKind]); } @@ -1243,7 +1249,7 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR assert(phiNode->gtOp.gtOp1 == nullptr || phiNode->gtOp.gtOp1->OperGet() == GT_LIST); unsigned lclNum = tree->gtOp.gtOp1->gtLclVar.gtLclNum; - unsigned ssaNum = pRenameState->CountForUse(lclNum); + unsigned ssaNum = pRenameState->Top(lclNum); // Search the arglist for an existing definition for ssaNum. // (Can we assert that its the head of the list? This should only happen when we add // during renaming for a definition that occurs within a try, and then that's the last @@ -1399,8 +1405,7 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR assert(phiNode->gtOp.gtOp1 == nullptr || phiNode->gtOp.gtOp1->OperGet() == GT_LIST); GenTreeArgList* argList = reinterpret_cast(phiNode->gtOp.gtOp1); - // What is the current SSAName from the predecessor for this local? - unsigned ssaNum = pRenameState->CountForUse(lclNum); + unsigned ssaNum = pRenameState->Top(lclNum); // See if this ssaNum is already an arg to the phi. bool alreadyArg = false; @@ -1474,35 +1479,10 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR } /** - * Walk the block's tree in the evaluation order and reclaim rename stack for var definitions. - * - * @param block Block for which SSA variables have to be renamed. - * @param pRenameState The incremental rename information stored during renaming process. - * - */ -void SsaBuilder::BlockPopStacks(BasicBlock* block, SsaRenameState* pRenameState) -{ - // Pop the names given to the non-phi nodes. - pRenameState->PopBlockStacks(block); - - // And for memory. - for (MemoryKind memoryKind : allMemoryKinds()) - { - if ((memoryKind == GcHeap) && m_pCompiler->byrefStatesMatchGcHeapStates) - { - // GcHeap and ByrefExposed share a rename stack, so don't try - // to pop it a second time. - continue; - } - pRenameState->PopBlockMemoryStack(memoryKind, block); - } -} - -/** * Perform variable renaming. * * Walks the blocks and renames all var defs with ssa numbers and all uses with the - * current count that is in the top of the stack. Assigns phi node rhs variables + * SSA number that is in the top of the stack. Assigns phi node rhs variables * (i.e., the arguments to the phi.) Then, calls the function recursively on child * nodes in the DOM tree to continue the renaming process. * @@ -1540,7 +1520,7 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRe // In ValueNum we'd assume un-inited variables get FIRST_SSA_NUM. assert(ssaNum == SsaConfig::FIRST_SSA_NUM); - pRenameState->Push(nullptr, lclNum, ssaNum); + pRenameState->Push(m_pCompiler->fgFirstBB, lclNum, ssaNum); } } @@ -1602,7 +1582,6 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRe // been (recursively) processed, we still need to call BlockPopStacks on it. blocksToDo->push_back(BlockWork(block, true)); - // Walk the block give counts to DEFs and give top of stack count for USEs. BlockRenameVariables(block, pRenameState); // Assign arguments to the phi node of successors, corresponding to the block's index. @@ -1621,8 +1600,8 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRe } else { - // Done, pop all the stack count, if there is one for this block. - BlockPopStacks(block, pRenameState); + // Done, pop all SSA numbers pushed in this block. + pRenameState->PopBlockStacks(block); DBG_SSA_JITDUMP("[SsaBuilder::RenameVariables] done with " FMT_BB "\n", block->bbNum); } } @@ -1744,9 +1723,8 @@ void SsaBuilder::Build() InsertPhiFunctions(postOrder, count); // Rename local variables and collect UD information for each ssa var. - SsaRenameState* pRenameState = - new (m_allocator) SsaRenameState(m_allocator, m_pCompiler->lvaCount, m_pCompiler->byrefStatesMatchGcHeapStates); - RenameVariables(domTree, pRenameState); + SsaRenameState renameState(m_allocator, m_pCompiler->lvaCount); + RenameVariables(domTree, &renameState); EndPhase(PHASE_BUILD_SSA_RENAME); #ifdef DEBUG diff --git a/src/coreclr/src/jit/ssabuilder.h b/src/coreclr/src/jit/ssabuilder.h index eab2554..86c1d12 100644 --- a/src/coreclr/src/jit/ssabuilder.h +++ b/src/coreclr/src/jit/ssabuilder.h @@ -9,7 +9,7 @@ #include "compiler.h" -struct SsaRenameState; +class SsaRenameState; typedef int LclVarNum; @@ -105,12 +105,6 @@ private: // Assigns gtSsaNames to all variables. void RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRenameState); - // Requires "block" to be any basic block participating in variable renaming, and has at least a - // definition that pushed a ssa number into the rename stack for a variable. Requires "pRenameState" - // to have variable stacks that have counts pushed into them for the block while assigning def - // numbers. Pops the stack for any local variable that has an entry for block on top. - void BlockPopStacks(BasicBlock* block, SsaRenameState* pRenameState); - // Requires "block" to be non-NULL; and is searched for defs and uses to assign ssa numbers. // Requires "pRenameState" to be non-NULL and be currently used for variables renaming. void BlockRenameVariables(BasicBlock* block, SsaRenameState* pRenameState); @@ -120,15 +114,15 @@ private: // implies that any definition occurring within "tree" is a phi definition. void TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRenameState* pRenameState, bool isPhiDefn); - // Assumes that "block" contains a definition for local var "lclNum", with SSA number "count". + // Assumes that "block" contains a definition for local var "lclNum", with SSA number "ssaNum". // IF "block" is within one or more try blocks, // and the local variable is live at the start of the corresponding handlers, - // add this SSA number "count" to the argument list of the phi for the variable in the start + // add this SSA number "ssaNum" to the argument list of the phi for the variable in the start // block of those handlers. - void AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigned count); + void AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigned ssaNum); // Same as above, for memory. - void AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned count); + void AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned ssaNum); // Requires "block" to be non-NULL. Requires "pRenameState" to be non-NULL and be currently used // for variables renaming. Assigns the rhs arguments to the phi, i.e., block's phi node arguments. diff --git a/src/coreclr/src/jit/ssarenamestate.cpp b/src/coreclr/src/jit/ssarenamestate.cpp index 63f120a..c715aeb 100644 --- a/src/coreclr/src/jit/ssarenamestate.cpp +++ b/src/coreclr/src/jit/ssarenamestate.cpp @@ -6,185 +6,156 @@ #include "ssaconfig.h" #include "ssarenamestate.h" -/** - * Constructor - initialize the stacks and counters maps (lclVar -> stack/counter) map. - * - * @params alloc The allocator class used to allocate jitstd data. - */ -SsaRenameState::SsaRenameState(CompAllocator alloc, unsigned lvaCount, bool byrefStatesMatchGcHeapStates) - : stacks(nullptr) - , definedLocs(alloc) - , memoryStack(alloc) - , lvaCount(lvaCount) - , m_alloc(alloc) - , byrefStatesMatchGcHeapStates(byrefStatesMatchGcHeapStates) +//------------------------------------------------------------------------ +// SsaRenameState: Initialize SsaRenameState +// +// Arguments: +// alloc - A memory allocator +// lvaCount - The number of local variables +// +SsaRenameState::SsaRenameState(CompAllocator alloc, unsigned lvaCount) + : m_alloc(alloc), m_lvaCount(lvaCount), m_stacks(nullptr), m_stackListTail(nullptr) { } -/** - * Allocates memory for holding pointers to lcl's stacks, - * if not allocated already. - * - */ +//------------------------------------------------------------------------ +// EnsureStacks: Allocate memory for the stacks array. +// void SsaRenameState::EnsureStacks() { - if (stacks == nullptr) + if (m_stacks == nullptr) { - stacks = m_alloc.allocate(lvaCount); - for (unsigned i = 0; i < lvaCount; ++i) - { - stacks[i] = nullptr; - } + m_stacks = new (m_alloc) Stack[m_lvaCount](); } } -/** - * Returns a SSA count number for a local variable from top of the stack. - * - * @params lclNum The local variable def for which a count has to be returned. - * @return the current variable name for the "use". - * - * @remarks If the stack is empty, then we have an use before a def. To handle this - * special case, we need to initialize the count with 'default+1', so the - * next definition will always use 'default+1' but return 'default' for - * all uses until a definition. - * - */ -unsigned SsaRenameState::CountForUse(unsigned lclNum) +//------------------------------------------------------------------------ +// Top: Get the SSA number at the top of the stack for the specified variable. +// +// Arguments: +// lclNum - The local variable number +// +// Return Value: +// The SSA number. +// +// Notes: +// The stack must not be empty. Method parameters and local variables that are live in at +// the start of the first block must have associated SSA definitions and their SSA numbers +// must have been pushed first. +// +unsigned SsaRenameState::Top(unsigned lclNum) { - EnsureStacks(); - DBG_SSA_JITDUMP("[SsaRenameState::CountForUse] V%02u\n", lclNum); + DBG_SSA_JITDUMP("[SsaRenameState::Top] V%02u\n", lclNum); - Stack* stack = stacks[lclNum]; - noway_assert((stack != nullptr) && !stack->empty()); - return stack->back().m_count; + noway_assert(m_stacks != nullptr); + StackNode* top = m_stacks[lclNum].Top(); + noway_assert(top != nullptr); + return top->m_ssaNum; } -/** - * Pushes a count value on the variable stack. - * - * @params lclNum The local variable def whose stack the count needs to be pushed onto. - * @params count The current count value that needs to be pushed on to the stack. - * - * @remarks Usually called when renaming a "def." - * Create stack lazily when needed for the first time. - */ -void SsaRenameState::Push(BasicBlock* bb, unsigned lclNum, unsigned count) +//------------------------------------------------------------------------ +// Push: Push a SSA number onto the stack for the specified variable. +// +// Arguments: +// block - The block where the SSA definition occurs +// lclNum - The local variable number +// ssaNum - The SSA number +// +void SsaRenameState::Push(BasicBlock* block, unsigned lclNum, unsigned ssaNum) { - EnsureStacks(); - - // We'll use BB00 here to indicate the "block before any real blocks..." - DBG_SSA_JITDUMP("[SsaRenameState::Push] " FMT_BB ", V%02u, count = %d\n", bb != nullptr ? bb->bbNum : 0, lclNum, - count); + DBG_SSA_JITDUMP("[SsaRenameState::Push] " FMT_BB ", V%02u, count = %d\n", block->bbNum, lclNum, ssaNum); - Stack* stack = stacks[lclNum]; + EnsureStacks(); + Push(&m_stacks[lclNum], block, ssaNum); +} - if (stack == nullptr) - { - DBG_SSA_JITDUMP("\tCreating a new stack\n"); - stack = stacks[lclNum] = new (m_alloc) Stack(m_alloc); - } +//------------------------------------------------------------------------ +// Push: Push a SSA number onto a stack +// +// Arguments: +// stack - The stack to push to +// block - The block where the SSA definition occurs +// ssaNum - The SSA number +// +void SsaRenameState::Push(Stack* stack, BasicBlock* block, unsigned ssaNum) +{ + StackNode* top = stack->Top(); - if (stack->empty() || stack->back().m_bb != bb) + if ((top == nullptr) || (top->m_block != block)) { - stack->push_back(SsaRenameStateForBlock(bb, count)); - // Remember that we've pushed a def for this loc (so we don't have - // to traverse *all* the locs to do the necessary pops later). - definedLocs.push_back(SsaRenameStateLocDef(bb, lclNum)); + stack->Push(AllocStackNode(m_stackListTail, block, ssaNum)); + // Append the stack to the stack list. The stack list allows PopBlockStacks + // to easily find stacks that need popping. + m_stackListTail = stack; } else { - stack->back().m_count = count; + // If we already have a stack node for this block then simply update + // update the SSA number, the previous one is no longer needed. + top->m_ssaNum = ssaNum; } -#ifdef DEBUG - if (JitTls::GetCompiler()->verboseSsa) - { - printf("\tContents of the stack: ["); - for (Stack::iterator iter2 = stack->begin(); iter2 != stack->end(); iter2++) - { - printf("<" FMT_BB ", %d>", ((*iter2).m_bb != nullptr ? (*iter2).m_bb->bbNum : 0), (*iter2).m_count); - } - printf("]\n"); - - DumpStacks(); - } -#endif + INDEBUG(DumpStack(stack)); } void SsaRenameState::PopBlockStacks(BasicBlock* block) { DBG_SSA_JITDUMP("[SsaRenameState::PopBlockStacks] " FMT_BB "\n", block->bbNum); - // Iterate over the stacks for all the variables, popping those that have an entry - // for "block" on top. - while (!definedLocs.empty() && definedLocs.back().m_bb == block) + + while ((m_stackListTail != nullptr) && (m_stackListTail->Top()->m_block == block)) { - unsigned lclNum = definedLocs.back().m_lclNum; - assert(stacks != nullptr); // Cannot be empty because definedLocs is not empty. - Stack* stack = stacks[lclNum]; - assert(stack != nullptr); - assert(stack->back().m_bb == block); - stack->pop_back(); - definedLocs.pop_back(); + StackNode* top = m_stackListTail->Pop(); + INDEBUG(DumpStack(m_stackListTail)); + m_stackListTail = top->m_listPrev; + m_freeStack.Push(top); } + #ifdef DEBUG - // It should now be the case that no stack in stacks has an entry for "block" on top -- - // the loop above popped them all. - for (unsigned i = 0; i < lvaCount; ++i) + if (m_stacks != nullptr) { - if (stacks != nullptr && stacks[i] != nullptr && !stacks[i]->empty()) + // It should now be the case that no stack in stacks has an entry for "block" on top -- + // the loop above popped them all. + for (unsigned i = 0; i < m_lvaCount; ++i) { - assert(stacks[i]->back().m_bb != block); + if (m_stacks[i].Top() != nullptr) + { + assert(m_stacks[i].Top()->m_block != block); + } } } - if (JitTls::GetCompiler()->verboseSsa) - { - DumpStacks(); - } #endif // DEBUG } -void SsaRenameState::PopBlockMemoryStack(MemoryKind memoryKind, BasicBlock* block) -{ - auto& stack = memoryStack[memoryKind]; - while (stack.size() > 0 && stack.back().m_bb == block) - { - stack.pop_back(); - } -} - #ifdef DEBUG -/** - * Print the stack data for each variable in a loop. - */ -void SsaRenameState::DumpStacks() +//------------------------------------------------------------------------ +// DumpStack: Print the specified stack. +// +// Arguments: +// stack - The stack to print +// +void SsaRenameState::DumpStack(Stack* stack) { - printf("Dumping stacks:\n-------------------------------\n"); - if (lvaCount == 0) - { - printf("None\n"); - } - else + if (JitTls::GetCompiler()->verboseSsa) { - EnsureStacks(); - for (unsigned i = 0; i < lvaCount; ++i) + if (stack == &m_memoryStack[ByrefExposed]) { - Stack* stack = stacks[i]; - printf("V%02u:\t", i); - if (stack != nullptr) - { - for (Stack::iterator iter2 = stack->begin(); iter2 != stack->end(); ++iter2) - { - if (iter2 != stack->begin()) - { - printf(", "); - } - printf("<" FMT_BB ", %2d>", ((*iter2).m_bb != nullptr ? (*iter2).m_bb->bbNum : 0), - (*iter2).m_count); - } - } - printf("\n"); + printf("ByrefExposed: "); + } + else if (stack == &m_memoryStack[GcHeap]) + { + printf("GcHeap: "); + } + else + { + printf("V%02u: ", stack - m_stacks); } + + for (StackNode* i = stack->Top(); i != nullptr; i = i->m_stackPrev) + { + printf("%s<" FMT_BB ", %u>", (i == stack->Top()) ? "" : ", ", i->m_block->bbNum, i->m_ssaNum); + } + + printf("\n"); } } #endif // DEBUG diff --git a/src/coreclr/src/jit/ssarenamestate.h b/src/coreclr/src/jit/ssarenamestate.h index 96abf5a..bf7dac1 100644 --- a/src/coreclr/src/jit/ssarenamestate.h +++ b/src/coreclr/src/jit/ssarenamestate.h @@ -4,144 +4,114 @@ #pragma once -#include "jitstd.h" - -// Fixed-size array that can hold elements with no default constructor; -// it will construct them all by forwarding whatever arguments are -// supplied to its constructor. -template -class ConstructedArray +class SsaRenameState { - union { - // Storage that gets used to hold the T objects. - unsigned char bytes[N * sizeof(T)]; - -#if defined(_MSC_VER) && (_MSC_VER < 1900) - // With MSVC pre-VS2015, the code in the #else branch would hit error C2621, - // so in that case just count on pointer alignment being sufficient - // (currently T is only ever instantiated as jitstd::list) - - // Unused (except to impart alignment requirement) - void* pointer; -#else - // Unused (except to impart alignment requirement) - T alignedArray[N]; -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - }; + struct StackNode; -public: - T& operator[](size_t i) + class Stack { - return *(reinterpret_cast(bytes + i * sizeof(T))); - } + StackNode* m_top; - template - ConstructedArray(Args&&... args) - { - for (int i = 0; i < N; ++i) + public: + Stack() : m_top(nullptr) { - new (bytes + i * sizeof(T), jitstd::placement_t()) T(jitstd::forward(args)...); } - } - ~ConstructedArray() - { - for (int i = 0; i < N; ++i) + StackNode* Top() { - operator[](i).~T(); + return m_top; } - } -}; -struct SsaRenameStateForBlock -{ - BasicBlock* m_bb; - unsigned m_count; - - SsaRenameStateForBlock(BasicBlock* bb, unsigned count) : m_bb(bb), m_count(count) - { - } - SsaRenameStateForBlock() : m_bb(nullptr), m_count(0) - { - } -}; + void Push(StackNode* node) + { + node->m_stackPrev = m_top; + m_top = node; + } -// A record indicating that local "m_loc" was defined in block "m_bb". -struct SsaRenameStateLocDef -{ - BasicBlock* m_bb; - unsigned m_lclNum; + StackNode* Pop() + { + StackNode* top = m_top; + m_top = top->m_stackPrev; + return top; + } + }; - SsaRenameStateLocDef(BasicBlock* bb, unsigned lclNum) : m_bb(bb), m_lclNum(lclNum) + struct StackNode { - } -}; - -struct SsaRenameState -{ - typedef jitstd::list Stack; - typedef Stack** Stacks; - typedef jitstd::list DefStack; + // Link to the previous stack top node + StackNode* m_stackPrev; + // Link to the previously pushed stack (used only when popping blocks) + Stack* m_listPrev; + // The basic block (used only when popping blocks) + BasicBlock* m_block; + // The actual information StackNode stores - the SSA number + unsigned m_ssaNum; + + StackNode(Stack* listPrev, BasicBlock* block, unsigned ssaNum) + : m_listPrev(listPrev), m_block(block), m_ssaNum(ssaNum) + { + } + }; - SsaRenameState(CompAllocator allocator, unsigned lvaCount, bool byrefStatesMatchGcHeapStates); + // Memory allocator + CompAllocator m_alloc; + // Number of local variables to allocate stacks for + unsigned m_lvaCount; + // An array of stack objects, one for each local variable + Stack* m_stacks; + // The tail of the list of stacks that have been pushed to + Stack* m_stackListTail; + // Same state for the special implicit memory variables + Stack m_memoryStack[MemoryKindCount]; + // A stack of free stack nodes + Stack m_freeStack; - void EnsureStacks(); +public: + SsaRenameState(CompAllocator alloc, unsigned lvaCount); - // Requires "lclNum" to be a variable number for which an ssa number at the top of the - // stack is required i.e., for variable "uses." - unsigned CountForUse(unsigned lclNum); + // Get the SSA number at the top of the stack for the specified variable. + unsigned Top(unsigned lclNum); - // Requires "lclNum" to be a variable number, and requires "count" to represent - // an ssa number, that needs to be pushed on to the stack corresponding to the lclNum. - void Push(BasicBlock* bb, unsigned lclNum, unsigned count); + // Push a SSA number onto the stack for the specified variable. + void Push(BasicBlock* block, unsigned lclNum, unsigned ssaNum); - // Pop all stacks that have an entry for "bb" on top. - void PopBlockStacks(BasicBlock* bb); + // Pop all stacks that have an entry for "block" on top. + void PopBlockStacks(BasicBlock* block); // Similar functions for the special implicit memory variable. - unsigned CountForMemoryUse(MemoryKind memoryKind) + unsigned TopMemory(MemoryKind memoryKind) { - if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates) - { - // Share rename stacks in this configuration. - memoryKind = ByrefExposed; - } - return memoryStack[memoryKind].back().m_count; + return m_memoryStack[memoryKind].Top()->m_ssaNum; } - void PushMemory(MemoryKind memoryKind, BasicBlock* bb, unsigned count) + void PushMemory(MemoryKind memoryKind, BasicBlock* block, unsigned ssaNum) { - if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates) - { - // Share rename stacks in this configuration. - memoryKind = ByrefExposed; - } - memoryStack[memoryKind].push_back(SsaRenameStateForBlock(bb, count)); + Push(&m_memoryStack[memoryKind], block, ssaNum); } - void PopBlockMemoryStack(MemoryKind memoryKind, BasicBlock* bb); - -#ifdef DEBUG - // Debug interface - void DumpStacks(); -#endif - private: - // Map of lclNum -> SsaRenameStateForBlock. - Stacks stacks; + void EnsureStacks(); - // This list represents the set of locals defined in the current block. - DefStack definedLocs; + // Allocate a new stack entry (possibly by popping it from the free stack) + template + StackNode* AllocStackNode(Args&&... args) + { + StackNode* stack = m_freeStack.Top(); - // Same state for the special implicit memory variables. - ConstructedArray memoryStack; + if (stack != nullptr) + { + m_freeStack.Pop(); + } + else + { + stack = m_alloc.allocate(1); + } - // Number of stacks/counts to allocate. - unsigned lvaCount; + return new (stack, jitstd::placement_t()) StackNode(jitstd::forward(args)...); + } - // Allocator to allocate stacks. - CompAllocator m_alloc; + // Push a SSA number onto a stack + void Push(Stack* stack, BasicBlock* block, unsigned ssaNum); - // Indicates whether GcHeap and ByrefExposed use the same state. - bool byrefStatesMatchGcHeapStates; + INDEBUG(void DumpStack(Stack* stack);) };