summaryrefslogtreecommitdiff
path: root/source/slang/ir-dominators.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-05-31 17:20:37 -0400
committerGitHub <noreply@github.com>2019-05-31 17:20:37 -0400
commit6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch)
tree5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/ir-dominators.cpp
parentb81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff)
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge.
Diffstat (limited to 'source/slang/ir-dominators.cpp')
-rw-r--r--source/slang/ir-dominators.cpp720
1 files changed, 0 insertions, 720 deletions
diff --git a/source/slang/ir-dominators.cpp b/source/slang/ir-dominators.cpp
deleted file mode 100644
index 488e67724..000000000
--- a/source/slang/ir-dominators.cpp
+++ /dev/null
@@ -1,720 +0,0 @@
-// ir-dominators.cpp
-#include "ir-dominators.h"
-
-//
-// This file implements the public interface of the `IRDominatorTree` type,
-// to enable queries on dominance relationships in a control-flow graph.
-//
-// It also implements computation of the dominator tree for a CFG using
-// the algorithm presented in "A Simple, Fast Dominance Algorithm" by
-// Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy.
-//
-// The algorithm is *not* the most efficinet one, asymptotically, but
-// it is one that is easy to implement and explain, and so we favor it
-// in order to get something up and running with a reasonable level of
-// confidence that the results are correct.
-//
-
-#include "ir.h"
-
-namespace Slang {
-
-//
-// Let's start with the implementation of the public API for `IRDominatorTree`
-//
-
-// IRDominatorTree
-
-bool IRDominatorTree::immediatelyDominates(IRBlock* dominator, IRBlock* dominated)
-{
- // To test if block A immediately dominates block B, we just
- // check if A is the (one and only) immediate dominator of B.
- return dominator == getImmediateDominator(dominated);
-}
-
-bool IRDominatorTree::properlyDominates(IRBlock* dominator, IRBlock* dominated)
-{
- // Because of how we laid out the tree, we can test if one node
- // properly dominates another in constant time.
- //
- // We simply need to test if the node index for `dominated` falls
- // in the range of indices for the descendents of `dominator`.
- //
-
- Int dominatorIndex = getBlockIndex(dominator);
- Int dominatedIndex = getBlockIndex(dominated);
- Node& dominatorNode = nodes[dominatorIndex];
-
- return (dominatedIndex >= dominatorNode.beginDescendents)
- && (dominatedIndex < dominatorNode.endDescendents);
-}
-
-bool IRDominatorTree::dominates(IRBlock* dominator, IRBlock* dominated)
-{
- // We need to check two cases here.
- //
- // First, a node always dominated itself, so if the blocks are
- // the the same, then we are done:
- //
- if(dominator == dominated)
- return true;
- //
- // Otherwise, for distinct blocks we just check for
- // proper dominance:
- //
- return properlyDominates(dominator, dominated);
-}
-
-IRBlock* IRDominatorTree::getImmediateDominator(IRBlock* block)
-{
- // The immediate dominator of a block is its parent
- // in the dominator tree. Looking this up is straightforward,
- // and we just need to be a bit careful to deal with
- // invalid node indices.
-
- Int blockIndex = getBlockIndex(block);
- if(blockIndex == kInvalidIndex) return nullptr;
-
- Int parentIndex = nodes[blockIndex].parent;
- if(parentIndex == kInvalidIndex) return nullptr;
-
- return nodes[parentIndex].block;
-}
-
-IRDominatorTree::DominatedList IRDominatorTree::getImmediatelyDominatedBlocks(IRBlock* block)
-{
- // Because of our representation, the immediately dominated blocks
- // for a node are contiguous, and we store their range in the
- // node already.
-
- Int blockIndex = getBlockIndex(block);
- if(blockIndex == kInvalidIndex) return DominatedList();
-
- Node& node = nodes[blockIndex];
- return DominatedList(
- this,
- node.beginDescendents,
- node.endChildren);
-}
-
-IRDominatorTree::DominatedList IRDominatorTree::getProperlyDominatedBlocks(IRBlock* block)
-{
- // Because of our representation, the properly dominated blocks
- // for a node are contiguous, and we store their range in the
- // node already.
-
- Int blockIndex = getBlockIndex(block);
- if(blockIndex == kInvalidIndex) return DominatedList();
-
- Node& node = nodes[blockIndex];
- return DominatedList(
- this,
- node.beginDescendents,
- node.endDescendents);
-}
-
-Int IRDominatorTree::getBlockIndex(IRBlock* block)
-{
- Int index = kInvalidIndex;
- if(!mapBlockToIndex.TryGetValue(block, index))
- {
- SLANG_UNEXPECTED("block was not present in dominator tree");
- }
- return index;
-}
-
-// IRDominatorTree::DominatedList
-
-IRDominatorTree::DominatedList::DominatedList()
- : mTree(nullptr)
- , mBegin(0)
- , mEnd(0)
-{}
-
-IRDominatorTree::DominatedList::DominatedList(
- IRDominatorTree* tree,
- Int begin,
- Int end)
- : mTree(tree)
- , mBegin(begin)
- , mEnd(end)
-{}
-
-IRDominatorTree::DominatedList::Iterator IRDominatorTree::DominatedList::begin() const
-{
- return Iterator(mTree, mBegin);
-}
-
-IRDominatorTree::DominatedList::Iterator IRDominatorTree::DominatedList::end() const
-{
- return Iterator(mTree, mEnd);
-}
-
-
-// IRDominatorTree::DominatedList::Iterator
-
-IRDominatorTree::DominatedList::Iterator::Iterator()
- : mTree(nullptr)
- , mIndex(0)
-{}
-
-IRDominatorTree::DominatedList::Iterator::Iterator(
- IRDominatorTree* tree,
- Int index)
- : mTree(tree)
- , mIndex(index)
-{}
-
-IRBlock* IRDominatorTree::DominatedList::Iterator::operator*() const
-{
- return mTree->nodes[mIndex].block;
-}
-
-void IRDominatorTree::DominatedList::Iterator::operator++()
-{
- mIndex++;
-}
-
-bool IRDominatorTree::DominatedList::Iterator::operator==(Iterator const& that) const
-{
- SLANG_ASSERT(mTree == that.mTree);
- return mIndex == that.mIndex;
-}
-
-//
-// The dominance computation algorithm we are using relies on being able to compute
-// a reverse postorder traversal of the nodes in the CFG, which is done using a depth-first
-// search (DFS). We don't currently have infrastructure for DFS in the compiler, so
-// we will implement it here for now, and plan to move it into its own file once
-// we have a second use case.
-//
-
-/// A base "visitor" class for use in depth-first search algorithms on an IR CFG.
-struct DepthFirstSearchContext
-{
- /// The blocks in the CFG that we've already visited.
- HashSet<IRBlock*> visited;
-
- /// Walk a (previously unvisited) block.
- ///
- /// This will perform any pre-order actions on the block,
- /// then recursively visit its (unvisited) successors, and
- /// then perform any post-actions.
- ///
- void walk(IRBlock* block)
- {
- visited.Add(block);
- preVisit(block);
- for(auto succ : block->getSuccessors())
- {
- if(!visited.Contains(succ))
- {
- walk(succ);
- }
- }
- postVisit(block);
- }
-
- /// Walk the blocks in a function (or other code-bearing value).
- void walk(IRGlobalValueWithCode* code)
- {
- auto root = code->getFirstBlock();
- if(!root)
- return;
- walk(root);
- }
-
- /// Overridable action to perform on first entering a CFG node.
- virtual void preVisit(IRBlock* /*block*/) {}
-
- /// Overridable action to perform on exiting a CFG node
- virtual void postVisit(IRBlock* /*block*/) {}
-};
-
-//
-// With DFS traversal factored out, computing a post-order walk
-// of the CFG is a simple matter of defining a visitor that appends
-// to an order as a post-action:
-//
-
-/// A visitor that computes a postorder traversal for a CFG.
-struct PostorderComputationContext : public DepthFirstSearchContext
-{
- /// List to append the computed order onto
- List<IRBlock*>* order;
-
- virtual void postVisit(IRBlock* block) SLANG_OVERRIDE
- {
- order->add(block);
- }
-};
-
-/// Compute a postorder traversal of the blocks in `code`, writing the resulting order to `outOrder`.
-void computePostorder(IRGlobalValueWithCode* code, List<IRBlock*>& outOrder)
-{
- PostorderComputationContext context;
- context.order = &outOrder;
- context.walk(code);
-}
-
-//
-// With the preliminaries out of the way, we are ready to implement
-// the dominator tree construction algorithm as described by Cooper, Harvey, and Kennedy.
-// The actual code for the algorithm is given in Figure 3 of the paper.
-//
-// We will wrap the subroutines of their algorithm in a `struct` type
-// to allow the temporary structures to be shared.
-//
-struct DominatorTreeComputationContext
-{
- // We will use signed integers to represent the "name" of a block.
- // The integers will reflect the a postorder traversal, and this
- // property will be exploited in the `intersect()` function.
- //
- typedef Int BlockName;
- //
- // An invalid/undefined block name will be represented as -1.
- //
- static const BlockName kUndefined = BlockName(-1);
- //
- // We will explicitly store the blocks visited in the postorder
- // traversal, so that we can look up a block based on its "name"
- //
- List<IRBlock*> postorder;
-
- //
- // We need a way to map our actual IR blocks to their names for
- // the purpose of this algorithm. This mapping step adds overhead,
- // but it seems unavoidable unless we also translate the CFG itself
- // to an index-based representation.
- //
- Dictionary<IRBlock*, BlockName> mapBlockToName;
- BlockName getBlockName(IRBlock* block)
- {
- return mapBlockToName[block];
- }
-
- //
- // The algorithm iteratively builds up an array `doms` that upon
- // completion will directly encode the immediate dominator for each
- // node. During the iterative steps it is used to implicitly encode
- // a representation of the set of dominators for each node.
- //
- List<BlockName> doms;
-
-
- //
- // Here we get to the meat of the algorithm presented in Cooper et al.
- // Figure 3:
- //
- void iterativelyComputeImmediateDominators(IRGlobalValueWithCode* code)
- {
- // First we compute the postorder traversal order for the blocks in the CFG.
- computePostorder(code, postorder);
-
- // We will initialize our map from the block objects to their "name"
- // (index in the traversal order), before moving on.
- BlockName blockCount = BlockName(postorder.getCount());
- for(BlockName bb = 0; bb < blockCount; ++bb)
- {
- mapBlockToName[postorder[bb]] = bb;
- }
-
- // Next we initialize the `doms` array that we will iteratively turn
- // into an encoding of the dominator tree.
- doms.setCount(blockCount);
- for(BlockName bb = 0; bb < blockCount; ++bb)
- {
- doms[bb] = kUndefined;
- }
-
- // The start node is special, since it is the root of the dominator tree.
- // Technically it doesn't have an immediate dominator, but we will set
- // its entry in `doms` to refer to itself, to indicate that we are done
- // processing the given node.
- //
- BlockName startNode = getBlockName(code->getFirstBlock());
- doms[startNode] = startNode;
-
- // Given that we computed a postorder traversal of the graph, we know
- // that the start node should be the last one in the computed order.
- //
- SLANG_ASSERT(startNode == blockCount - 1);
-
- // We are using an iterative algorithm, so we will detect that we
- // have reached a fixed point when we hit an iteration where nothing
- // changes.
- //
- bool changed = true;
- while(changed)
- {
- changed = false;
-
- // The algorithm specifies that we should walk through the blocks
- // in *reverse* postorder, since this speeds up convergence.
- // Because we've numbered the blocks in postorder, walking them
- // in reverse numerical order will do the trick.
- //
- // We don't want to include the start node in our iteration
- // (since we already know its dominators), and because we know
- // that the start node is always the last in the order (`blockCount - 1`)
- // we can just start at the next node after it (`blockCount - 2`).
- //
- // Note: it is important that we are using signed integers for
- // block numbers here, since we will drop below zero before exiting
- // the loop, and if the CFG had only a single block, then our *starting*
- // block index would be `-1`.
- //
- for(auto b = blockCount - 2; b >= 0; --b)
- {
- // We are walking through block indices, but the predecessor
- // lists are encoded in the IR blocks themselves.
- //
- IRBlock* block = postorder[b];
-
- // The algorithm description in the paper says to pick the
- // initial value for the `new_idom` variable from the "first
- // (processed) predecessor of b (pick one)".
- // After that step, the algorithm walks over the remaining
- // predecessors, and for the ones that have a valid entry
- // in the `doms` array, performs an intersection of their
- // implicitly-represented dominator sets.
- //
- // The paper doesn't precisely clarify what they mean by
- // a "processed" predecessor, but it seems to mean one that
- // has a valid value in the `doms` array, which is what
- // the subsequent loop is already checking.
- //
- // We are going to fold this logic together into a single loop.
- // We will start with an invalid/undefined value for
- // `new_idom`, which represents our best guess at the
- // immediate dominator for block `b`:
- //
- BlockName new_idom = kUndefined;
-
- // Now we will loop over *all* of the predecessors, ...
- for(auto pred : block->getPredecessors())
- {
- // ... and skip those that haven't been "processed".
- BlockName p = getBlockName(pred);
- BlockName dominatorOfPredecessor = doms[p];
- if(dominatorOfPredecessor == kUndefined)
- continue;
-
- // When we encounter the first "processed" predecessor,
- // we can initialize the variable tracking our best
- // guess at the immediate dominator.
- //
- if(new_idom == kUndefined)
- {
- new_idom = p;
- }
- //
- // Otherwise, we need to merge information between
- // the predecessor `p` and our best-guess immediate
- // dominator `new_idom`. We need a node that dominates
- // both of them to be the immediate dominator of `b`.
- //
- else
- {
- new_idom = intersect(p, new_idom);
- }
- }
-
- // After we've computed a new best guess at the immediate
- // dominator for `b`, we need to see if the computed
- // value differs from what we'd previously stored in the
- // `doms` array. If anything changed, then we haven't
- // converged yet, and we need to keep going.
- //
- BlockName oldDominator = doms[b];
- if(oldDominator != new_idom)
- {
- doms[b] = new_idom;
- changed = true;
- }
- }
- }
-
- // Upon exiting the loop, things should have converged with
- // the `doms` array being an explicit encoding of the immediate
- // dominator for each node, with one small error: there is no
- // immediate dominator for the start node:
- doms[startNode] = kUndefined;
- }
-
- //
- // The algorithm above relied on a utility routine `intersect()` that
- // is implicitly used to compute intersections between sets of nodes,
- // but explicitly takes the form of a routine that computes a common
- // parent in the dominator tree for two nodes.
- //
- // We present that subroutine here, almost identical to how it
- // is presented in Cooper et al. Figure 3:
- //
- BlockName intersect(BlockName b1, BlockName b2)
- {
- // We need to find a common ancestor of both `b1` and `b2`,
- // and will do this by tracking two "fingers," each initially
- // pointing at one node, and then iteratively move the finger
- // that is furthest to the "left" (earlier in the postorder
- // traversal to the left until) to the "right" (by moving
- // the immediate dominator of the node we are pointing at),
- // until the two fingers are pointing at the same place.
- //
- // Termination is guaranteed because we are always moving the
- // fingers from a node to its immediate dominator, and the
- // entry node is guaranteed to be at the root of the dominator
- // tree.
- //
- // The use of the postorder here relies on the (subtle) fact
- // that the immediate dominator of a node must come later
- // in a postorder traversal.
- //
- BlockName finger1 = b1;
- BlockName finger2 = b2;
-
- while(finger1 != finger2)
- {
- while(finger1 < finger2)
- finger1 = doms[finger1];
- while(finger2 < finger1)
- finger2 = doms[finger2];
- }
- return finger1;
- }
-
- //
- // Now that we've implemented Cooper et al. fairly close to how
- // it was presented, we can build an array encoding the immediate
- // dominator relationship. We still need to expand that array
- // into an encoding that lets us efficiently answer queries
- // about dominance.
- //
- // In order to do that, we need to expand the information we
- // have built on each block (currently just an immediate dominator)
- // into a bit more detail:
- //
- struct BlockInfo
- {
- // How many children does this node/block have in the dominator tree?
- Int childCount = 0;
-
- // How many indirect (non-child) descendents?
- Int indirectDescendentCount = 0;
-
- // What is the 0-based offset of this node among all the children of its parent?
- Int childOffsetInParent = 0;
-
- // What is the 0-based offset for this node's descendent list,
- // among all the children in its parent?
- Int descendentOffsetInParent = 0;
-
- Int nodeIndex = 0;
- Int firstDescendentIndex = 0;
- };
- //
-
- RefPtr<IRDominatorTree> createDominatorTree(IRGlobalValueWithCode* code)
- {
- // We first run the Cooper et al. algorithm to compute the `doms` array
- // which encodes immediate dominators.
- //
- iterativelyComputeImmediateDominators(code);
-
- // We will build some intermediate information on each
- // block to help us fill out the tree.
- BlockName blockCount = BlockName(doms.getCount());
- List<BlockInfo> blockInfos;
- for(BlockName bb = 0; bb < blockCount; ++bb)
- {
- blockInfos.add(BlockInfo());
- }
-
- // We will propagate layout information in two passes over the tree.
- //
- // First we will perform a "bottom up" pass that will accumulate
- // the number of children and the total number of descendents for
- // each node, and also assign each child its relative offsets within
- // the storage for its parent.
- //
- // Because our blocks are ordered in postorder, we can do this
- // bottom-up walk just by iterating over them in the given order.
- //
- for(BlockName bb = 0; bb < blockCount; ++bb)
- {
- BlockName parent = doms[bb];
- if(parent == kUndefined)
- continue;
-
- // For our iteration order to make sense, we need to be certain
- // that parent nodes come after their child nodes in the postorder traversal.
- SLANG_ASSERT(parent > bb);
-
- // Compute the 0-based index of this child among all the children
- // with the same parent, and increment its child count.
- blockInfos[bb].childOffsetInParent = blockInfos[parent].childCount;
- blockInfos[parent].childCount++;
-
- // Our layout for the descendents of a node will put all the immediate
- // child nodes contiguously first, followed by their descendents (in contiguous blocks).
- //
- // We need to compute an offset for where the descendents of this node will
- // be stored, within the overall space carved out for the "indirect" descendents
- // of the parent node.
- //
- blockInfos[bb].descendentOffsetInParent = blockInfos[parent].indirectDescendentCount;
- //
- // When adding up the indirect descendents of `parent`, we need to include both
- // the direct and indirect descendents of our node `bb`.
- blockInfos[parent].indirectDescendentCount += blockInfos[bb].childCount
- + blockInfos[bb].indirectDescendentCount;
- }
- //
- // The next pass is a top-down pass that uses the accumulated
- // information to assign absolute indices to each node.
- //
- // For each node, we want to compute its absolute index in
- // the overall array of nodes, and then we also want to compute
- // the index where its first descendent node will be placed
- // (which can then be used by child nodes to compute their
- // index).
- //
- // The start node in the CFG is special, and will always get
- // index zero, with its first desecendent at index 1.
- //
- BlockName startBlock = getBlockName(code->getFirstBlock());
- blockInfos[startBlock].nodeIndex = 0;
- blockInfos[startBlock].firstDescendentIndex = 1;
- //
- // For the remaining nodes, we'll compute them in a top-down
- // pass (using reverse postorder).
- //
- for(BlockName bb = blockCount-1; bb >= 0; --bb)
- {
- // We will skip nodes without a parent in the dominator tree.
- // This should really only be the start node, but it might
- // happen that we have some unreachable nodes that shouldn't
- // appear in the dominator tree at all.
- //
- // TODO: make sure we either handle those correctly, or
- // else add a pass to eliminate unreachable blocks first.
- //
- BlockName parent = doms[bb];
- if(parent == kUndefined)
- continue;
-
- // The absolute index of a node is the absolute index for its
- // parent's descendent list, plus the relative offset of this
- // child node in its parent.
- //
- blockInfos[bb].nodeIndex = blockInfos[parent].firstDescendentIndex
- + blockInfos[bb].childOffsetInParent;
-
- // The other descendents of a node are always laid out in the space
- // after its immediate children. Thus, the index for where this node
- // will place its descendents (direct + indirect) must come after
- // the storage for the children of the parent.
- //
- blockInfos[bb].firstDescendentIndex = blockInfos[parent].firstDescendentIndex
- + blockInfos[parent].childCount
- + blockInfos[bb].descendentOffsetInParent;
- }
-
- // We now have all the information we need, and can start to fill in
- // the actual `IRDominatorTree` structure with the encoded information.
- //
- RefPtr<IRDominatorTree> dominatorTree = new IRDominatorTree();
- dominatorTree->code = code;
- dominatorTree->nodes.setCount(blockCount);
-
- // We will iterate over all of the blocks, and fill in the corresponding
- // dominator tree node for each.
- //
- // Note that the number of the blocks (in postorder) and the numbering
- // of the nodes (in breadth-first order) will not match, so we have
- // to be careful around whehter we are working with a block index/name,
- // or a node index.
- //
- for(BlockName bb = 0; bb < blockCount; ++bb)
- {
- // Find the IR block, look up our pre-computed information,
- // and find the corresponding node in the dominator tree.
- //
- IRBlock* block = postorder[bb];
- BlockInfo const& blockInfo = blockInfos[bb];
- Int nodeIndex = blockInfo.nodeIndex;
- IRDominatorTree::Node& node = dominatorTree->nodes[nodeIndex];
-
- // We will now start filling in the node. Filling in the block is
- // trial, and while we are at it we can add an entry to the mapping
- // from the block to the node index.
- //
- node.block = block;
- dominatorTree->mapBlockToIndex.Add(block, nodeIndex);
-
- // Filling in the parent is easy enough, just with the detail that
- // we need to handle the invalid case explicitly (for a node with
- // no parent), and need to carefully map the block index `parent`
- // over to its corresponding node index.
- //
- BlockName parent = doms[bb];
- node.parent = parent == kUndefined ? IRDominatorTree::kInvalidIndex : blockInfos[parent].nodeIndex;
-
- // Finally we need to compute the range information to use for the
- // descendents (both immediate children and indirect descendents).
- //
- // All of the relevant information was computed in our two passes
- // above, so all that has to happen here is adding together the
- // absolute start index for the descendent range with the counts
- // we accumulated.
- //
- Int beginDescendents = blockInfo.firstDescendentIndex;
- Int endChildren = beginDescendents + blockInfo.childCount;
- //
- // The indirect descendents of a node will always come after
- // its direct descenents.
- //
- Int endDescendents = endChildren + blockInfo.indirectDescendentCount;
- node.beginDescendents = beginDescendents;
- node.endChildren = endChildren;
- node.endDescendents = endDescendents;
- }
-
-#if 0
- // Let's do some ad hoc validation here, just to be sure we built the
- // data structure reasonably.
- for(BlockName ii = 0; ii < blockCount; ++ii)
- {
- for(BlockName jj = 0; jj < blockCount; ++jj)
- {
- IRBlock* i = postorder[ii];
- IRBlock* j = postorder[jj];
-
- SLANG_RELEASE_ASSERT(dominatorTree->immediatelyDominates(i, j) == (ii == doms[jj]));
-
- Int dd = jj;
- while(dd != kUndefined)
- {
- if(dd == ii)
- break;
- dd = doms[dd];
- }
- SLANG_RELEASE_ASSERT(dominatorTree->dominates(i, j) == (dd != kUndefined));
-
- }
- }
-#endif
-
- return dominatorTree;
- }
-};
-
-
-RefPtr<IRDominatorTree> computeDominatorTree(IRGlobalValueWithCode* code)
-{
- DominatorTreeComputationContext context;
- return context.createDominatorTree(code);
-}
-
-}