• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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