| // Copyright (c) 2017 Google Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <iostream> | |
| #include <memory> | |
| #include <set> | |
| #include "source/cfa.h" | |
| #include "source/opt/dominator_tree.h" | |
| #include "source/opt/ir_context.h" | |
| // Calculates the dominator or postdominator tree for a given function. | |
| // 1 - Compute the successors and predecessors for each BasicBlock. We add a | |
| // placeholder node for the start node or for postdominators the exit. This node | |
| // will point to all entry or all exit nodes. | |
| // 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of | |
| // all BasicBlocks. Using the successors (or for postdominator, predecessors) | |
| // calculated in step 1 to traverse the tree. | |
| // 3 - Pass the list calculated in step 2 to the CFA::CalculateDominators using | |
| // the predecessors list (or for postdominator, successors). This will give us a | |
| // vector of BB pairs. Each BB and its immediate dominator. | |
| // 4 - Using the list from 3 use those edges to build a tree of | |
| // DominatorTreeNodes. Each node containing a link to the parent dominator and | |
| // children which are dominated. | |
| // 5 - Using the tree from 4, perform a depth first traversal to calculate the | |
| // preorder and postorder index of each node. We use these indexes to compare | |
| // nodes against each other for domination checks. | |
| namespace spvtools { | |
| namespace opt { | |
| namespace { | |
| // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform | |
| // depth first search on generic BasicBlock types. Will call post and pre order | |
| // user defined functions during traversal | |
| // | |
| // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode | |
| // SuccessorLambda - Lamdba matching the signature of 'const | |
| // std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes | |
| // succeeding BasicBlock A. | |
| // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be | |
| // called on each node traversed AFTER their children. | |
| // PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be | |
| // called on each node traversed BEFORE their children. | |
| template <typename BBType, typename SuccessorLambda, typename PreLambda, | |
| typename PostLambda> | |
| void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, | |
| PreLambda pre, PostLambda post) { | |
| auto no_terminal_blocks = [](const BBType*) { return false; }; | |
| CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, | |
| no_terminal_blocks); | |
| } | |
| // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform | |
| // depth first search on generic BasicBlock types. This overload is for only | |
| // performing user defined post order. | |
| // | |
| // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode | |
| // SuccessorLambda - Lamdba matching the signature of 'const | |
| // std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes | |
| // succeeding BasicBlock A. | |
| // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be | |
| // called on each node traversed after their children. | |
| template <typename BBType, typename SuccessorLambda, typename PostLambda> | |
| void DepthFirstSearchPostOrder(const BBType* bb, SuccessorLambda successors, | |
| PostLambda post) { | |
| // Ignore preorder operation. | |
| auto nop_preorder = [](const BBType*) {}; | |
| DepthFirstSearch(bb, successors, nop_preorder, post); | |
| } | |
| // Small type trait to get the function class type. | |
| template <typename BBType> | |
| struct GetFunctionClass { | |
| using FunctionType = Function; | |
| }; | |
| // Helper class to compute predecessors and successors for each Basic Block in a | |
| // function. Through GetPredFunctor and GetSuccessorFunctor it provides an | |
| // interface to get the successor and predecessor lists for each basic | |
| // block. This is required by the DepthFirstTraversal and ComputeDominator | |
| // functions which take as parameter an std::function returning the successors | |
| // and predecessors respectively. | |
| // | |
| // When computing the post-dominator tree, all edges are inverted. So successors | |
| // returned by this class will be predecessors in the original CFG. | |
| template <typename BBType> | |
| class BasicBlockSuccessorHelper { | |
| // This should eventually become const BasicBlock. | |
| using BasicBlock = BBType; | |
| using Function = typename GetFunctionClass<BBType>::FunctionType; | |
| using BasicBlockListTy = std::vector<BasicBlock*>; | |
| using BasicBlockMapTy = | |
| std::unordered_map<const BasicBlock*, BasicBlockListTy>; | |
| public: | |
| // For compliance with the dominance tree computation, entry nodes are | |
| // connected to a single placeholder node. | |
| BasicBlockSuccessorHelper(Function& func, | |
| const BasicBlock* placeholder_start_node, | |
| bool post); | |
| // CFA::CalculateDominators requires std::vector<BasicBlock*>. | |
| using GetBlocksFunction = | |
| std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>; | |
| // Returns the list of predecessor functions. | |
| GetBlocksFunction GetPredFunctor() { | |
| return [this](const BasicBlock* bb) { | |
| BasicBlockListTy* v = &this->predecessors_[bb]; | |
| return v; | |
| }; | |
| } | |
| // Returns a vector of the list of successor nodes from a given node. | |
| GetBlocksFunction GetSuccessorFunctor() { | |
| return [this](const BasicBlock* bb) { | |
| BasicBlockListTy* v = &this->successors_[bb]; | |
| return v; | |
| }; | |
| } | |
| private: | |
| bool invert_graph_; | |
| BasicBlockMapTy successors_; | |
| BasicBlockMapTy predecessors_; | |
| // Build the successors and predecessors map for each basic blocks |f|. | |
| // If |invert_graph_| is true, all edges are reversed (successors becomes | |
| // predecessors and vice versa). | |
| // For convenience, the start of the graph is |placeholder_start_node|. | |
| // The dominator tree construction requires a unique entry node, which cannot | |
| // be guaranteed for the postdominator graph. The |placeholder_start_node| BB | |
| // is here to gather all entry nodes. | |
| void CreateSuccessorMap(Function& f, | |
| const BasicBlock* placeholder_start_node); | |
| }; | |
| template <typename BBType> | |
| BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper( | |
| Function& func, const BasicBlock* placeholder_start_node, bool invert) | |
| : invert_graph_(invert) { | |
| CreateSuccessorMap(func, placeholder_start_node); | |
| } | |
| template <typename BBType> | |
| void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap( | |
| Function& f, const BasicBlock* placeholder_start_node) { | |
| IRContext* context = f.DefInst().context(); | |
| if (invert_graph_) { | |
| // For the post dominator tree, we see the inverted graph. | |
| // successors_ in the inverted graph are the predecessors in the CFG. | |
| // The tree construction requires 1 entry point, so we add a placeholder | |
| // node that is connected to all function exiting basic blocks. An exiting | |
| // basic block is a block with an OpKill, OpUnreachable, OpReturn, | |
| // OpReturnValue, or OpTerminateInvocation as terminator instruction. | |
| for (BasicBlock& bb : f) { | |
| if (bb.hasSuccessor()) { | |
| BasicBlockListTy& pred_list = predecessors_[&bb]; | |
| const auto& const_bb = bb; | |
| const_bb.ForEachSuccessorLabel( | |
| [this, &pred_list, &bb, context](const uint32_t successor_id) { | |
| BasicBlock* succ = context->get_instr_block(successor_id); | |
| // Inverted graph: our successors in the CFG | |
| // are our predecessors in the inverted graph. | |
| this->successors_[succ].push_back(&bb); | |
| pred_list.push_back(succ); | |
| }); | |
| } else { | |
| successors_[placeholder_start_node].push_back(&bb); | |
| predecessors_[&bb].push_back( | |
| const_cast<BasicBlock*>(placeholder_start_node)); | |
| } | |
| } | |
| } else { | |
| successors_[placeholder_start_node].push_back(f.entry().get()); | |
| predecessors_[f.entry().get()].push_back( | |
| const_cast<BasicBlock*>(placeholder_start_node)); | |
| for (BasicBlock& bb : f) { | |
| BasicBlockListTy& succ_list = successors_[&bb]; | |
| const auto& const_bb = bb; | |
| const_bb.ForEachSuccessorLabel([&](const uint32_t successor_id) { | |
| BasicBlock* succ = context->get_instr_block(successor_id); | |
| succ_list.push_back(succ); | |
| predecessors_[succ].push_back(&bb); | |
| }); | |
| } | |
| } | |
| } | |
| } // namespace | |
| bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const { | |
| if (a == b) return false; | |
| return Dominates(a, b); | |
| } | |
| bool DominatorTree::StrictlyDominates(const BasicBlock* a, | |
| const BasicBlock* b) const { | |
| return DominatorTree::StrictlyDominates(a->id(), b->id()); | |
| } | |
| bool DominatorTree::StrictlyDominates(const DominatorTreeNode* a, | |
| const DominatorTreeNode* b) const { | |
| if (a == b) return false; | |
| return Dominates(a, b); | |
| } | |
| bool DominatorTree::Dominates(uint32_t a, uint32_t b) const { | |
| // Check that both of the inputs are actual nodes. | |
| const DominatorTreeNode* a_node = GetTreeNode(a); | |
| const DominatorTreeNode* b_node = GetTreeNode(b); | |
| if (!a_node || !b_node) return false; | |
| return Dominates(a_node, b_node); | |
| } | |
| bool DominatorTree::Dominates(const DominatorTreeNode* a, | |
| const DominatorTreeNode* b) const { | |
| if (!a || !b) return false; | |
| // Node A dominates node B if they are the same. | |
| if (a == b) return true; | |
| return a->dfs_num_pre_ < b->dfs_num_pre_ && | |
| a->dfs_num_post_ > b->dfs_num_post_; | |
| } | |
| bool DominatorTree::Dominates(const BasicBlock* A, const BasicBlock* B) const { | |
| return Dominates(A->id(), B->id()); | |
| } | |
| BasicBlock* DominatorTree::ImmediateDominator(const BasicBlock* A) const { | |
| return ImmediateDominator(A->id()); | |
| } | |
| BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const { | |
| // Check that A is a valid node in the tree. | |
| auto a_itr = nodes_.find(a); | |
| if (a_itr == nodes_.end()) return nullptr; | |
| const DominatorTreeNode* node = &a_itr->second; | |
| if (node->parent_ == nullptr) { | |
| return nullptr; | |
| } | |
| return node->parent_->bb_; | |
| } | |
| DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) { | |
| DominatorTreeNode* dtn = nullptr; | |
| std::map<uint32_t, DominatorTreeNode>::iterator node_iter = | |
| nodes_.find(bb->id()); | |
| if (node_iter == nodes_.end()) { | |
| dtn = &nodes_.emplace(std::make_pair(bb->id(), DominatorTreeNode{bb})) | |
| .first->second; | |
| } else { | |
| dtn = &node_iter->second; | |
| } | |
| return dtn; | |
| } | |
| void DominatorTree::GetDominatorEdges( | |
| const Function* f, const BasicBlock* placeholder_start_node, | |
| std::vector<std::pair<BasicBlock*, BasicBlock*>>* edges) { | |
| // Each time the depth first traversal calls the postorder callback | |
| // std::function we push that node into the postorder vector to create our | |
| // postorder list. | |
| std::vector<const BasicBlock*> postorder; | |
| auto postorder_function = [&](const BasicBlock* b) { | |
| postorder.push_back(b); | |
| }; | |
| // CFA::CalculateDominators requires std::vector<BasicBlock*> | |
| // BB are derived from F, so we need to const cast it at some point | |
| // no modification is made on F. | |
| BasicBlockSuccessorHelper<BasicBlock> helper{ | |
| *const_cast<Function*>(f), placeholder_start_node, postdominator_}; | |
| // The successor function tells DepthFirstTraversal how to move to successive | |
| // nodes by providing an interface to get a list of successor nodes from any | |
| // given node. | |
| auto successor_functor = helper.GetSuccessorFunctor(); | |
| // The predecessor functor does the same as the successor functor | |
| // but for all nodes preceding a given node. | |
| auto predecessor_functor = helper.GetPredFunctor(); | |
| // If we're building a post dominator tree we traverse the tree in reverse | |
| // using the predecessor function in place of the successor function and vice | |
| // versa. | |
| DepthFirstSearchPostOrder(placeholder_start_node, successor_functor, | |
| postorder_function); | |
| *edges = CFA<BasicBlock>::CalculateDominators(postorder, predecessor_functor); | |
| } | |
| void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) { | |
| ClearTree(); | |
| // Skip over empty functions. | |
| if (f->cbegin() == f->cend()) { | |
| return; | |
| } | |
| const BasicBlock* placeholder_start_node = | |
| postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block(); | |
| // Get the immediate dominator for each node. | |
| std::vector<std::pair<BasicBlock*, BasicBlock*>> edges; | |
| GetDominatorEdges(f, placeholder_start_node, &edges); | |
| // Transform the vector<pair> into the tree structure which we can use to | |
| // efficiently query dominance. | |
| for (auto edge : edges) { | |
| DominatorTreeNode* first = GetOrInsertNode(edge.first); | |
| if (edge.first == edge.second) { | |
| if (std::find(roots_.begin(), roots_.end(), first) == roots_.end()) | |
| roots_.push_back(first); | |
| continue; | |
| } | |
| DominatorTreeNode* second = GetOrInsertNode(edge.second); | |
| first->parent_ = second; | |
| second->children_.push_back(first); | |
| } | |
| ResetDFNumbering(); | |
| } | |
| void DominatorTree::ResetDFNumbering() { | |
| int index = 0; | |
| auto preFunc = [&index](const DominatorTreeNode* node) { | |
| const_cast<DominatorTreeNode*>(node)->dfs_num_pre_ = ++index; | |
| }; | |
| auto postFunc = [&index](const DominatorTreeNode* node) { | |
| const_cast<DominatorTreeNode*>(node)->dfs_num_post_ = ++index; | |
| }; | |
| auto getSucc = [](const DominatorTreeNode* node) { return &node->children_; }; | |
| for (auto root : roots_) DepthFirstSearch(root, getSucc, preFunc, postFunc); | |
| } | |
| void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const { | |
| out_stream << "digraph {\n"; | |
| Visit([&out_stream](const DominatorTreeNode* node) { | |
| // Print the node. | |
| if (node->bb_) { | |
| out_stream << node->bb_->id() << "[label=\"" << node->bb_->id() | |
| << "\"];\n"; | |
| } | |
| // Print the arrow from the parent to this node. Entry nodes will not have | |
| // parents so draw them as children from the placeholder node. | |
| if (node->parent_) { | |
| out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id() | |
| << ";\n"; | |
| } | |
| // Return true to continue the traversal. | |
| return true; | |
| }); | |
| out_stream << "}\n"; | |
| } | |
| } // namespace opt | |
| } // namespace spvtools |