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