diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/ir-dominators.cpp | 720 | ||||
| -rw-r--r-- | source/slang/ir-dominators.h | 162 | ||||
| -rw-r--r-- | source/slang/ir-ssa.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 2 |
5 files changed, 887 insertions, 1 deletions
diff --git a/source/slang/ir-dominators.cpp b/source/slang/ir-dominators.cpp new file mode 100644 index 000000000..3760beeca --- /dev/null +++ b/source/slang/ir-dominators.cpp @@ -0,0 +1,720 @@ +// 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, mBegin); +} + + +// 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.Count()); + 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.SetSize(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.Count()); + 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.SetSize(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); +} + +} diff --git a/source/slang/ir-dominators.h b/source/slang/ir-dominators.h new file mode 100644 index 000000000..936e9780a --- /dev/null +++ b/source/slang/ir-dominators.h @@ -0,0 +1,162 @@ +// ir-dominators.h +#pragma once + +#include "../core/basic.h" + +namespace Slang +{ + struct IRBlock; + struct IRGlobalValueWithCode; + + /// The computed dominator tree for an IR control flow graph. + struct IRDominatorTree : public RefObject + { + /// The function or other code-bearing value for which the dominator tree was computed. + IRGlobalValueWithCode* code; + + /// Does the first block dominate the second? + /// + /// A block A dominates block B iff every control-flow path + /// that starts at the entry block of the CFG and passes + /// through B must first pass through A. + /// + bool dominates(IRBlock* dominator, IRBlock* dominated); + + /// Does the first block properly dominate the second? + /// + /// Block A properly dominates block B iff A dominates B + /// and A != B. + /// + bool properlyDominates(IRBlock* dominator, IRBlock* dominated); + + /// Does the first block immediately dominate the second? + /// + /// Block A immediately dominates block B iff A dominates B + /// and for any block X that dominates B, X also dominates A. + /// + bool immediatelyDominates(IRBlock* dominator, IRBlock* dominated); + + /// Get the immediate dominator (idom) of a block. + /// + /// This is the parent of `block` in the dominator tree. + IRBlock* getImmediateDominator(IRBlock* block); + + /// An iterable collection of the blocks dominated by a specific block + struct DominatedList; + + /// Get the blocks that a block immediately dominates. + /// + /// These are the children of the block in the dominator tree. + DominatedList getImmediatelyDominatedBlocks(IRBlock* block); + + /// Get the blocks that a block properly dominates. + /// + /// These are the descendents of the block in the dominator tree. + DominatedList getProperlyDominatedBlocks(IRBlock* block); + + struct DominatedList + { + public: + DominatedList(); + + struct Iterator + { + public: + Iterator(); + + IRBlock* operator*() const; + void operator++(); + bool operator==(Iterator const& that) const; + + private: + friend struct DominatedList; + Iterator( + IRDominatorTree* tree, + Int index); + + IRDominatorTree* mTree; + Int mIndex; + }; + + Iterator begin() const; + Iterator end() const; + + private: + friend struct IRDominatorTree; + DominatedList( + IRDominatorTree* tree, + Int begin, + Int end); + + IRDominatorTree* mTree; + Int mBegin; + Int mEnd; + }; + + private: + // + // The layout of an `IRDominatorTree` uses a dense array for all of the nodes in the CFG. + // We therefore need a way to map an `IRBlock*` pointer over to an index in this array: + // + + /// Map a block to its index in the `nodes` array + Int getBlockIndex(IRBlock* block); + + /// Dictionary used to accelerate `getBlockIndex` + Dictionary<IRBlock*, Int> mapBlockToIndex; + + // + // In order to accelerate queries on the tree structure, we will order the tree nodes + // carefully, so that all of the descendants of a node are contiguous, with all of + // the immediate children coming first. + // + // Each node thus needs to remember its parent (immediate dominator), and the range + // of indices that represent children and descendents (respectively), with the knowledge + // that the first child and first descendent share the same index. + // + + /// Information about one node in the dominator tree + struct Node + { + /// The block associated with this tree node + IRBlock* block; + + /// Index of the parent node or -1 if no parent + Int parent; + + /// Index of first descendent + Int beginDescendents; + + /// "One after the end" value for range of child node indices. + Int endChildren; + + /// "One after the end" value for range of descendent node indices. + Int endDescendents; + }; + + /// Storage for the dominator tree itself + List<Node> nodes; + + /// Value to use for invalid node indices (e.g., + /// when a node has no parent). + static const Int kInvalidIndex = -1; + + // + // The `DominatedList` type needs direct access to all of this + // data in order to provide iteration. + // + friend struct DominatedList; + friend struct DominatedList::Iterator; + // + // The context type we will use to compute the dominator tree + // also needs to be able to access all the fields to initialze + // an `IRDominatorTree` + // + friend struct DominatorTreeComputationContext; + + // TODO: we should probably build/store a postdominator + // tree in the same structure, just to make life simpler. + }; + + RefPtr<IRDominatorTree> computeDominatorTree(IRGlobalValueWithCode* code); +} diff --git a/source/slang/ir-ssa.cpp b/source/slang/ir-ssa.cpp index 6c4cfa8ff..b2d336507 100644 --- a/source/slang/ir-ssa.cpp +++ b/source/slang/ir-ssa.cpp @@ -595,7 +595,7 @@ IRInst* readVarRec( // it predecessor list, and use that to decide what to do. auto predecessors = blockInfo->block->getPredecessors(); - // + // IRBlock* firstPred = nullptr; bool multiplePreds = false; for (auto pp : predecessors) diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 83a8a167c..fa75d7378 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -178,6 +178,7 @@ <ClInclude Include="emit.h" /> <ClInclude Include="expr-defs.h" /> <ClInclude Include="ir-constexpr.h" /> + <ClInclude Include="ir-dominators.h" /> <ClInclude Include="ir-inst-defs.h" /> <ClInclude Include="ir-insts.h" /> <ClInclude Include="ir-ssa.h" /> @@ -222,6 +223,7 @@ <ClCompile Include="dxc-support.cpp" /> <ClCompile Include="emit.cpp" /> <ClCompile Include="ir-constexpr.cpp" /> + <ClCompile Include="ir-dominators.cpp" /> <ClCompile Include="ir-legalize-types.cpp" /> <ClCompile Include="ir-ssa.cpp" /> <ClCompile Include="ir-validate.cpp" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 0c522b921..90b542551 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -49,6 +49,7 @@ <ClInclude Include="ir-constexpr.h" /> <ClInclude Include="type-system-shared.h" /> <ClInclude Include="ir-validate.h" /> + <ClInclude Include="ir-dominators.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="check.cpp" /> @@ -83,6 +84,7 @@ <ClCompile Include="ir-constexpr.cpp" /> <ClCompile Include="type-system-shared.cpp" /> <ClCompile Include="ir-validate.cpp" /> + <ClCompile Include="ir-dominators.cpp" /> </ItemGroup> <ItemGroup> <CustomBuild Include="core.meta.slang" /> |
