• 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 #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