1 // Copyright (c) 2021 Google LLC. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SOURCE_OPT_CONTROL_DEPENDENCE_H_ 16 #define SOURCE_OPT_CONTROL_DEPENDENCE_H_ 17 18 #include <algorithm> 19 #include <cstdint> 20 #include <functional> 21 #include <ostream> 22 #include <unordered_map> 23 #include <vector> 24 25 #include "source/opt/cfg.h" 26 #include "source/opt/dominator_analysis.h" 27 28 namespace spvtools { 29 namespace opt { 30 31 class ControlDependence { 32 public: 33 // The label of the source of this dependence, i.e. the block on which the 34 // target is dependent on. 35 // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the 36 // execution of |target_bb_id| is only dependent on entry to the function. source_bb_id()37 uint32_t source_bb_id() const { return source_bb_id_; } 38 // The label of the target of this dependence, i.e. the block which is 39 // dependent on the source. target_bb_id()40 uint32_t target_bb_id() const { return target_bb_id_; } 41 // The label of the target of the *branch* for this dependence. 42 // Equal to the ID of the entry block for entry dependences. 43 // 44 // For example, for the partial CFG pictured below: 45 // 1 ---> 2 ---> 4 ---> 6 46 // \ \ ^ 47 // \-> 3 \-> 5 -----/ 48 // Block 6 is control dependent on block 1, but this dependence comes from the 49 // branch 1 -> 2, so in this case the branch target ID would be 2. branch_target_bb_id()50 uint32_t branch_target_bb_id() const { return branch_target_bb_id_; } 51 52 // Create a direct control dependence from BB ID |source| to |target|. ControlDependence(uint32_t source,uint32_t target)53 ControlDependence(uint32_t source, uint32_t target) 54 : source_bb_id_(source), 55 target_bb_id_(target), 56 branch_target_bb_id_(target) {} 57 // Create a control dependence from BB ID |source| to |target| through the 58 // branch from |source| to |branch_target|. ControlDependence(uint32_t source,uint32_t target,uint32_t branch_target)59 ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target) 60 : source_bb_id_(source), 61 target_bb_id_(target), 62 branch_target_bb_id_(branch_target) {} 63 64 // Gets the ID of the conditional value for the branch corresponding to this 65 // control dependence. This is the first input operand for both 66 // OpConditionalBranch and OpSwitch. 67 // Returns 0 for entry dependences. 68 uint32_t GetConditionID(const CFG& cfg) const; 69 70 bool operator==(const ControlDependence& other) const; 71 bool operator!=(const ControlDependence& other) const { 72 return !(*this == other); 73 } 74 75 // Comparison operators, ordered lexicographically. Total ordering. 76 bool operator<(const ControlDependence& other) const; 77 bool operator>(const ControlDependence& other) const { return other < *this; } 78 bool operator<=(const ControlDependence& other) const { 79 return !(*this > other); 80 } 81 bool operator>=(const ControlDependence& other) const { 82 return !(*this < other); 83 } 84 85 private: 86 uint32_t source_bb_id_; 87 uint32_t target_bb_id_; 88 uint32_t branch_target_bb_id_; 89 }; 90 91 // Prints |dep| to |os| in a human-readable way. For example, 92 // 1->2 (target_bb_id = branch_target_bb_id = 2) 93 // 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5) 94 std::ostream& operator<<(std::ostream& os, const ControlDependence& dep); 95 96 // Represents the control dependence graph. A basic block is control dependent 97 // on another if the result of that block (e.g. the condition of a conditional 98 // branch) influences whether it is executed or not. More formally, a block A is 99 // control dependent on B iff: 100 // 1. there exists a path from A to the exit node that does *not* go through B 101 // (i.e., A does not postdominate B), and 102 // 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates 103 // all nodes b_i. 104 class ControlDependenceAnalysis { 105 public: 106 // Map basic block labels to control dependencies/dependents. 107 // Not guaranteed to be in any particular order. 108 using ControlDependenceList = std::vector<ControlDependence>; 109 using ControlDependenceListMap = 110 std::unordered_map<uint32_t, ControlDependenceList>; 111 112 // 0, the label number for the pseudo entry block. 113 // All control dependences on the pseudo entry block are of type kEntry, and 114 // vice versa. 115 static constexpr uint32_t kPseudoEntryBlock = 0; 116 117 // Build the control dependence graph for the given control flow graph |cfg| 118 // and corresponding post-dominator analysis |pdom|. 119 void ComputeControlDependenceGraph(const CFG& cfg, 120 const PostDominatorAnalysis& pdom); 121 122 // Get the list of the nodes that depend on a block. 123 // Return value is not guaranteed to be in any particular order. GetDependenceTargets(uint32_t block)124 const ControlDependenceList& GetDependenceTargets(uint32_t block) const { 125 return forward_nodes_.at(block); 126 } 127 128 // Get the list of the nodes on which a block depends on. 129 // Return value is not guaranteed to be in any particular order. GetDependenceSources(uint32_t block)130 const ControlDependenceList& GetDependenceSources(uint32_t block) const { 131 return reverse_nodes_.at(block); 132 } 133 134 // Runs the function |f| on each block label in the CDG. If any iteration 135 // returns false, immediately stops iteration and returns false. Otherwise 136 // returns true. Nodes are iterated in some undefined order, including the 137 // pseudo-entry block. WhileEachBlockLabel(std::function<bool (uint32_t)> f)138 bool WhileEachBlockLabel(std::function<bool(uint32_t)> f) const { 139 for (const auto& entry : forward_nodes_) { 140 if (!f(entry.first)) { 141 return false; 142 } 143 } 144 return true; 145 } 146 147 // Runs the function |f| on each block label in the CDG. Nodes are iterated in 148 // some undefined order, including the pseudo-entry block. ForEachBlockLabel(std::function<void (uint32_t)> f)149 void ForEachBlockLabel(std::function<void(uint32_t)> f) const { 150 WhileEachBlockLabel([&f](uint32_t label) { 151 f(label); 152 return true; 153 }); 154 } 155 156 // Returns true if the block |id| exists in the control dependence graph. 157 // This can be false even if the block exists in the function when it is part 158 // of an infinite loop, since it is not part of the post-dominator tree. HasBlock(uint32_t id)159 bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; } 160 161 // Returns true if block |a| is dependent on block |b|. IsDependent(uint32_t a,uint32_t b)162 bool IsDependent(uint32_t a, uint32_t b) const { 163 if (!HasBlock(a)) return false; 164 // BBs tend to have more dependents (targets) than they are dependent on 165 // (sources), so search sources. 166 const ControlDependenceList& a_sources = GetDependenceSources(a); 167 return std::find_if(a_sources.begin(), a_sources.end(), 168 [b](const ControlDependence& dep) { 169 return dep.source_bb_id() == b; 170 }) != a_sources.end(); 171 } 172 173 private: 174 // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node 175 // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is 176 // not modified. 177 void ComputePostDominanceFrontiers(const CFG& cfg, 178 const PostDominatorAnalysis& pdom); 179 // Computes the post-dominance frontier for a specific node |pdom_node| in the 180 // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()]. 181 void ComputePostDominanceFrontierForNode(const CFG& cfg, 182 const PostDominatorAnalysis& pdom, 183 uint32_t function_entry, 184 const DominatorTreeNode& pdom_node); 185 186 // Computes the forward graph (forward_nodes_) from the reverse graph 187 // (reverse_nodes_). 188 void ComputeForwardGraphFromReverse(); 189 190 ControlDependenceListMap forward_nodes_; 191 ControlDependenceListMap reverse_nodes_; 192 }; 193 194 } // namespace opt 195 } // namespace spvtools 196 197 #endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_ 198