blob: 32549edc72633682e5790d24ae58e68ae1a8d891 [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"
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +010018#include "spirv_cross.hpp"
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010019#include <algorithm>
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010020#include <assert.h>
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010021
22using namespace std;
23
24namespace spirv_cross
25{
26CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010027 : compiler(compiler_)
28 , func(func_)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010029{
30 preceding_edges.resize(compiler.get_current_id_bound());
31 succeeding_edges.resize(compiler.get_current_id_bound());
32 visit_order.resize(compiler.get_current_id_bound());
33 immediate_dominators.resize(compiler.get_current_id_bound());
34
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010035 build_post_order_visit_order();
36 build_immediate_dominators();
37}
38
39uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
40{
41 while (a != b)
42 {
43 if (visit_order[a] < visit_order[b])
44 a = immediate_dominators[a];
45 else
46 b = immediate_dominators[b];
47 }
48 return a;
49}
50
51uint32_t CFG::update_common_dominator(uint32_t a, uint32_t b)
52{
53 auto dominator = find_common_dominator(immediate_dominators[a], immediate_dominators[b]);
54 immediate_dominators[a] = dominator;
55 immediate_dominators[b] = dominator;
56 return dominator;
57}
58
59void CFG::build_immediate_dominators()
60{
61 // Traverse the post-order in reverse and build up the immediate dominator tree.
62 fill(begin(immediate_dominators), end(immediate_dominators), 0);
63 immediate_dominators[func.entry_block] = func.entry_block;
64
65 for (auto i = post_order.size(); i; i--)
66 {
67 uint32_t block = post_order[i - 1];
68 auto &pred = preceding_edges[block];
69 if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
70 continue;
71
72 for (auto &edge : pred)
73 {
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010074 if (immediate_dominators[block])
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010075 {
76 assert(immediate_dominators[edge]);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010077 immediate_dominators[block] = update_common_dominator(block, edge);
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010078 }
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010079 else
80 immediate_dominators[block] = edge;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010081 }
82 }
83}
84
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010085bool CFG::is_back_edge(uint32_t to) const
86{
87 // We have a back edge if the visit order is set with the temporary magic value 0.
88 // Crossing edges will have already been recorded with a visit order.
89 return visit_order[to] == 0;
90}
91
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010092bool CFG::post_order_visit(uint32_t block_id)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010093{
94 // If we have already branched to this block (back edge), stop recursion.
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010095 // If our branches are back-edges, we do not record them.
96 // We have to record crossing edges however.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +010097 if (visit_order[block_id] >= 0)
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +010098 return !is_back_edge(block_id);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010099
100 // Block back-edges from recursively revisiting ourselves.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100101 visit_order[block_id] = 0;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100102
103 // First visit our branch targets.
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100104 auto &block = compiler.get<SPIRBlock>(block_id);
105 switch (block.terminator)
106 {
107 case SPIRBlock::Direct:
108 if (post_order_visit(block.next_block))
109 add_branch(block_id, block.next_block);
110 break;
111
112 case SPIRBlock::Select:
113 if (post_order_visit(block.true_block))
114 add_branch(block_id, block.true_block);
115 if (post_order_visit(block.false_block))
116 add_branch(block_id, block.false_block);
117 break;
118
119 case SPIRBlock::MultiSelect:
120 for (auto &target : block.cases)
121 {
122 if (post_order_visit(target.block))
123 add_branch(block_id, target.block);
124 }
125 if (block.default_block && post_order_visit(block.default_block))
126 add_branch(block_id, block.default_block);
127 break;
128
129 default:
130 break;
131 }
132
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100133 // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
134 visit_order[block_id] = ++visit_count;
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100135 post_order.push_back(block_id);
136 return true;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100137}
138
139void CFG::build_post_order_visit_order()
140{
141 uint32_t block = func.entry_block;
142 visit_count = 0;
143 fill(begin(visit_order), end(visit_order), -1);
144 post_order.clear();
145 post_order_visit(block);
146}
147
148void CFG::add_branch(uint32_t from, uint32_t to)
149{
150 const auto add_unique = [](vector<uint32_t> &l, uint32_t value) {
151 auto itr = find(begin(l), end(l), value);
152 if (itr == end(l))
153 l.push_back(value);
154 };
155 add_unique(preceding_edges[to], from);
156 add_unique(succeeding_edges[from], to);
157}
158
159DominatorBuilder::DominatorBuilder(const CFG &cfg_)
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100160 : cfg(cfg_)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100161{
162}
163
164void DominatorBuilder::add_block(uint32_t block)
165{
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +0100166 if (!cfg.get_immediate_dominator(block))
167 {
168 // Unreachable block via the CFG, we will never emit this code anyways.
169 return;
170 }
171
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100172 if (!dominator)
173 {
174 dominator = block;
175 return;
176 }
177
178 if (block != dominator)
179 dominator = cfg.find_common_dominator(block, dominator);
180}
Hans-Kristian Arntzen5ff11cc2016-11-18 16:45:11 +0100181
182void DominatorBuilder::lift_continue_block_dominator()
183{
184 // 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.
185 // We cannot safely declare variables inside a continue block, so move any variable declared
186 // in a continue block to the entry block to simplify.
187 // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
188 // solution.
189
190 if (!dominator)
191 return;
192
193 auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
194 auto post_order = cfg.get_visit_order(dominator);
195
196 // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
197 // since we cannot create sensible GLSL code for this, fallback to entry block.
198 bool back_edge_dominator = false;
199 switch (block.terminator)
200 {
201 case SPIRBlock::Direct:
202 if (cfg.get_visit_order(block.next_block) > post_order)
203 back_edge_dominator = true;
204 break;
205
206 case SPIRBlock::Select:
207 if (cfg.get_visit_order(block.true_block) > post_order)
208 back_edge_dominator = true;
209 if (cfg.get_visit_order(block.false_block) > post_order)
210 back_edge_dominator = true;
211 break;
212
213 case SPIRBlock::MultiSelect:
214 for (auto &target : block.cases)
215 {
216 if (cfg.get_visit_order(target.block) > post_order)
217 back_edge_dominator = true;
218 }
219 if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
220 back_edge_dominator = true;
221 break;
222
223 default:
224 break;
225 }
226
227 if (back_edge_dominator)
228 dominator = cfg.get_function().entry_block;
229}
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +0100230}