blob: b7abf4773a369f8bbce782a65ccddffce7debb82 [file] [log] [blame]
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01001/*
Hans-Kristian Arntzen18c37bc2017-01-28 09:00:40 +01002 * Copyright 2016-2017 ARM Limited
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "spirv_cfg.hpp"
18#include <algorithm>
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010019#include <assert.h>
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010020
21using namespace std;
22
23namespace spirv_cross
24{
25CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010026 : compiler(compiler_)
27 , func(func_)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010028{
29 preceding_edges.resize(compiler.get_current_id_bound());
30 succeeding_edges.resize(compiler.get_current_id_bound());
31 visit_order.resize(compiler.get_current_id_bound());
32 immediate_dominators.resize(compiler.get_current_id_bound());
33
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010034 build_post_order_visit_order();
35 build_immediate_dominators();
36}
37
38uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
39{
40 while (a != b)
41 {
42 if (visit_order[a] < visit_order[b])
43 a = immediate_dominators[a];
44 else
45 b = immediate_dominators[b];
46 }
47 return a;
48}
49
50uint32_t CFG::update_common_dominator(uint32_t a, uint32_t b)
51{
52 auto dominator = find_common_dominator(immediate_dominators[a], immediate_dominators[b]);
53 immediate_dominators[a] = dominator;
54 immediate_dominators[b] = dominator;
55 return dominator;
56}
57
58void CFG::build_immediate_dominators()
59{
60 // Traverse the post-order in reverse and build up the immediate dominator tree.
61 fill(begin(immediate_dominators), end(immediate_dominators), 0);
62 immediate_dominators[func.entry_block] = func.entry_block;
63
64 for (auto i = post_order.size(); i; i--)
65 {
66 uint32_t block = post_order[i - 1];
67 auto &pred = preceding_edges[block];
68 if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
69 continue;
70
71 for (auto &edge : pred)
72 {
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010073 if (immediate_dominators[block])
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010074 {
75 assert(immediate_dominators[edge]);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010076 immediate_dominators[block] = update_common_dominator(block, edge);
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010077 }
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010078 else
79 immediate_dominators[block] = edge;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010080 }
81 }
82}
83
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010084bool CFG::is_back_edge(uint32_t to) const
85{
86 // We have a back edge if the visit order is set with the temporary magic value 0.
87 // Crossing edges will have already been recorded with a visit order.
88 return visit_order[to] == 0;
89}
90
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010091bool CFG::post_order_visit(uint32_t block_id)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010092{
93 // If we have already branched to this block (back edge), stop recursion.
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010094 // If our branches are back-edges, we do not record them.
95 // We have to record crossing edges however.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010096 if (visit_order[block_id] >= 0)
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010097 return !is_back_edge(block_id);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010098
99 // Block back-edges from recursively revisiting ourselves.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100100 visit_order[block_id] = 0;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100101
102 // First visit our branch targets.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100103 auto &block = compiler.get<SPIRBlock>(block_id);
104 switch (block.terminator)
105 {
106 case SPIRBlock::Direct:
107 if (post_order_visit(block.next_block))
108 add_branch(block_id, block.next_block);
109 break;
110
111 case SPIRBlock::Select:
112 if (post_order_visit(block.true_block))
113 add_branch(block_id, block.true_block);
114 if (post_order_visit(block.false_block))
115 add_branch(block_id, block.false_block);
116 break;
117
118 case SPIRBlock::MultiSelect:
119 for (auto &target : block.cases)
120 {
121 if (post_order_visit(target.block))
122 add_branch(block_id, target.block);
123 }
124 if (block.default_block && post_order_visit(block.default_block))
125 add_branch(block_id, block.default_block);
126 break;
127
128 default:
129 break;
130 }
131
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100132 // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
133 visit_order[block_id] = ++visit_count;
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100134 post_order.push_back(block_id);
135 return true;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100136}
137
138void CFG::build_post_order_visit_order()
139{
140 uint32_t block = func.entry_block;
141 visit_count = 0;
142 fill(begin(visit_order), end(visit_order), -1);
143 post_order.clear();
144 post_order_visit(block);
145}
146
147void CFG::add_branch(uint32_t from, uint32_t to)
148{
149 const auto add_unique = [](vector<uint32_t> &l, uint32_t value) {
150 auto itr = find(begin(l), end(l), value);
151 if (itr == end(l))
152 l.push_back(value);
153 };
154 add_unique(preceding_edges[to], from);
155 add_unique(succeeding_edges[from], to);
156}
157
158DominatorBuilder::DominatorBuilder(const CFG &cfg_)
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100159 : cfg(cfg_)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100160{
161}
162
163void DominatorBuilder::add_block(uint32_t block)
164{
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100165 if (!cfg.get_immediate_dominator(block))
166 {
167 // Unreachable block via the CFG, we will never emit this code anyways.
168 return;
169 }
170
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100171 if (!dominator)
172 {
173 dominator = block;
174 return;
175 }
176
177 if (block != dominator)
178 dominator = cfg.find_common_dominator(block, dominator);
179}
Hans-Kristian Arntzen5ff11cc2016-11-18 16:45:11 +0100180
181void DominatorBuilder::lift_continue_block_dominator()
182{
183 // It is possible for a continue block to be the dominator if a variable is only accessed inside the while block of a do-while loop.
184 // We cannot safely declare variables inside a continue block, so move any variable declared
185 // in a continue block to the entry block to simplify.
186 // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
187 // solution.
188
189 if (!dominator)
190 return;
191
192 auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
193 auto post_order = cfg.get_visit_order(dominator);
194
195 // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
196 // since we cannot create sensible GLSL code for this, fallback to entry block.
197 bool back_edge_dominator = false;
198 switch (block.terminator)
199 {
200 case SPIRBlock::Direct:
201 if (cfg.get_visit_order(block.next_block) > post_order)
202 back_edge_dominator = true;
203 break;
204
205 case SPIRBlock::Select:
206 if (cfg.get_visit_order(block.true_block) > post_order)
207 back_edge_dominator = true;
208 if (cfg.get_visit_order(block.false_block) > post_order)
209 back_edge_dominator = true;
210 break;
211
212 case SPIRBlock::MultiSelect:
213 for (auto &target : block.cases)
214 {
215 if (cfg.get_visit_order(target.block) > post_order)
216 back_edge_dominator = true;
217 }
218 if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
219 back_edge_dominator = true;
220 break;
221
222 default:
223 break;
224 }
225
226 if (back_edge_dominator)
227 dominator = cfg.get_function().entry_block;
228}
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100229}