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 #include "source/opt/control_dependence.h"
16
17 #include <cassert>
18 #include <tuple>
19 #include <utility>
20 #include <vector>
21
22 #include "source/opt/basic_block.h"
23 #include "source/opt/cfg.h"
24 #include "source/opt/dominator_analysis.h"
25 #include "source/opt/function.h"
26 #include "source/opt/instruction.h"
27 #include "spirv/unified1/spirv.h"
28
29 // Computes the control dependence graph (CDG) using the algorithm in Cytron
30 // 1991, "Efficiently Computing Static Single Assignment Form and the Control
31 // Dependence Graph." It relies on the fact that the control dependence sources
32 // (blocks on which a block is control dependent) are exactly the post-dominance
33 // frontier for that block. The explanation and proofs are given in Section 6 of
34 // that paper.
35 // Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf
36 //
37 // The algorithm in Section 4.2 of the same paper is used to construct the
38 // dominance frontier. It uses the post-dominance tree, which is available in
39 // the IR context.
40
41 namespace spvtools {
42 namespace opt {
43 constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock;
44
GetConditionID(const CFG & cfg) const45 uint32_t ControlDependence::GetConditionID(const CFG& cfg) const {
46 if (source_bb_id() == 0) {
47 // Entry dependence; return 0.
48 return 0;
49 }
50 const BasicBlock* source_bb = cfg.block(source_bb_id());
51 const Instruction* branch = source_bb->terminator();
52 assert((branch->opcode() == SpvOpBranchConditional ||
53 branch->opcode() == SpvOpSwitch) &&
54 "invalid control dependence; last instruction must be conditional "
55 "branch or switch");
56 return branch->GetSingleWordInOperand(0);
57 }
58
operator <(const ControlDependence & other) const59 bool ControlDependence::operator<(const ControlDependence& other) const {
60 return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) <
61 std::tie(other.source_bb_id_, other.target_bb_id_,
62 other.branch_target_bb_id_);
63 }
64
operator ==(const ControlDependence & other) const65 bool ControlDependence::operator==(const ControlDependence& other) const {
66 return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) ==
67 std::tie(other.source_bb_id_, other.target_bb_id_,
68 other.branch_target_bb_id_);
69 }
70
operator <<(std::ostream & os,const ControlDependence & dep)71 std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) {
72 os << dep.source_bb_id() << "->" << dep.target_bb_id();
73 if (dep.branch_target_bb_id() != dep.target_bb_id()) {
74 os << " through " << dep.branch_target_bb_id();
75 }
76 return os;
77 }
78
ComputePostDominanceFrontiers(const CFG & cfg,const PostDominatorAnalysis & pdom)79 void ControlDependenceAnalysis::ComputePostDominanceFrontiers(
80 const CFG& cfg, const PostDominatorAnalysis& pdom) {
81 // Compute post-dominance frontiers (reverse graph).
82 // The dominance frontier for a block X is equal to (Equation 4)
83 // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) }
84 // (ipdom(Z) is the immediate post-dominator of Z.)
85 // where
86 // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y }
87 // represents the contribution of X's predecessors to the DF, and
88 // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y }
89 // (note: ipdom(Z) = X.)
90 // represents the contribution of a block to its immediate post-
91 // dominator's DF.
92 // This is computed in one pass through a post-order traversal of the
93 // post-dominator tree.
94
95 // Assert that there is a block other than the pseudo exit in the pdom tree,
96 // as we need one to get the function entry point (as the pseudo exit is not
97 // actually part of the function.)
98 assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_));
99 Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent();
100 uint32_t function_entry = function->entry()->id();
101 // Explicitly initialize pseudo-entry block, as it doesn't depend on anything,
102 // so it won't be initialized in the following loop.
103 reverse_nodes_[kPseudoEntryBlock] = {};
104 for (auto it = pdom.GetDomTree().post_cbegin();
105 it != pdom.GetDomTree().post_cend(); ++it) {
106 ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it);
107 }
108 }
109
ComputePostDominanceFrontierForNode(const CFG & cfg,const PostDominatorAnalysis & pdom,uint32_t function_entry,const DominatorTreeNode & pdom_node)110 void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode(
111 const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry,
112 const DominatorTreeNode& pdom_node) {
113 const uint32_t label = pdom_node.id();
114 ControlDependenceList& edges = reverse_nodes_[label];
115 for (uint32_t pred : cfg.preds(label)) {
116 if (!pdom.StrictlyDominates(label, pred)) {
117 edges.push_back(ControlDependence(pred, label));
118 }
119 }
120 if (label == function_entry) {
121 // Add edge from pseudo-entry to entry.
122 // In CDG construction, an edge is added from entry to exit, so only the
123 // exit node can post-dominate entry.
124 edges.push_back(ControlDependence(kPseudoEntryBlock, label));
125 }
126 for (DominatorTreeNode* child : pdom_node) {
127 // Note: iterate dependences by value, as we need a copy.
128 for (const ControlDependence& dep : reverse_nodes_[child->id()]) {
129 // Special-case pseudo-entry, as above.
130 if (dep.source_bb_id() == kPseudoEntryBlock ||
131 !pdom.StrictlyDominates(label, dep.source_bb_id())) {
132 edges.push_back(ControlDependence(dep.source_bb_id(), label,
133 dep.branch_target_bb_id()));
134 }
135 }
136 }
137 }
138
ComputeControlDependenceGraph(const CFG & cfg,const PostDominatorAnalysis & pdom)139 void ControlDependenceAnalysis::ComputeControlDependenceGraph(
140 const CFG& cfg, const PostDominatorAnalysis& pdom) {
141 ComputePostDominanceFrontiers(cfg, pdom);
142 ComputeForwardGraphFromReverse();
143 }
144
ComputeForwardGraphFromReverse()145 void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() {
146 for (const auto& entry : reverse_nodes_) {
147 // Ensure an entry is created for each node.
148 forward_nodes_[entry.first];
149 for (const ControlDependence& dep : entry.second) {
150 forward_nodes_[dep.source_bb_id()].push_back(dep);
151 }
152 }
153 }
154
155 } // namespace opt
156 } // namespace spvtools
157