unsigned bbNum; // the block's number
- unsigned bbPostOrderNum; // the block's post order number in the graph.
unsigned bbRefs; // number of blocks that can reach here, either by fall-through or a branch. If this falls to zero,
// the block is unreachable.
#define BBCT_FILTER_HANDLER 0xFFFFFFFF
#define handlerGetsXcptnObj(hndTyp) ((hndTyp) != BBCT_NONE && (hndTyp) != BBCT_FAULT && (hndTyp) != BBCT_FINALLY)
+ // The following fields are used for loop detection
+ typedef unsigned char loopNumber;
+ static const unsigned NOT_IN_LOOP = UCHAR_MAX;
+
+ // This is the label a loop gets as part of the second, reachability-based
+ // loop discovery mechanism. This is apparently only used for debugging.
+ // We hope we'll eventually just have one loop-discovery mechanism, and this will go away.
+ INDEBUG(loopNumber bbLoopNum;) // set to 'n' for a loop #n header
+
+ loopNumber bbNatLoopNum; // Index, in optLoopTable, of most-nested loop that contains this block,
+ // or else NOT_IN_LOOP if this block is not in a loop.
+
+#define MAX_LOOP_NUM 16 // we're using a 'short' for the mask
+#define LOOP_MASK_TP unsigned // must be big enough for a mask
+
// TODO-Cleanup: Get rid of bbStkDepth and use bbStackDepthOnEntry() instead
union {
unsigned short bbStkDepth; // stack depth on entry
BlockSet bbReach; // Set of all blocks that can reach this one
BasicBlock* bbIDom; // Represent the closest dominator to this block (called the Immediate
// Dominator) used to compute the dominance tree.
- unsigned bbDfsNum; // The index of this block in DFS reverse post order
- // relative to the flow graph.
+
+ unsigned bbPostOrderNum; // the block's post order number in the graph.
IL_OFFSET bbCodeOffs; // IL offset of the beginning of the block
IL_OFFSET bbCodeOffsEnd; // IL offset past the end of the block. Thus, the [bbCodeOffs..bbCodeOffsEnd)
verTypeVal* bbTypesOut; // list of variable types on output
#endif // VERIFIER
- /* The following fields used for loop detection */
-
- typedef unsigned char loopNumber;
- static const unsigned NOT_IN_LOOP = UCHAR_MAX;
-
-#ifdef DEBUG
- // This is the label a loop gets as part of the second, reachability-based
- // loop discovery mechanism. This is apparently only used for debugging.
- // We hope we'll eventually just have one loop-discovery mechanism, and this will go away.
- loopNumber bbLoopNum; // set to 'n' for a loop #n header
-#endif // DEBUG
-
- loopNumber bbNatLoopNum; // Index, in optLoopTable, of most-nested loop that contains this block,
- // or else NOT_IN_LOOP if this block is not in a loop.
-
-#define MAX_LOOP_NUM 16 // we're using a 'short' for the mask
-#define LOOP_MASK_TP unsigned // must be big enough for a mask
-
//-------------------------------------------------------------------------
#if MEASURE_BLOCK_SIZE
}
};
-struct DfsNumEntry
-{
- DfsStackState dfsStackState; // The pre/post traversal action for this entry
- unsigned dfsNum; // The corresponding block number for the action
-
- DfsNumEntry() : dfsStackState(DSS_Invalid), dfsNum(0)
- {
- }
-
- DfsNumEntry(DfsStackState state, unsigned bbNum) : dfsStackState(state), dfsNum(bbNum)
- {
- }
-};
-
/*****************************************************************************
*
* The following call-backs supplied by the client; it's used by the code
}
};
+// Simple dominator tree node that keeps track of a node's first child and next sibling.
+// The parent is provided by BasicBlock::bbIDom.
+struct DomTreeNode
+{
+ BasicBlock* firstChild;
+ BasicBlock* nextSibling;
+};
+
/*****************************************************************************/
#endif // _BLOCK_H_
/*****************************************************************************/
unsigned* fgDomTreePreOrder;
unsigned* fgDomTreePostOrder;
+ // Dominator tree used by SSA construction and copy propagation (the two are expected to use the same tree
+ // in order to avoid the need for SSA reconstruction and an "out of SSA" phase).
+ DomTreeNode* fgSsaDomTree;
+
bool fgBBVarSetsInited;
// Allocate array like T* a = new T[fgBBNumMax + 1];
BlockSet_ValRet_T fgDomFindStartNodes(); // Computes which basic blocks don't have incoming edges in the flow graph.
// Returns this as a set.
- BlockSet_ValRet_T fgDomTreeEntryNodes(BasicBlockList** domTree); // Computes which nodes in the dominance forest are
- // root nodes. Returns this as a set.
-
-#ifdef DEBUG
- void fgDispDomTree(BasicBlockList** domTree); // Helper that prints out the Dominator Tree in debug builds.
-#endif // DEBUG
+ INDEBUG(void fgDispDomTree(DomTreeNode* domTree);) // Helper that prints out the Dominator Tree in debug builds.
- void fgBuildDomTree(); // Once we compute all the immediate dominator sets for each node in the flow graph
- // (performed by fgComputeDoms), this procedure builds the dominance tree represented
- // adjacency lists.
+ DomTreeNode* fgBuildDomTree(); // Once we compute all the immediate dominator sets for each node in the flow graph
+ // (performed by fgComputeDoms), this procedure builds the dominance tree represented
+ // adjacency lists.
// In order to speed up the queries of the form 'Does A dominates B', we can perform a DFS preorder and postorder
// traversal of the dominance tree and the dominance query will become A dominates B iif preOrder(A) <= preOrder(B)
// && postOrder(A) >= postOrder(B) making the computation O(1).
- void fgTraverseDomTree(unsigned bbNum, BasicBlockList** domTree, unsigned* preNum, unsigned* postNum);
+ void fgNumberDomTree(DomTreeNode* domTree);
// When the flow graph changes, we need to update the block numbers, predecessor lists, reachability sets, and
// dominators.
}
};
+// A dominator tree visitor implemented using the curiosly-recurring-template pattern, similar to GenTreeVisitor.
+template <typename TVisitor>
+class DomTreeVisitor
+{
+protected:
+ Compiler* const m_compiler;
+ DomTreeNode* const m_domTree;
+
+ DomTreeVisitor(Compiler* compiler, DomTreeNode* domTree) : m_compiler(compiler), m_domTree(domTree)
+ {
+ }
+
+ void Begin()
+ {
+ }
+
+ void PreOrderVisit(BasicBlock* block)
+ {
+ }
+
+ void PostOrderVisit(BasicBlock* block)
+ {
+ }
+
+ void End()
+ {
+ }
+
+public:
+ //------------------------------------------------------------------------
+ // WalkTree: Walk the dominator tree, starting from fgFirstBB.
+ //
+ // Notes:
+ // This performs a non-recursive, non-allocating walk of the tree by using
+ // DomTreeNode's firstChild and nextSibling links to locate the children of
+ // a node and BasicBlock's bbIDom parent link to go back up the tree when
+ // no more children are left.
+ //
+ // Forests are also supported, provided that all the roots are chained via
+ // DomTreeNode::nextSibling to fgFirstBB.
+ //
+ void WalkTree()
+ {
+ static_cast<TVisitor*>(this)->Begin();
+
+ for (BasicBlock *next, *block = m_compiler->fgFirstBB; block != nullptr; block = next)
+ {
+ static_cast<TVisitor*>(this)->PreOrderVisit(block);
+
+ next = m_domTree[block->bbNum].firstChild;
+
+ if (next != nullptr)
+ {
+ assert(next->bbIDom == block);
+ continue;
+ }
+
+ do
+ {
+ static_cast<TVisitor*>(this)->PostOrderVisit(block);
+
+ next = m_domTree[block->bbNum].nextSibling;
+
+ if (next != nullptr)
+ {
+ assert(next->bbIDom == block->bbIDom);
+ break;
+ }
+
+ block = block->bbIDom;
+
+ } while (block != nullptr);
+ }
+
+ static_cast<TVisitor*>(this)->End();
+ }
+};
+
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
return;
}
- CompAllocator allocator(getAllocator(CMK_CopyProp));
-
- // Compute the domTree to use.
- BlkToBlkVectorMap* domTree = new (allocator) BlkToBlkVectorMap(allocator);
- domTree->Reallocate(fgBBcount * 3 / 2); // Prime the allocation
- SsaBuilder::ComputeDominators(this, domTree);
-
- struct BlockWork
- {
- BasicBlock* m_blk;
- bool m_processed;
-
- BlockWork(BasicBlock* blk, bool processed = false) : m_blk(blk), m_processed(processed)
- {
- }
- };
- typedef jitstd::vector<BlockWork> BlockWorkStack;
-
VarSetOps::AssignNoCopy(this, compCurLife, VarSetOps::MakeEmpty(this));
VarSetOps::AssignNoCopy(this, optCopyPropKillSet, VarSetOps::MakeEmpty(this));
- // The map from lclNum to its recently live definitions as a stack.
- LclNumToGenTreePtrStack curSsaName(allocator);
-
- BlockWorkStack* worklist = new (allocator) BlockWorkStack(allocator);
-
- worklist->push_back(BlockWork(fgFirstBB));
- while (!worklist->empty())
+ class CopyPropDomTreeVisitor : public DomTreeVisitor<CopyPropDomTreeVisitor>
{
- BlockWork work = worklist->back();
- worklist->pop_back();
+ // The map from lclNum to its recently live definitions as a stack.
+ LclNumToGenTreePtrStack m_curSsaName;
- BasicBlock* block = work.m_blk;
- if (work.m_processed)
+ public:
+ CopyPropDomTreeVisitor(Compiler* compiler)
+ : DomTreeVisitor(compiler, compiler->fgSsaDomTree), m_curSsaName(compiler->getAllocator(CMK_CopyProp))
{
- // Pop all the live definitions for this block.
- optBlockCopyPropPopStacks(block, &curSsaName);
- continue;
}
- // Generate copy assertions in this block, and keeping curSsaName variable up to date.
- worklist->push_back(BlockWork(block, true));
-
- optBlockCopyProp(block, &curSsaName);
+ void PreOrderVisit(BasicBlock* block)
+ {
+ // TODO-Cleanup: Move this function from Compiler to this class.
+ m_compiler->optBlockCopyProp(block, &m_curSsaName);
+ }
- // Add dom children to work on.
- BlkVector* domChildren = domTree->LookupPointer(block);
- if (domChildren != nullptr)
+ void PostOrderVisit(BasicBlock* block)
{
- for (BasicBlock* child : *domChildren)
- {
- worklist->push_back(BlockWork(child));
- }
+ // TODO-Cleanup: Move this function from Compiler to this class.
+ m_compiler->optBlockCopyPropPopStacks(block, &m_curSsaName);
}
- }
+ };
+
+ CopyPropDomTreeVisitor visitor(this);
+ visitor.WalkTree();
// Tracked variable count increases after CopyProp, so don't keep a shorter array around.
// Destroy (release) the varset.
unsigned invCount = fgBBcount - *count + 1;
assert(1 <= invCount && invCount <= fgBBNumMax);
- fgBBInvPostOrder[invCount] = currentBlock;
- currentBlock->bbDfsNum = invCount;
+ fgBBInvPostOrder[invCount] = currentBlock;
+ currentBlock->bbPostOrderNum = invCount;
++(*count);
}
}
flowList flRoot;
BasicBlock bbRoot;
- bbRoot.bbPreds = nullptr;
- bbRoot.bbNum = 0;
- bbRoot.bbIDom = &bbRoot;
- bbRoot.bbDfsNum = 0;
- bbRoot.bbFlags = 0;
- flRoot.flNext = nullptr;
- flRoot.flBlock = &bbRoot;
+ bbRoot.bbPreds = nullptr;
+ bbRoot.bbNum = 0;
+ bbRoot.bbIDom = &bbRoot;
+ bbRoot.bbPostOrderNum = 0;
+ bbRoot.bbFlags = 0;
+ flRoot.flNext = nullptr;
+ flRoot.flBlock = &bbRoot;
fgBBInvPostOrder[0] = &bbRoot;
}
#endif
- fgBuildDomTree();
+ fgNumberDomTree(fgBuildDomTree());
fgModified = false;
fgDomBBcount = fgBBcount;
fgDomsComputed = true;
}
-void Compiler::fgBuildDomTree()
+//------------------------------------------------------------------------
+// fgBuildDomTree: Build the dominator tree for the current flowgraph.
+//
+// Returns:
+// An array of dominator tree nodes, indexed by BasicBlock::bbNum.
+//
+// Notes:
+// Immediate dominators must have already been computed in BasicBlock::bbIDom
+// before calling this.
+//
+DomTreeNode* Compiler::fgBuildDomTree()
{
- unsigned i;
- BasicBlock* block;
-
-#ifdef DEBUG
- if (verbose)
- {
- printf("\nInside fgBuildDomTree\n");
- }
-#endif // DEBUG
-
- // domTree :: The dominance tree represented using adjacency lists. We use BasicBlockList to represent edges.
- // Indexed by basic block number.
- unsigned bbArraySize = fgBBNumMax + 1;
- BasicBlockList** domTree = new (this, CMK_DominatorMemory) BasicBlockList*[bbArraySize];
+ JITDUMP("\nInside fgBuildDomTree\n");
- fgDomTreePreOrder = new (this, CMK_DominatorMemory) unsigned[bbArraySize];
- fgDomTreePostOrder = new (this, CMK_DominatorMemory) unsigned[bbArraySize];
+ unsigned bbArraySize = fgBBNumMax + 1;
+ DomTreeNode* domTree = new (this, CMK_DominatorMemory) DomTreeNode[bbArraySize];
// Initialize all the data structures.
- for (i = 0; i < bbArraySize; ++i)
+ for (unsigned i = 0; i < bbArraySize; ++i)
{
- domTree[i] = nullptr;
- fgDomTreePreOrder[i] = fgDomTreePostOrder[i] = 0;
+ domTree[i].firstChild = nullptr;
+ domTree[i].nextSibling = nullptr;
}
- // Build the dominance tree.
- for (block = fgFirstBB; block != nullptr; block = block->bbNext)
- {
- // If the immediate dominator is not the imaginary root (bbRoot)
- // we proceed to append this block to the children of the dominator node.
- if (block->bbIDom->bbNum != 0)
- {
- int bbNum = block->bbIDom->bbNum;
- domTree[bbNum] = new (this, CMK_DominatorMemory) BasicBlockList(block, domTree[bbNum]);
- }
- else
- {
- // This means this block had bbRoot set as its IDom. We clear it out
- // and convert the tree back to a forest.
- block->bbIDom = nullptr;
- }
- }
+ BasicBlock* imaginaryRoot = fgFirstBB->bbIDom;
-#ifdef DEBUG
- if (verbose)
+ if (imaginaryRoot != nullptr)
{
- printf("\nAfter computing the Dominance Tree:\n");
- fgDispDomTree(domTree);
- }
-#endif // DEBUG
-
- // Get the bitset that represents the roots of the dominance tree.
- // Something to note here is that the dominance tree has been converted from a forest to a tree
- // by using the bbRoot trick on fgComputeDoms. The reason we have a forest instead of a real tree
- // is because we treat the EH blocks as entry nodes so the real dominance tree is not necessarily connected.
- BlockSet_ValRet_T domTreeEntryNodes = fgDomTreeEntryNodes(domTree);
+ // If the first block has a dominator then this must be the imaginary entry block added
+ // by fgComputeDoms, it is not actually part of the flowgraph and should have number 0.
+ assert(imaginaryRoot->bbNum == 0);
+ assert(imaginaryRoot->bbIDom == imaginaryRoot);
- // The preorder and postorder numbers.
- // We start from 1 to match the bbNum ordering.
- unsigned preNum = 1;
- unsigned postNum = 1;
-
- // There will be nodes in the dominance tree that will not be reachable:
- // the catch blocks that return since they don't have any predecessor.
- // For that matter we'll keep track of how many nodes we can
- // reach and assert at the end that we visited all of them.
- unsigned domTreeReachable = fgBBcount;
-
- // Once we have the dominance tree computed, we need to traverse it
- // to get the preorder and postorder numbers for each node. The purpose of
- // this is to achieve O(1) queries for of the form A dominates B.
- for (i = 1; i <= fgBBNumMax; ++i)
- {
- if (BlockSetOps::IsMember(this, domTreeEntryNodes, i))
- {
- if (domTree[i] == nullptr)
- {
- // If this is an entry node but there's no children on this
- // node, it means it's unreachable so we decrement the reachable
- // counter.
- --domTreeReachable;
- }
- else
- {
- // Otherwise, we do a DFS traversal of the dominator tree.
- fgTraverseDomTree(i, domTree, &preNum, &postNum);
- }
- }
+ // Clear the imaginary dominator to turn the tree back to a forest.
+ fgFirstBB->bbIDom = nullptr;
}
- noway_assert(preNum == domTreeReachable + 1);
- noway_assert(postNum == domTreeReachable + 1);
-
- // Once we have all the reachable nodes numbered, we proceed to
- // assign numbers to the non-reachable ones, just assign incrementing
- // values. We must reach fgBBcount at the end.
+ // If the imaginary root is present then we'll need to create a forest instead of a tree.
+ // Forest roots are chained via DomTreeNode::nextSibling and we keep track of this list's
+ // tail in order to append to it. The head of the list if fgFirstBB, by construction.
+ BasicBlock* rootListTail = fgFirstBB;
- for (i = 1; i <= fgBBNumMax; ++i)
+ // Traverse the entire block list to build the dominator tree. Skip fgFirstBB
+ // as it is always a root of the dominator forest.
+ for (BasicBlock* block = fgFirstBB->bbNext; block != nullptr; block = block->bbNext)
{
- if (BlockSetOps::IsMember(this, domTreeEntryNodes, i))
- {
- if (domTree[i] == nullptr)
- {
- fgDomTreePreOrder[i] = preNum++;
- fgDomTreePostOrder[i] = postNum++;
- }
- }
- }
+ BasicBlock* parent = block->bbIDom;
- noway_assert(preNum == fgBBNumMax + 1);
- noway_assert(postNum == fgBBNumMax + 1);
- noway_assert(fgDomTreePreOrder[0] == 0); // Unused first element
- noway_assert(fgDomTreePostOrder[0] == 0); // Unused first element
-
-#ifdef DEBUG
- if (0 && verbose)
- {
- printf("\nAfter traversing the dominance tree:\n");
- printf("PreOrder:\n");
- for (i = 1; i <= fgBBNumMax; ++i)
+ if (parent != imaginaryRoot)
{
- printf(FMT_BB " : %02u\n", i, fgDomTreePreOrder[i]);
+ assert(block->bbNum < bbArraySize);
+ assert(parent->bbNum < bbArraySize);
+
+ domTree[block->bbNum].nextSibling = domTree[parent->bbNum].firstChild;
+ domTree[parent->bbNum].firstChild = block;
}
- printf("PostOrder:\n");
- for (i = 1; i <= fgBBNumMax; ++i)
+ else if (imaginaryRoot != nullptr)
{
- printf(FMT_BB " : %02u\n", i, fgDomTreePostOrder[i]);
- }
- }
-#endif // DEBUG
-}
-
-BlockSet_ValRet_T Compiler::fgDomTreeEntryNodes(BasicBlockList** domTree)
-{
- // domTreeEntryNodes :: Set that represents which basic blocks are roots of the dominator forest.
+ assert(rootListTail->bbNum < bbArraySize);
- BlockSet domTreeEntryNodes(BlockSetOps::MakeFull(this));
+ domTree[rootListTail->bbNum].nextSibling = block;
+ rootListTail = block;
- // First of all we need to find all the roots of the dominance forest.
-
- for (unsigned i = 1; i <= fgBBNumMax; ++i)
- {
- for (BasicBlockList* current = domTree[i]; current != nullptr; current = current->next)
- {
- BlockSetOps::RemoveElemD(this, domTreeEntryNodes, current->block->bbNum);
+ // Clear the imaginary dominator to turn the tree back to a forest.
+ block->bbIDom = nullptr;
}
}
- return domTreeEntryNodes;
+ JITDUMP("\nAfter computing the Dominance Tree:\n");
+ DBEXEC(verbose, fgDispDomTree(domTree));
+
+ return domTree;
}
#ifdef DEBUG
-void Compiler::fgDispDomTree(BasicBlockList** domTree)
+void Compiler::fgDispDomTree(DomTreeNode* domTree)
{
for (unsigned i = 1; i <= fgBBNumMax; ++i)
{
- if (domTree[i] != nullptr)
+ if (domTree[i].firstChild != nullptr)
{
printf(FMT_BB " : ", i);
- for (BasicBlockList* current = domTree[i]; current != nullptr; current = current->next)
+ for (BasicBlock* child = domTree[i].firstChild; child != nullptr; child = domTree[child->bbNum].nextSibling)
{
- assert(current->block);
- printf(FMT_BB " ", current->block->bbNum);
+ printf(FMT_BB " ", child->bbNum);
}
printf("\n");
}
#endif // DEBUG
//------------------------------------------------------------------------
-// fgTraverseDomTree: Assign pre/post-order numbers to the dominator tree.
+// fgNumberDomTree: Assign pre/post-order numbers to the dominator tree.
//
// Arguments:
-// bbNum - The basic block number of the starting block
-// domTree - The dominator tree (as child block lists)
-// preNum - Pointer to the pre-number counter
-// postNum - Pointer to the post-number counter
+// domTree - The dominator tree node array
//
// Notes:
-// Runs a non-recursive DFS traversal of the dominator tree using an
-// evaluation stack to assign pre-order and post-order numbers.
-// These numberings are used to provide constant time lookup for
-// ancestor/descendent tests between pairs of nodes in the tree.
-
-void Compiler::fgTraverseDomTree(unsigned bbNum, BasicBlockList** domTree, unsigned* preNum, unsigned* postNum)
+// Runs a non-recursive DFS traversal of the dominator tree to assign
+// pre-order and post-order numbers. These numbers are used to provide
+// constant time lookup ancestor/descendent tests between pairs of nodes
+// in the tree.
+//
+void Compiler::fgNumberDomTree(DomTreeNode* domTree)
{
- noway_assert(bbNum <= fgBBNumMax);
-
- // If the block preorder number is not zero it means we already visited
- // that node, so we skip it.
- if (fgDomTreePreOrder[bbNum] == 0)
+ class NumberDomTreeVisitor : public DomTreeVisitor<NumberDomTreeVisitor>
{
- // If this is the first time we visit this node, both preorder and postnumber
- // values must be zero.
- noway_assert(fgDomTreePostOrder[bbNum] == 0);
+ unsigned m_preNum;
+ unsigned m_postNum;
- // Allocate a local stack to hold the Dfs traversal actions necessary
- // to compute pre/post-ordering of the dominator tree.
- ArrayStack<DfsNumEntry> stack(getAllocator(CMK_ArrayStack));
-
- // Push the first entry number on the stack to seed the traversal.
- stack.Push(DfsNumEntry(DSS_Pre, bbNum));
+ public:
+ NumberDomTreeVisitor(Compiler* compiler, DomTreeNode* domTree) : DomTreeVisitor(compiler, domTree)
+ {
+ }
- // The search is terminated once all the actions have been processed.
- while (!stack.Empty())
+ void Begin()
{
- DfsNumEntry current = stack.Pop();
- unsigned currentNum = current.dfsNum;
+ unsigned bbArraySize = m_compiler->fgBBNumMax + 1;
+ m_compiler->fgDomTreePreOrder = new (m_compiler, CMK_DominatorMemory) unsigned[bbArraySize];
+ m_compiler->fgDomTreePostOrder = new (m_compiler, CMK_DominatorMemory) unsigned[bbArraySize];
- if (current.dfsStackState == DSS_Pre)
+ // Initialize all the data structures.
+ for (unsigned i = 0; i < bbArraySize; ++i)
{
- // This pre-visit action corresponds to the first time the
- // node is encountered during the spanning traversal.
- noway_assert(fgDomTreePreOrder[currentNum] == 0);
- noway_assert(fgDomTreePostOrder[currentNum] == 0);
-
- // Assign the preorder number on the first visit.
- fgDomTreePreOrder[currentNum] = (*preNum)++;
+ m_compiler->fgDomTreePreOrder[i] = 0;
+ m_compiler->fgDomTreePostOrder[i] = 0;
+ }
- // Push this nodes post-action on the stack such that all successors
- // pre-order visits occur before this nodes post-action. We will assign
- // its post-order numbers when we pop off the stack.
- stack.Push(DfsNumEntry(DSS_Post, currentNum));
+ // The preorder and postorder numbers.
+ // We start from 1 to match the bbNum ordering.
+ m_preNum = 1;
+ m_postNum = 1;
+ }
- // For each child in the dominator tree process its pre-actions.
- for (BasicBlockList* child = domTree[currentNum]; child != nullptr; child = child->next)
- {
- unsigned childNum = child->block->bbNum;
+ void PreOrderVisit(BasicBlock* block)
+ {
+ m_compiler->fgDomTreePreOrder[block->bbNum] = m_preNum++;
+ }
- // This is a tree so never could have been visited
- assert(fgDomTreePreOrder[childNum] == 0);
+ void PostOrderVisit(BasicBlock* block)
+ {
+ m_compiler->fgDomTreePostOrder[block->bbNum] = m_postNum++;
+ }
- // Push the successor in the dominator tree for pre-actions.
- stack.Push(DfsNumEntry(DSS_Pre, childNum));
- }
- }
- else
- {
- // This post-visit action corresponds to the last time the node
- // is encountered and only after all descendents in the spanning
- // tree have had pre and post-order numbers assigned.
+ void End()
+ {
+ noway_assert(m_preNum == m_compiler->fgBBNumMax + 1);
+ noway_assert(m_postNum == m_compiler->fgBBNumMax + 1);
- assert(current.dfsStackState == DSS_Post);
- assert(fgDomTreePreOrder[currentNum] != 0);
- assert(fgDomTreePostOrder[currentNum] == 0);
+ noway_assert(m_compiler->fgDomTreePreOrder[0] == 0); // Unused first element
+ noway_assert(m_compiler->fgDomTreePostOrder[0] == 0); // Unused first element
+ noway_assert(m_compiler->fgDomTreePreOrder[1] == 1); // First block should be first in pre order
- // Now assign this nodes post-order number.
- fgDomTreePostOrder[currentNum] = (*postNum)++;
+#ifdef DEBUG
+ if (m_compiler->verbose)
+ {
+ printf("\nAfter numbering the dominator tree:\n");
+ for (unsigned i = 1; i <= m_compiler->fgBBNumMax; ++i)
+ {
+ printf(FMT_BB ": pre=%02u, post=%02u\n", i, m_compiler->fgDomTreePreOrder[i],
+ m_compiler->fgDomTreePostOrder[i]);
+ }
}
+#endif // DEBUG
}
- }
+ };
+
+ NumberDomTreeVisitor visitor(this, domTree);
+ visitor.WalkTree();
}
// This code finds the lowest common ancestor in the
BasicBlock* finger2 = b;
while (finger1 != finger2)
{
- while (finger1->bbDfsNum > finger2->bbDfsNum)
+ while (finger1->bbPostOrderNum > finger2->bbPostOrderNum)
{
finger1 = finger1->bbIDom;
}
- while (finger2->bbDfsNum > finger1->bbDfsNum)
+ while (finger2->bbPostOrderNum > finger1->bbPostOrderNum)
{
finger2 = finger2->bbIDom;
}
}
}
- // Clear post-order numbers and SSA numbers; SSA construction will overwrite these,
- // but only for reachable code, so clear them to avoid analysis getting confused
- // by stale annotations in unreachable code.
- blk->bbPostOrderNum = 0;
for (Statement* stmt : blk->Statements())
{
for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
, m_allocator(pCompiler->getAllocator(CMK_SSA))
, m_visitedTraits(0, pCompiler) // at this point we do not know the size, SetupBBRoot can add a block
, m_renameStack(m_allocator, pCompiler->lvaCount)
-#ifdef SSA_FEATURE_DOMARR
- , m_pDomPreOrder(nullptr)
- , m_pDomPostOrder(nullptr)
-#endif
{
}
{
JITDUMP("[SsaBuilder::ComputeImmediateDom]\n");
- // TODO-Cleanup: We currently have two dominance computations happening. We should unify them; for
- // now, at least forget the results of the first.
- for (BasicBlock* blk = m_pCompiler->fgFirstBB; blk != nullptr; blk = blk->bbNext)
- {
- blk->bbIDom = nullptr;
- }
-
// Add entry point to visited as its IDom is NULL.
BitVecOps::ClearD(&m_visitedTraits, m_visited);
BitVecOps::AddElemD(&m_visitedTraits, m_visited, m_pCompiler->fgFirstBB->bbNum);
}
}
-#ifdef SSA_FEATURE_DOMARR
-/**
- * Walk the DOM tree and compute pre and post-order arrangement of the tree.
- *
- * @param curBlock The current block being operated on at some recursive level.
- * @param domTree The DOM tree as a map (block -> set of child blocks.)
- * @param preIndex The initial index given to the first block visited in pre order.
- * @param postIndex The initial index given to the first block visited in post order.
- *
- * @remarks This would help us answer queries such as "a dom b?" in constant time.
- * For example, if a dominated b, then Pre[a] < Pre[b] but Post[a] > Post[b]
- */
-void SsaBuilder::DomTreeWalk(BasicBlock* curBlock, BlkToBlkVectorMap* domTree, int* preIndex, int* postIndex)
-{
- JITDUMP("[SsaBuilder::DomTreeWalk] block %s:\n", curBlock->dspToString());
-
- // Store the order number at the block number in the pre order list.
- m_pDomPreOrder[curBlock->bbNum] = *preIndex;
- ++(*preIndex);
-
- BlkVector* domChildren = domTree->LookupPointer(curBlock);
- if (domChildren != nullptr)
- {
- for (BasicBlock* child : *domChildren)
- {
- if (curBlock != child)
- {
- DomTreeWalk(child, domTree, preIndex, postIndex);
- }
- }
- }
-
- // Store the order number at the block number in the post order list.
- m_pDomPostOrder[curBlock->bbNum] = *postIndex;
- ++(*postIndex);
-}
-#endif
-
-/**
- * Using IDom of each basic block, add a mapping from block->IDom -> block.
- * @param pCompiler Compiler instance
- * @param block The basic block that will become the child node of it's iDom.
- * @param domTree The output domTree which will hold the mapping "block->bbIDom" -> "block"
- *
- */
-/* static */
-void SsaBuilder::ConstructDomTreeForBlock(Compiler* pCompiler, BasicBlock* block, BlkToBlkVectorMap* domTree)
-{
- BasicBlock* bbIDom = block->bbIDom;
-
- // bbIDom for (only) fgFirstBB will be NULL.
- if (bbIDom == nullptr)
- {
- return;
- }
-
- // If the bbIDom map key doesn't exist, create one.
- BlkVector* domChildren = domTree->Emplace(bbIDom, domTree->GetAllocator());
-
- DBG_SSA_JITDUMP("Inserting " FMT_BB " as dom child of " FMT_BB ".\n", block->bbNum, bbIDom->bbNum);
- // Insert the block into the block's set.
- domChildren->push_back(block);
-}
-
-/**
- * Using IDom of each basic block, compute the whole tree. If a block "b" has IDom "i",
- * then, block "b" is dominated by "i". The mapping then is i -> { ..., b, ... }, in
- * other words, "domTree" is a tree represented by nodes mapped to their children.
- *
- * @param pCompiler Compiler instance
- * @param domTree The output domTree which will hold the mapping "block->bbIDom" -> "block"
- *
- */
-/* static */
-void SsaBuilder::ComputeDominators(Compiler* pCompiler, BlkToBlkVectorMap* domTree)
-{
- JITDUMP("*************** In SsaBuilder::ComputeDominators(Compiler*, ...)\n");
-
- // Construct the DOM tree from bbIDom
- for (BasicBlock* block = pCompiler->fgFirstBB; block != nullptr; block = block->bbNext)
- {
- ConstructDomTreeForBlock(pCompiler, block, domTree);
- }
-
- DBEXEC(pCompiler->verboseSsa, DisplayDominators(domTree));
-}
-
-/**
- * Compute the DOM tree into a map(block -> set of blocks) adjacency representation.
- *
- * Using IDom of each basic block, compute the whole tree. If a block "b" has IDom "i",
- * then, block "b" is dominated by "i". The mapping then is i -> { ..., b, ... }
- *
- * @param postOrder The array of basic blocks arranged in postOrder.
- * @param count The size of valid elements in the postOrder array.
- * @param domTree A map of (block -> set of blocks) tree representation that is empty.
- *
- */
-void SsaBuilder::ComputeDominators(BasicBlock** postOrder, int count, BlkToBlkVectorMap* domTree)
-{
- JITDUMP("*************** In SsaBuilder::ComputeDominators(BasicBlock** postOrder, int count, ...)\n");
-
- // Construct the DOM tree from bbIDom
- for (int i = 0; i < count; ++i)
- {
- ConstructDomTreeForBlock(m_pCompiler, postOrder[i], domTree);
- }
-
- DBEXEC(m_pCompiler->verboseSsa, DisplayDominators(domTree));
-
-#ifdef SSA_FEATURE_DOMARR
- // Allocate space for constant time computation of (a DOM b?) query.
- unsigned bbArrSize = m_pCompiler->fgBBNumMax + 1; // We will use 1-based bbNums as indices into these arrays, so
- // add 1.
- m_pDomPreOrder = new (&m_allocator) int[bbArrSize];
- m_pDomPostOrder = new (&m_allocator) int[bbArrSize];
-
- // Initial counters.
- int preIndex = 0;
- int postIndex = 0;
-
- // Populate the pre and post order of the tree.
- DomTreeWalk(m_pCompiler->fgFirstBB, domTree, &preIndex, &postIndex);
-#endif
-}
-
-#ifdef DEBUG
-
-/**
- * Display the DOM tree.
- *
- * @param domTree A map of (block -> set of blocks) tree representation.
- */
-/* static */
-void SsaBuilder::DisplayDominators(BlkToBlkVectorMap* domTree)
-{
- printf("After computing dominator tree: \n");
- for (BlkToBlkVectorMap::KeyIterator nodes = domTree->Begin(); !nodes.Equal(domTree->End()); ++nodes)
- {
- printf(FMT_BB " := {", nodes.Get()->bbNum);
- int index = 0;
- for (BasicBlock* child : nodes.GetValue())
- {
- printf("%s" FMT_BB, (index++ == 0) ? "" : ",", child->bbNum);
- }
- printf("}\n");
- }
-}
-
-#endif // DEBUG
-
//------------------------------------------------------------------------
// ComputeDominanceFrontiers: Compute flow graph dominance frontiers
//
//------------------------------------------------------------------------
// RenameVariables: Rename all definitions and uses within the compiled method.
//
-// Arguments:
-// domTree - The dominator tree
-//
// Notes:
// See Briggs, Cooper, Harvey and Simpson "Practical Improvements to the Construction
// and Destruction of Static Single Assignment Form."
//
-void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree)
+void SsaBuilder::RenameVariables()
{
JITDUMP("*************** In SsaBuilder::RenameVariables()\n");
}
}
- struct BlockWork
+ class SsaRenameDomTreeVisitor : public DomTreeVisitor<SsaRenameDomTreeVisitor>
{
- BasicBlock* m_blk;
- bool m_processed; // Whether the this block have already been processed: its var renamed, and children
- // processed.
- // If so, awaiting only BlockPopStacks.
- BlockWork(BasicBlock* blk, bool processed = false) : m_blk(blk), m_processed(processed)
+ SsaBuilder* m_builder;
+ SsaRenameState* m_renameStack;
+
+ public:
+ SsaRenameDomTreeVisitor(Compiler* compiler, SsaBuilder* builder, SsaRenameState* renameStack)
+ : DomTreeVisitor(compiler, compiler->fgSsaDomTree), m_builder(builder), m_renameStack(renameStack)
{
}
- };
- typedef jitstd::vector<BlockWork> BlockWorkStack;
-
- BlockWorkStack* blocksToDo = new (m_allocator) BlockWorkStack(m_allocator);
- blocksToDo->push_back(BlockWork(m_pCompiler->fgFirstBB)); // Probably have to include other roots of dom tree.
- while (blocksToDo->size() != 0)
- {
- BlockWork blockWrk = blocksToDo->back();
- blocksToDo->pop_back();
- BasicBlock* block = blockWrk.m_blk;
-
- DBG_SSA_JITDUMP("[SsaBuilder::RenameVariables](" FMT_BB ", processed = %d)\n", block->bbNum,
- blockWrk.m_processed);
-
- if (!blockWrk.m_processed)
+ void PreOrderVisit(BasicBlock* block)
{
- // Push the block back on the stack with "m_processed" true, to record the fact that when its children have
- // been (recursively) processed, we still need to call BlockPopStacks on it.
- blocksToDo->push_back(BlockWork(block, true));
-
- BlockRenameVariables(block);
-
- // Add arguments to PHI nodes in block's successors.
- AddPhiArgsToSuccessors(block);
-
- // Recurse with the block's DOM children.
- BlkVector* domChildren = domTree->LookupPointer(block);
- if (domChildren != nullptr)
- {
- for (BasicBlock* child : *domChildren)
- {
- DBG_SSA_JITDUMP("[SsaBuilder::RenameVariables](pushing dom child " FMT_BB ")\n", child->bbNum);
- blocksToDo->push_back(BlockWork(child));
- }
- }
+ // TODO-Cleanup: Move these functions from SsaBuilder to this class.
+ m_builder->BlockRenameVariables(block);
+ m_builder->AddPhiArgsToSuccessors(block);
}
- else
+
+ void PostOrderVisit(BasicBlock* block)
{
- // Done, pop all SSA numbers pushed in this block.
- m_renameStack.PopBlockStacks(block);
- DBG_SSA_JITDUMP("[SsaBuilder::RenameVariables] done with " FMT_BB "\n", block->bbNum);
+ m_renameStack->PopBlockStacks(block);
}
- }
+ };
+
+ SsaRenameDomTreeVisitor visitor(m_pCompiler, this, &m_renameStack);
+ visitor.WalkTree();
}
#ifdef DEBUG
m_visitedTraits = BitVecTraits(blockCount, m_pCompiler);
m_visited = BitVecOps::MakeEmpty(&m_visitedTraits);
+ // TODO-Cleanup: We currently have two dominance computations happening. We should unify them; for
+ // now, at least forget the results of the first.
+ for (BasicBlock* block = m_pCompiler->fgFirstBB; block != nullptr; block = block->bbNext)
+ {
+ block->bbIDom = nullptr;
+ block->bbPostOrderNum = 0;
+ }
+
// Topologically sort the graph.
int count = TopologicalSort(postOrder, blockCount);
JITDUMP("[SsaBuilder] Topologically sorted the graph.\n");
// Compute IDom(b).
ComputeImmediateDom(postOrder, count);
- // Compute the dominator tree.
- BlkToBlkVectorMap* domTree = new (m_allocator) BlkToBlkVectorMap(m_allocator);
- ComputeDominators(postOrder, count, domTree);
+ m_pCompiler->fgSsaDomTree = m_pCompiler->fgBuildDomTree();
EndPhase(PHASE_BUILD_SSA_DOMS);
// Compute liveness on the graph.
InsertPhiFunctions(postOrder, count);
// Rename local variables and collect UD information for each ssa var.
- RenameVariables(domTree);
+ RenameVariables();
EndPhase(PHASE_BUILD_SSA_RENAME);
#ifdef DEBUG
#pragma once
#pragma warning(disable : 4503) // 'identifier' : decorated name length exceeded, name was truncated
-#undef SSA_FEATURE_DOMARR
-
#include "compiler.h"
#include "ssarenamestate.h"
// def of an SSA variable can be looked up similarly using m_defs member.
void Build();
- // Requires "bbIDom" of each block to be computed. Requires "domTree" to be allocated
- // and can be updated, i.e., by adding mapping from a block to it's dominated children.
- // Using IDom of each basic block, compute the whole domTree. If a block "b" has IDom "i",
- // then, block "b" is dominated by "i". The mapping then is i -> { ..., b, ... }, in
- // other words, "domTree" is a tree represented by nodes mapped to their children.
- static void ComputeDominators(Compiler* pCompiler, BlkToBlkVectorMap* domTree);
-
private:
// Ensures that the basic block graph has a root for the dominator graph, by ensuring
// that there is a first block that is not in a try region (adding an empty block for that purpose
// each block's immediate dominator and records it in the BasicBlock in bbIDom.
void ComputeImmediateDom(BasicBlock** postOrder, int count);
-#ifdef SSA_FEATURE_DOMARR
- // Requires "curBlock" to be the first basic block at the first step of the recursion.
- // Requires "domTree" to be a adjacency list (actually, a set of blocks with a set of blocks
- // as children.) Requires "preIndex" and "postIndex" to be initialized to 0 at entry into recursion.
- // Computes arrays "m_pDomPreOrder" and "m_pDomPostOrder" of block indices such that the blocks of a
- // "domTree" are in pre and postorder respectively.
- void DomTreeWalk(BasicBlock* curBlock, BlkToBlkVectorMap* domTree, int* preIndex, int* postIndex);
-#endif
-
- // Requires all blocks to have computed "bbIDom." Requires "domTree" to be a preallocated BlkToBlkVectorMap.
- // Helper to compute "domTree" from the pre-computed bbIDom of the basic blocks.
- static void ConstructDomTreeForBlock(Compiler* pCompiler, BasicBlock* block, BlkToBlkVectorMap* domTree);
-
- // Requires "postOrder" to hold the blocks of the flowgraph in topologically sorted order. Requires
- // count to be the valid entries in the "postOrder" array. Computes "domTree" as a adjacency list
- // like object, i.e., a set of blocks with a set of blocks as children defining the DOM relation.
- void ComputeDominators(BasicBlock** postOrder, int count, BlkToBlkVectorMap* domTree);
-
-#ifdef DEBUG
- // Display the dominator tree.
- static void DisplayDominators(BlkToBlkVectorMap* domTree);
-#endif // DEBUG
-
// Compute flow graph dominance frontiers.
void ComputeDominanceFrontiers(BasicBlock** postOrder, int count, BlkToBlkVectorMap* mapDF);
void InsertPhiFunctions(BasicBlock** postOrder, int count);
// Rename all definitions and uses within the compiled method.
- void RenameVariables(BlkToBlkVectorMap* domTree);
+ void RenameVariables();
// Rename all definitions and uses within a block.
void BlockRenameVariables(BasicBlock* block);
// Rename a local or memory definition generated by a GT_ASG node.
BitVec m_visited;
SsaRenameState m_renameStack;
-
-#ifdef SSA_FEATURE_DOMARR
- // To answer queries of type a DOM b.
- // Do not move these outside of this class, use accessors/interface methods.
- int* m_pDomPreOrder;
- int* m_pDomPostOrder;
-#endif
};