1 /*
2 * Copyright 2016-2021 Arm Limited
3 * SPDX-License-Identifier: Apache-2.0 OR MIT
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /*
19 * At your option, you may choose to accept this material under either:
20 * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
21 * 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
22 */
23
24 #include "spirv_cfg.hpp"
25 #include "spirv_cross.hpp"
26 #include <algorithm>
27 #include <assert.h>
28
29 using namespace std;
30
31 namespace SPIRV_CROSS_NAMESPACE
32 {
CFG(Compiler &compiler_, const SPIRFunction &func_)33 CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
34 : compiler(compiler_)
35 , func(func_)
36 {
37 build_post_order_visit_order();
38 build_immediate_dominators();
39 }
40
find_common_dominator(uint32_t a, uint32_t b) const41 uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
42 {
43 while (a != b)
44 {
45 if (get_visit_order(a) < get_visit_order(b))
46 a = get_immediate_dominator(a);
47 else
48 b = get_immediate_dominator(b);
49 }
50 return a;
51 }
52
build_immediate_dominators()53 void CFG::build_immediate_dominators()
54 {
55 // Traverse the post-order in reverse and build up the immediate dominator tree.
56 immediate_dominators.clear();
57 immediate_dominators[func.entry_block] = func.entry_block;
58
59 for (auto i = post_order.size(); i; i--)
60 {
61 uint32_t block = post_order[i - 1];
62 auto &pred = preceding_edges[block];
63 if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
64 continue;
65
66 for (auto &edge : pred)
67 {
68 if (immediate_dominators[block])
69 {
70 assert(immediate_dominators[edge]);
71 immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge);
72 }
73 else
74 immediate_dominators[block] = edge;
75 }
76 }
77 }
78
is_back_edge(uint32_t to) const79 bool CFG::is_back_edge(uint32_t to) const
80 {
81 // We have a back edge if the visit order is set with the temporary magic value 0.
82 // Crossing edges will have already been recorded with a visit order.
83 auto itr = visit_order.find(to);
84 return itr != end(visit_order) && itr->second.get() == 0;
85 }
86
has_visited_forward_edge(uint32_t to) const87 bool CFG::has_visited_forward_edge(uint32_t to) const
88 {
89 // If > 0, we have visited the edge already, and this is not a back edge branch.
90 auto itr = visit_order.find(to);
91 return itr != end(visit_order) && itr->second.get() > 0;
92 }
93
post_order_visit(uint32_t block_id)94 bool CFG::post_order_visit(uint32_t block_id)
95 {
96 // If we have already branched to this block (back edge), stop recursion.
97 // If our branches are back-edges, we do not record them.
98 // We have to record crossing edges however.
99 if (has_visited_forward_edge(block_id))
100 return true;
101 else if (is_back_edge(block_id))
102 return false;
103
104 // Block back-edges from recursively revisiting ourselves.
105 visit_order[block_id].get() = 0;
106
107 auto &block = compiler.get<SPIRBlock>(block_id);
108
109 // If this is a loop header, add an implied branch to the merge target.
110 // This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners.
111 // To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block.
112 // This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator.
113 // We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG.
114
115 // Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop
116 // is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis.
117 // For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine,
118 // but for loops, only the header might end up actually branching to merge block.
119 if (block.merge == SPIRBlock::MergeLoop && post_order_visit(block.merge_block))
120 add_branch(block_id, block.merge_block);
121
122 // First visit our branch targets.
123 switch (block.terminator)
124 {
125 case SPIRBlock::Direct:
126 if (post_order_visit(block.next_block))
127 add_branch(block_id, block.next_block);
128 break;
129
130 case SPIRBlock::Select:
131 if (post_order_visit(block.true_block))
132 add_branch(block_id, block.true_block);
133 if (post_order_visit(block.false_block))
134 add_branch(block_id, block.false_block);
135 break;
136
137 case SPIRBlock::MultiSelect:
138 for (auto &target : block.cases)
139 {
140 if (post_order_visit(target.block))
141 add_branch(block_id, target.block);
142 }
143 if (block.default_block && post_order_visit(block.default_block))
144 add_branch(block_id, block.default_block);
145 break;
146
147 default:
148 break;
149 }
150
151 // If this is a selection merge, add an implied branch to the merge target.
152 // This is needed to avoid cases where an inner branch dominates the outer branch.
153 // This can happen if one of the branches exit early, e.g.:
154 // if (cond) { ...; break; } else { var = 100 } use_var(var);
155 // We can use the variable without a Phi since there is only one possible parent here.
156 // However, in this case, we need to hoist out the inner variable to outside the branch.
157 // Use same strategy as loops.
158 if (block.merge == SPIRBlock::MergeSelection && post_order_visit(block.next_block))
159 {
160 // If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup.
161 // Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement
162 // will be hoisted out to outside the selection merge.
163 // If size > 1, the variable will be automatically hoisted, so we should not mess with it.
164 // The exception here is switch blocks, where we can have multiple edges to merge block,
165 // all coming from same scope, so be more conservative in this case.
166 // Adding fake branches unconditionally breaks parameter preservation analysis,
167 // which looks at how variables are accessed through the CFG.
168 auto pred_itr = preceding_edges.find(block.next_block);
169 if (pred_itr != end(preceding_edges))
170 {
171 auto &pred = pred_itr->second;
172 auto succ_itr = succeeding_edges.find(block_id);
173 size_t num_succeeding_edges = 0;
174 if (succ_itr != end(succeeding_edges))
175 num_succeeding_edges = succ_itr->second.size();
176
177 if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1)
178 {
179 // Multiple branches can come from the same scope due to "break;", so we need to assume that all branches
180 // come from same case scope in worst case, even if there are multiple preceding edges.
181 // If we have more than one succeeding edge from the block header, it should be impossible
182 // to have a dominator be inside the block.
183 // Only case this can go wrong is if we have 2 or more edges from block header and
184 // 2 or more edges to merge block, and still have dominator be inside a case label.
185 if (!pred.empty())
186 add_branch(block_id, block.next_block);
187 }
188 else
189 {
190 if (pred.size() == 1 && *pred.begin() != block_id)
191 add_branch(block_id, block.next_block);
192 }
193 }
194 else
195 {
196 // If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it.
197 // We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge.
198 add_branch(block_id, block.next_block);
199 }
200 }
201
202 // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
203 visit_order[block_id].get() = ++visit_count;
204 post_order.push_back(block_id);
205 return true;
206 }
207
build_post_order_visit_order()208 void CFG::build_post_order_visit_order()
209 {
210 uint32_t block = func.entry_block;
211 visit_count = 0;
212 visit_order.clear();
213 post_order.clear();
214 post_order_visit(block);
215 }
216
add_branch(uint32_t from, uint32_t to)217 void CFG::add_branch(uint32_t from, uint32_t to)
218 {
219 const auto add_unique = [](SmallVector<uint32_t> &l, uint32_t value) {
220 auto itr = find(begin(l), end(l), value);
221 if (itr == end(l))
222 l.push_back(value);
223 };
224 add_unique(preceding_edges[to], from);
225 add_unique(succeeding_edges[from], to);
226 }
227
find_loop_dominator(uint32_t block_id) const228 uint32_t CFG::find_loop_dominator(uint32_t block_id) const
229 {
230 while (block_id != SPIRBlock::NoDominator)
231 {
232 auto itr = preceding_edges.find(block_id);
233 if (itr == end(preceding_edges))
234 return SPIRBlock::NoDominator;
235 if (itr->second.empty())
236 return SPIRBlock::NoDominator;
237
238 uint32_t pred_block_id = SPIRBlock::NoDominator;
239 bool ignore_loop_header = false;
240
241 // If we are a merge block, go directly to the header block.
242 // Only consider a loop dominator if we are branching from inside a block to a loop header.
243 // NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly.
244 for (auto &pred : itr->second)
245 {
246 auto &pred_block = compiler.get<SPIRBlock>(pred);
247 if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id))
248 {
249 pred_block_id = pred;
250 ignore_loop_header = true;
251 break;
252 }
253 else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id))
254 {
255 pred_block_id = pred;
256 break;
257 }
258 }
259
260 // No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we
261 // take will lead there.
262 if (pred_block_id == SPIRBlock::NoDominator)
263 pred_block_id = itr->second.front();
264
265 block_id = pred_block_id;
266
267 if (!ignore_loop_header && block_id)
268 {
269 auto &block = compiler.get<SPIRBlock>(block_id);
270 if (block.merge == SPIRBlock::MergeLoop)
271 return block_id;
272 }
273 }
274
275 return block_id;
276 }
277
node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const278 bool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const
279 {
280 // Walk backwards, starting from "to" block.
281 // Only follow pred edges if they have a 1:1 relationship, or a merge relationship.
282 // If we cannot find a path to "from", we must assume that to is inside control flow in some way.
283
284 auto &from_block = compiler.get<SPIRBlock>(from);
285 BlockID ignore_block_id = 0;
286 if (from_block.merge == SPIRBlock::MergeLoop)
287 ignore_block_id = from_block.merge_block;
288
289 while (to != from)
290 {
291 auto pred_itr = preceding_edges.find(to);
292 if (pred_itr == end(preceding_edges))
293 return false;
294
295 DominatorBuilder builder(*this);
296 for (auto &edge : pred_itr->second)
297 builder.add_block(edge);
298
299 uint32_t dominator = builder.get_dominator();
300 if (dominator == 0)
301 return false;
302
303 auto &dom = compiler.get<SPIRBlock>(dominator);
304
305 bool true_path_ignore = false;
306 bool false_path_ignore = false;
307 if (ignore_block_id && dom.terminator == SPIRBlock::Select)
308 {
309 auto &true_block = compiler.get<SPIRBlock>(dom.true_block);
310 auto &false_block = compiler.get<SPIRBlock>(dom.false_block);
311 auto &ignore_block = compiler.get<SPIRBlock>(ignore_block_id);
312 true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block);
313 false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block);
314 }
315
316 if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) ||
317 (dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) ||
318 (dom.terminator == SPIRBlock::Direct && dom.next_block == to) ||
319 (dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) ||
320 (dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore))
321 {
322 // Allow walking selection constructs if the other branch reaches out of a loop construct.
323 // It cannot be in-scope anymore.
324 to = dominator;
325 }
326 else
327 return false;
328 }
329
330 return true;
331 }
332
DominatorBuilder(const CFG &cfg_)333 DominatorBuilder::DominatorBuilder(const CFG &cfg_)
334 : cfg(cfg_)
335 {
336 }
337
add_block(uint32_t block)338 void DominatorBuilder::add_block(uint32_t block)
339 {
340 if (!cfg.get_immediate_dominator(block))
341 {
342 // Unreachable block via the CFG, we will never emit this code anyways.
343 return;
344 }
345
346 if (!dominator)
347 {
348 dominator = block;
349 return;
350 }
351
352 if (block != dominator)
353 dominator = cfg.find_common_dominator(block, dominator);
354 }
355
lift_continue_block_dominator()356 void DominatorBuilder::lift_continue_block_dominator()
357 {
358 // It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop.
359 // We cannot safely declare variables inside a continue block, so move any variable declared
360 // in a continue block to the entry block to simplify.
361 // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
362 // solution.
363
364 if (!dominator)
365 return;
366
367 auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
368 auto post_order = cfg.get_visit_order(dominator);
369
370 // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
371 // since we cannot create sensible GLSL code for this, fallback to entry block.
372 bool back_edge_dominator = false;
373 switch (block.terminator)
374 {
375 case SPIRBlock::Direct:
376 if (cfg.get_visit_order(block.next_block) > post_order)
377 back_edge_dominator = true;
378 break;
379
380 case SPIRBlock::Select:
381 if (cfg.get_visit_order(block.true_block) > post_order)
382 back_edge_dominator = true;
383 if (cfg.get_visit_order(block.false_block) > post_order)
384 back_edge_dominator = true;
385 break;
386
387 case SPIRBlock::MultiSelect:
388 for (auto &target : block.cases)
389 {
390 if (cfg.get_visit_order(target.block) > post_order)
391 back_edge_dominator = true;
392 }
393 if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
394 back_edge_dominator = true;
395 break;
396
397 default:
398 break;
399 }
400
401 if (back_edge_dominator)
402 dominator = cfg.get_function().entry_block;
403 }
404 } // namespace SPIRV_CROSS_NAMESPACE
405