| // Copyright (c) 2021 Google LLC. | |
| // | |
| // 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 "source/opt/control_dependence.h" | |
| #include <cassert> | |
| #include <tuple> | |
| #include "source/opt/basic_block.h" | |
| #include "source/opt/cfg.h" | |
| #include "source/opt/dominator_analysis.h" | |
| #include "source/opt/function.h" | |
| #include "source/opt/instruction.h" | |
| // Computes the control dependence graph (CDG) using the algorithm in Cytron | |
| // 1991, "Efficiently Computing Static Single Assignment Form and the Control | |
| // Dependence Graph." It relies on the fact that the control dependence sources | |
| // (blocks on which a block is control dependent) are exactly the post-dominance | |
| // frontier for that block. The explanation and proofs are given in Section 6 of | |
| // that paper. | |
| // Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf | |
| // | |
| // The algorithm in Section 4.2 of the same paper is used to construct the | |
| // dominance frontier. It uses the post-dominance tree, which is available in | |
| // the IR context. | |
| namespace spvtools { | |
| namespace opt { | |
| constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock; | |
| uint32_t ControlDependence::GetConditionID(const CFG& cfg) const { | |
| if (source_bb_id() == 0) { | |
| // Entry dependence; return 0. | |
| return 0; | |
| } | |
| const BasicBlock* source_bb = cfg.block(source_bb_id()); | |
| const Instruction* branch = source_bb->terminator(); | |
| assert((branch->opcode() == spv::Op::OpBranchConditional || | |
| branch->opcode() == spv::Op::OpSwitch) && | |
| "invalid control dependence; last instruction must be conditional " | |
| "branch or switch"); | |
| return branch->GetSingleWordInOperand(0); | |
| } | |
| bool ControlDependence::operator<(const ControlDependence& other) const { | |
| return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) < | |
| std::tie(other.source_bb_id_, other.target_bb_id_, | |
| other.branch_target_bb_id_); | |
| } | |
| bool ControlDependence::operator==(const ControlDependence& other) const { | |
| return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) == | |
| std::tie(other.source_bb_id_, other.target_bb_id_, | |
| other.branch_target_bb_id_); | |
| } | |
| std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) { | |
| os << dep.source_bb_id() << "->" << dep.target_bb_id(); | |
| if (dep.branch_target_bb_id() != dep.target_bb_id()) { | |
| os << " through " << dep.branch_target_bb_id(); | |
| } | |
| return os; | |
| } | |
| void ControlDependenceAnalysis::ComputePostDominanceFrontiers( | |
| const CFG& cfg, const PostDominatorAnalysis& pdom) { | |
| // Compute post-dominance frontiers (reverse graph). | |
| // The dominance frontier for a block X is equal to (Equation 4) | |
| // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) } | |
| // (ipdom(Z) is the immediate post-dominator of Z.) | |
| // where | |
| // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y } | |
| // represents the contribution of X's predecessors to the DF, and | |
| // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y } | |
| // (note: ipdom(Z) = X.) | |
| // represents the contribution of a block to its immediate post- | |
| // dominator's DF. | |
| // This is computed in one pass through a post-order traversal of the | |
| // post-dominator tree. | |
| // Assert that there is a block other than the pseudo exit in the pdom tree, | |
| // as we need one to get the function entry point (as the pseudo exit is not | |
| // actually part of the function.) | |
| assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_)); | |
| Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent(); | |
| uint32_t function_entry = function->entry()->id(); | |
| // Explicitly initialize pseudo-entry block, as it doesn't depend on anything, | |
| // so it won't be initialized in the following loop. | |
| reverse_nodes_[kPseudoEntryBlock] = {}; | |
| for (auto it = pdom.GetDomTree().post_cbegin(); | |
| it != pdom.GetDomTree().post_cend(); ++it) { | |
| ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it); | |
| } | |
| } | |
| void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode( | |
| const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry, | |
| const DominatorTreeNode& pdom_node) { | |
| const uint32_t label = pdom_node.id(); | |
| ControlDependenceList& edges = reverse_nodes_[label]; | |
| for (uint32_t pred : cfg.preds(label)) { | |
| if (!pdom.StrictlyDominates(label, pred)) { | |
| edges.push_back(ControlDependence(pred, label)); | |
| } | |
| } | |
| if (label == function_entry) { | |
| // Add edge from pseudo-entry to entry. | |
| // In CDG construction, an edge is added from entry to exit, so only the | |
| // exit node can post-dominate entry. | |
| edges.push_back(ControlDependence(kPseudoEntryBlock, label)); | |
| } | |
| for (DominatorTreeNode* child : pdom_node) { | |
| // Note: iterate dependences by value, as we need a copy. | |
| for (const ControlDependence& dep : reverse_nodes_[child->id()]) { | |
| // Special-case pseudo-entry, as above. | |
| if (dep.source_bb_id() == kPseudoEntryBlock || | |
| !pdom.StrictlyDominates(label, dep.source_bb_id())) { | |
| edges.push_back(ControlDependence(dep.source_bb_id(), label, | |
| dep.branch_target_bb_id())); | |
| } | |
| } | |
| } | |
| } | |
| void ControlDependenceAnalysis::ComputeControlDependenceGraph( | |
| const CFG& cfg, const PostDominatorAnalysis& pdom) { | |
| ComputePostDominanceFrontiers(cfg, pdom); | |
| ComputeForwardGraphFromReverse(); | |
| } | |
| void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() { | |
| for (const auto& entry : reverse_nodes_) { | |
| // Ensure an entry is created for each node. | |
| forward_nodes_[entry.first]; | |
| for (const ControlDependence& dep : entry.second) { | |
| forward_nodes_[dep.source_bb_id()].push_back(dep); | |
| } | |
| } | |
| } | |
| } // namespace opt | |
| } // namespace spvtools |