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