1 | // Copyright (c) 2017 Google Inc. |
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/propagator.h" |
16 | |
17 | namespace spvtools { |
18 | namespace opt { |
19 | |
20 | void SSAPropagator::AddControlEdge(const Edge& edge) { |
21 | BasicBlock* dest_bb = edge.dest; |
22 | |
23 | // Refuse to add the exit block to the work list. |
24 | if (dest_bb == ctx_->cfg()->pseudo_exit_block()) { |
25 | return; |
26 | } |
27 | |
28 | // Try to mark the edge executable. If it was already in the set of |
29 | // executable edges, do nothing. |
30 | if (!MarkEdgeExecutable(edge)) { |
31 | return; |
32 | } |
33 | |
34 | // If the edge had not already been marked executable, add the destination |
35 | // basic block to the work list. |
36 | blocks_.push(dest_bb); |
37 | } |
38 | |
39 | void SSAPropagator::AddSSAEdges(Instruction* instr) { |
40 | // Ignore instructions that produce no result. |
41 | if (instr->result_id() == 0) { |
42 | return; |
43 | } |
44 | |
45 | get_def_use_mgr()->ForEachUser( |
46 | instr->result_id(), [this](Instruction* use_instr) { |
47 | // If the basic block for |use_instr| has not been simulated yet, do |
48 | // nothing. The instruction |use_instr| will be simulated next time the |
49 | // block is scheduled. |
50 | if (!BlockHasBeenSimulated(ctx_->get_instr_block(use_instr))) { |
51 | return; |
52 | } |
53 | |
54 | if (ShouldSimulateAgain(use_instr)) { |
55 | ssa_edge_uses_.push(use_instr); |
56 | } |
57 | }); |
58 | } |
59 | |
60 | bool SSAPropagator::IsPhiArgExecutable(Instruction* phi, uint32_t i) const { |
61 | BasicBlock* phi_bb = ctx_->get_instr_block(phi); |
62 | |
63 | uint32_t in_label_id = phi->GetSingleWordOperand(i + 1); |
64 | Instruction* in_label_instr = get_def_use_mgr()->GetDef(in_label_id); |
65 | BasicBlock* in_bb = ctx_->get_instr_block(in_label_instr); |
66 | |
67 | return IsEdgeExecutable(Edge(in_bb, phi_bb)); |
68 | } |
69 | |
70 | bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) { |
71 | bool has_old_status = false; |
72 | PropStatus old_status = kVarying; |
73 | if (HasStatus(inst)) { |
74 | has_old_status = true; |
75 | old_status = Status(inst); |
76 | } |
77 | |
78 | assert((!has_old_status || old_status <= status) && |
79 | "Invalid lattice transition" ); |
80 | |
81 | bool status_changed = !has_old_status || (old_status != status); |
82 | if (status_changed) statuses_[inst] = status; |
83 | |
84 | return status_changed; |
85 | } |
86 | |
87 | bool SSAPropagator::Simulate(Instruction* instr) { |
88 | bool changed = false; |
89 | |
90 | // Don't bother visiting instructions that should not be simulated again. |
91 | if (!ShouldSimulateAgain(instr)) { |
92 | return changed; |
93 | } |
94 | |
95 | BasicBlock* dest_bb = nullptr; |
96 | PropStatus status = visit_fn_(instr, &dest_bb); |
97 | bool status_changed = SetStatus(instr, status); |
98 | |
99 | if (status == kVarying) { |
100 | // The statement produces a varying result, add it to the list of statements |
101 | // not to simulate anymore and add its SSA def-use edges for simulation. |
102 | DontSimulateAgain(instr); |
103 | if (status_changed) { |
104 | AddSSAEdges(instr); |
105 | } |
106 | |
107 | // If |instr| is a block terminator, add all the control edges out of its |
108 | // block. |
109 | if (instr->IsBlockTerminator()) { |
110 | BasicBlock* block = ctx_->get_instr_block(instr); |
111 | for (const auto& e : bb_succs_.at(block)) { |
112 | AddControlEdge(e); |
113 | } |
114 | } |
115 | return false; |
116 | } else if (status == kInteresting) { |
117 | // Add the SSA edges coming out of this instruction if the propagation |
118 | // status has changed. |
119 | if (status_changed) { |
120 | AddSSAEdges(instr); |
121 | } |
122 | |
123 | // If there are multiple outgoing control flow edges and we know which one |
124 | // will be taken, add the destination block to the CFG work list. |
125 | if (dest_bb) { |
126 | AddControlEdge(Edge(ctx_->get_instr_block(instr), dest_bb)); |
127 | } |
128 | changed = true; |
129 | } |
130 | |
131 | // At this point, we are dealing with instructions that are in status |
132 | // kInteresting or kNotInteresting. To decide whether this instruction should |
133 | // be simulated again, we examine its operands. If at least one operand O is |
134 | // defined at an instruction D that should be simulated again, then the output |
135 | // of D might affect |instr|, so we should simulate |instr| again. |
136 | bool has_operands_to_simulate = false; |
137 | if (instr->opcode() == SpvOpPhi) { |
138 | // For Phi instructions, an operand causes the Phi to be simulated again if |
139 | // the operand comes from an edge that has not yet been traversed or if its |
140 | // definition should be simulated again. |
141 | for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { |
142 | // Phi arguments come in pairs. Index 'i' contains the |
143 | // variable id, index 'i + 1' is the originating block id. |
144 | assert(i % 2 == 0 && i < instr->NumOperands() - 1 && |
145 | "malformed Phi arguments" ); |
146 | |
147 | uint32_t arg_id = instr->GetSingleWordOperand(i); |
148 | Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id); |
149 | if (!IsPhiArgExecutable(instr, i) || ShouldSimulateAgain(arg_def_instr)) { |
150 | has_operands_to_simulate = true; |
151 | break; |
152 | } |
153 | } |
154 | } else { |
155 | // For regular instructions, check if the defining instruction of each |
156 | // operand needs to be simulated again. If so, then this instruction should |
157 | // also be simulated again. |
158 | has_operands_to_simulate = |
159 | !instr->WhileEachInId([this](const uint32_t* use) { |
160 | Instruction* def_instr = get_def_use_mgr()->GetDef(*use); |
161 | if (ShouldSimulateAgain(def_instr)) { |
162 | return false; |
163 | } |
164 | return true; |
165 | }); |
166 | } |
167 | |
168 | if (!has_operands_to_simulate) { |
169 | DontSimulateAgain(instr); |
170 | } |
171 | |
172 | return changed; |
173 | } |
174 | |
175 | bool SSAPropagator::Simulate(BasicBlock* block) { |
176 | if (block == ctx_->cfg()->pseudo_exit_block()) { |
177 | return false; |
178 | } |
179 | |
180 | // Always simulate Phi instructions, even if we have simulated this block |
181 | // before. We do this because Phi instructions receive their inputs from |
182 | // incoming edges. When those edges are marked executable, the corresponding |
183 | // operand can be simulated. |
184 | bool changed = false; |
185 | block->ForEachPhiInst( |
186 | [&changed, this](Instruction* instr) { changed |= Simulate(instr); }); |
187 | |
188 | // If this is the first time this block is being simulated, simulate every |
189 | // statement in it. |
190 | if (!BlockHasBeenSimulated(block)) { |
191 | block->ForEachInst([this, &changed](Instruction* instr) { |
192 | if (instr->opcode() != SpvOpPhi) { |
193 | changed |= Simulate(instr); |
194 | } |
195 | }); |
196 | |
197 | MarkBlockSimulated(block); |
198 | |
199 | // If this block has exactly one successor, mark the edge to its successor |
200 | // as executable. |
201 | if (bb_succs_.at(block).size() == 1) { |
202 | AddControlEdge(bb_succs_.at(block).at(0)); |
203 | } |
204 | } |
205 | |
206 | return changed; |
207 | } |
208 | |
209 | void SSAPropagator::Initialize(Function* fn) { |
210 | // Compute predecessor and successor blocks for every block in |fn|'s CFG. |
211 | // TODO(dnovillo): Move this to CFG and always build them. Alternately, |
212 | // move it to IRContext and build CFG preds/succs on-demand. |
213 | bb_succs_[ctx_->cfg()->pseudo_entry_block()].push_back( |
214 | Edge(ctx_->cfg()->pseudo_entry_block(), fn->entry().get())); |
215 | |
216 | for (auto& block : *fn) { |
217 | const auto& const_block = block; |
218 | const_block.ForEachSuccessorLabel([this, &block](const uint32_t label_id) { |
219 | BasicBlock* succ_bb = |
220 | ctx_->get_instr_block(get_def_use_mgr()->GetDef(label_id)); |
221 | bb_succs_[&block].push_back(Edge(&block, succ_bb)); |
222 | bb_preds_[succ_bb].push_back(Edge(succ_bb, &block)); |
223 | }); |
224 | if (block.IsReturnOrAbort()) { |
225 | bb_succs_[&block].push_back( |
226 | Edge(&block, ctx_->cfg()->pseudo_exit_block())); |
227 | bb_preds_[ctx_->cfg()->pseudo_exit_block()].push_back( |
228 | Edge(ctx_->cfg()->pseudo_exit_block(), &block)); |
229 | } |
230 | } |
231 | |
232 | // Add the edges out of the entry block to seed the propagator. |
233 | const auto& entry_succs = bb_succs_[ctx_->cfg()->pseudo_entry_block()]; |
234 | for (const auto& e : entry_succs) { |
235 | AddControlEdge(e); |
236 | } |
237 | } |
238 | |
239 | bool SSAPropagator::Run(Function* fn) { |
240 | Initialize(fn); |
241 | |
242 | bool changed = false; |
243 | while (!blocks_.empty() || !ssa_edge_uses_.empty()) { |
244 | // Simulate all blocks first. Simulating blocks will add SSA edges to |
245 | // follow after all the blocks have been simulated. |
246 | if (!blocks_.empty()) { |
247 | auto block = blocks_.front(); |
248 | changed |= Simulate(block); |
249 | blocks_.pop(); |
250 | continue; |
251 | } |
252 | |
253 | // Simulate edges from the SSA queue. |
254 | if (!ssa_edge_uses_.empty()) { |
255 | Instruction* instr = ssa_edge_uses_.front(); |
256 | changed |= Simulate(instr); |
257 | ssa_edge_uses_.pop(); |
258 | } |
259 | } |
260 | |
261 | #ifndef NDEBUG |
262 | // Verify all visited values have settled. No value that has been simulated |
263 | // should end on not interesting. |
264 | fn->ForEachInst([this](Instruction* inst) { |
265 | assert( |
266 | (!HasStatus(inst) || Status(inst) != SSAPropagator::kNotInteresting) && |
267 | "Unsettled value" ); |
268 | }); |
269 | #endif |
270 | |
271 | return changed; |
272 | } |
273 | |
274 | std::ostream& operator<<(std::ostream& str, |
275 | const SSAPropagator::PropStatus& status) { |
276 | switch (status) { |
277 | case SSAPropagator::kVarying: |
278 | str << "Varying" ; |
279 | break; |
280 | case SSAPropagator::kInteresting: |
281 | str << "Interesting" ; |
282 | break; |
283 | default: |
284 | str << "Not interesting" ; |
285 | break; |
286 | } |
287 | return str; |
288 | } |
289 | |
290 | } // namespace opt |
291 | } // namespace spvtools |
292 | |