Improve dominator tree building & walking (dotnet/coreclr#27282)
authormikedn <onemihaid@hotmail.com>
Wed, 13 Nov 2019 23:50:08 +0000 (01:50 +0200)
committerBruce Forstall <brucefo@microsoft.com>
Wed, 13 Nov 2019 23:50:08 +0000 (15:50 -0800)
* Delete unused SsaBuilder code

* Delete BasicBlock::bbDfsNum

bbPostOrderNum serves the same purpose

* Improve dominator tree building & walking
- Change the dominator tree data structure to a simple array of nodes
- Adapt fgBuildDomTree so it can be used by SsaBuilder
- Add a dominator tree visitor class
- Reuse the dominator tree built by SsaBuilder for copy propagation

* Move bbNatLoopNum to fill a padding hole

Commit migrated from https://github.com/dotnet/coreclr/commit/9251ceff0395f989253b7874862feb396b7bddd3

src/coreclr/src/jit/block.h
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/copyprop.cpp
src/coreclr/src/jit/flowgraph.cpp
src/coreclr/src/jit/ssabuilder.cpp
src/coreclr/src/jit/ssabuilder.h

index a179f7d..a886249 100644 (file)
@@ -378,7 +378,6 @@ struct BasicBlock : private LIR::Range
 
     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.
 
@@ -874,6 +873,21 @@ struct BasicBlock : private LIR::Range
 #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
@@ -894,8 +908,8 @@ struct BasicBlock : private LIR::Range
     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)
@@ -995,24 +1009,6 @@ struct BasicBlock : private LIR::Range
     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
@@ -1337,20 +1333,6 @@ struct DfsBlockEntry
     }
 };
 
-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
@@ -1452,6 +1434,14 @@ public:
     }
 };
 
+// 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_
 /*****************************************************************************/
index ea97027..73db3cb 100644 (file)
@@ -4172,6 +4172,10 @@ public:
     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];
@@ -4803,21 +4807,16 @@ protected:
     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.
@@ -10330,6 +10329,84 @@ public:
     }
 };
 
+// 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
index 29ebbb6..3bacee1 100644 (file)
@@ -435,61 +435,35 @@ void Compiler::optVnCopyProp()
         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.
index f1ddad9..97b6659 100644 (file)
@@ -2395,8 +2395,8 @@ void Compiler::fgDfsInvPostOrderHelper(BasicBlock* block, BlockSet& visited, uns
 
             unsigned invCount = fgBBcount - *count + 1;
             assert(1 <= invCount && invCount <= fgBBNumMax);
-            fgBBInvPostOrder[invCount] = currentBlock;
-            currentBlock->bbDfsNum     = invCount;
+            fgBBInvPostOrder[invCount]   = currentBlock;
+            currentBlock->bbPostOrderNum = invCount;
             ++(*count);
         }
     }
@@ -2439,13 +2439,13 @@ void Compiler::fgComputeDoms()
     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;
 
@@ -2575,7 +2575,7 @@ void Compiler::fgComputeDoms()
     }
 #endif
 
-    fgBuildDomTree();
+    fgNumberDomTree(fgBuildDomTree());
 
     fgModified   = false;
     fgDomBBcount = fgBBcount;
@@ -2585,171 +2585,91 @@ void Compiler::fgComputeDoms()
     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");
         }
@@ -2759,87 +2679,83 @@ void Compiler::fgDispDomTree(BasicBlockList** domTree)
 #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
@@ -2852,11 +2768,11 @@ BasicBlock* Compiler::fgIntersectDom(BasicBlock* a, BasicBlock* b)
     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;
         }
index 9e2ada7..1313dc2 100644 (file)
@@ -107,10 +107,6 @@ void Compiler::fgResetForSsa()
             }
         }
 
-        // 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)
@@ -137,10 +133,6 @@ SsaBuilder::SsaBuilder(Compiler* pCompiler)
     , 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
 {
 }
 
@@ -242,13 +234,6 @@ void SsaBuilder::ComputeImmediateDom(BasicBlock** postOrder, int count)
 {
     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);
@@ -322,157 +307,6 @@ void SsaBuilder::ComputeImmediateDom(BasicBlock** postOrder, int count)
     }
 }
 
-#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
 //
@@ -1503,14 +1337,11 @@ void SsaBuilder::AddPhiArgsToSuccessors(BasicBlock* block)
 //------------------------------------------------------------------------
 // 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");
 
@@ -1566,59 +1397,32 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree)
         }
     }
 
-    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
@@ -1710,6 +1514,14 @@ void SsaBuilder::Build()
     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");
@@ -1718,9 +1530,7 @@ void SsaBuilder::Build()
     // 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.
@@ -1737,7 +1547,7 @@ void SsaBuilder::Build()
     InsertPhiFunctions(postOrder, count);
 
     // Rename local variables and collect UD information for each ssa var.
-    RenameVariables(domTree);
+    RenameVariables();
     EndPhase(PHASE_BUILD_SSA_RENAME);
 
 #ifdef DEBUG
index af5367f..b15c5cf 100644 (file)
@@ -5,8 +5,6 @@
 #pragma once
 #pragma warning(disable : 4503) // 'identifier' : decorated name length exceeded, name was truncated
 
-#undef SSA_FEATURE_DOMARR
-
 #include "compiler.h"
 #include "ssarenamestate.h"
 
@@ -39,13 +37,6 @@ public:
     // 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
@@ -64,29 +55,6 @@ private:
     // 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);
 
@@ -107,7 +75,7 @@ private:
     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.
@@ -141,11 +109,4 @@ private:
     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
 };