{
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
{
// 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]);
}
// 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);
}
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)
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;
{
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();
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)
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);
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;
}
}
}
}
-void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned count)
+void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned ssaNum)
{
if (m_pCompiler->ehBlockHasExnFlowDsc(block))
{
// 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)
{
{
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
if (handlerMemoryPhi == BasicBlock::EmptyMemoryPhiDef)
{
- handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(count);
+ handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(ssaNum);
}
else
{
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)
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
{
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
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
{
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]);
}
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
assert(phiNode->gtOp.gtOp1 == nullptr || phiNode->gtOp.gtOp1->OperGet() == GT_LIST);
GenTreeArgList* argList = reinterpret_cast<GenTreeArgList*>(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;
}
/**
- * 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.
*
// 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);
}
}
// 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.
}
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);
}
}
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
#include "compiler.h"
-struct SsaRenameState;
+class SsaRenameState;
typedef int LclVarNum;
// 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);
// 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.
#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<Stack*>(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
#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 <typename T, int N>
-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<SsaRenameStateForBlock>)
-
- // 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<T*>(bytes + i * sizeof(T)));
- }
+ StackNode* m_top;
- template <typename... Args>
- 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>(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<SsaRenameStateForBlock> Stack;
- typedef Stack** Stacks;
- typedef jitstd::list<SsaRenameStateLocDef> 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 <class... Args>
+ StackNode* AllocStackNode(Args&&... args)
+ {
+ StackNode* stack = m_freeStack.Top();
- // Same state for the special implicit memory variables.
- ConstructedArray<Stack, MemoryKindCount> memoryStack;
+ if (stack != nullptr)
+ {
+ m_freeStack.Pop();
+ }
+ else
+ {
+ stack = m_alloc.allocate<StackNode>(1);
+ }
- // Number of stacks/counts to allocate.
- unsigned lvaCount;
+ return new (stack, jitstd::placement_t()) StackNode(jitstd::forward<Args>(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);)
};