nir: add deref lowering sanity checking
[mesa.git] / src / compiler / nir / nir_deref.c
1 /*
2 * Copyright © 2018 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "nir.h"
25 #include "nir_builder.h"
26
27 /**
28 * Recursively removes unused deref instructions
29 */
30 bool
31 nir_deref_instr_remove_if_unused(nir_deref_instr *instr)
32 {
33 bool progress = false;
34
35 for (nir_deref_instr *d = instr; d; d = nir_deref_instr_parent(d)) {
36 /* If anyone is using this deref, leave it alone */
37 assert(d->dest.is_ssa);
38 if (!list_empty(&d->dest.ssa.uses))
39 break;
40
41 nir_instr_remove(&d->instr);
42 progress = true;
43 }
44
45 return progress;
46 }
47
48 bool
49 nir_remove_dead_derefs_impl(nir_function_impl *impl)
50 {
51 bool progress = false;
52
53 nir_foreach_block(block, impl) {
54 nir_foreach_instr_safe(instr, block) {
55 if (instr->type == nir_instr_type_deref &&
56 nir_deref_instr_remove_if_unused(nir_instr_as_deref(instr)))
57 progress = true;
58 }
59 }
60
61 if (progress)
62 nir_metadata_preserve(impl, nir_metadata_block_index |
63 nir_metadata_dominance);
64
65 return progress;
66 }
67
68 bool
69 nir_remove_dead_derefs(nir_shader *shader)
70 {
71 bool progress = false;
72 nir_foreach_function(function, shader) {
73 if (function->impl && nir_remove_dead_derefs_impl(function->impl))
74 progress = true;
75 }
76
77 return progress;
78 }
79
80 nir_deref_var *
81 nir_deref_instr_to_deref(nir_deref_instr *instr, void *mem_ctx)
82 {
83 nir_deref *deref = NULL;
84
85 while (instr->deref_type != nir_deref_type_var) {
86 nir_deref *nderef;
87 switch (instr->deref_type) {
88 case nir_deref_type_array:
89 case nir_deref_type_array_wildcard: {
90 nir_deref_array *deref_arr = nir_deref_array_create(mem_ctx);
91 if (instr->deref_type == nir_deref_type_array) {
92 nir_const_value *const_index =
93 nir_src_as_const_value(instr->arr.index);
94 if (const_index) {
95 deref_arr->deref_array_type = nir_deref_array_type_direct;
96 deref_arr->base_offset = const_index->u32[0];
97 } else {
98 deref_arr->deref_array_type = nir_deref_array_type_indirect;
99 deref_arr->base_offset = 0;
100 nir_src_copy(&deref_arr->indirect, &instr->arr.index, mem_ctx);
101 }
102 } else {
103 deref_arr->deref_array_type = nir_deref_array_type_wildcard;
104 }
105 nderef = &deref_arr->deref;
106 break;
107 }
108
109 case nir_deref_type_struct:
110 nderef = &nir_deref_struct_create(mem_ctx, instr->strct.index)->deref;
111 break;
112
113 default:
114 unreachable("Invalid deref instruction type");
115 }
116
117 nderef->child = deref;
118 ralloc_steal(nderef, deref);
119 nderef->type = instr->type;
120
121 deref = nderef;
122 assert(instr->parent.is_ssa);
123 instr = nir_src_as_deref(instr->parent);
124 }
125
126 assert(instr->deref_type == nir_deref_type_var);
127 nir_deref_var *deref_var = nir_deref_var_create(mem_ctx, instr->var);
128 deref_var->deref.child = deref;
129 ralloc_steal(deref_var, deref);
130
131 return deref_var;
132 }
133
134 static nir_deref_var *
135 nir_deref_src_to_deref(nir_src src, void *mem_ctx)
136 {
137 return nir_deref_instr_to_deref(nir_src_as_deref(src), mem_ctx);
138 }
139
140 static bool
141 nir_lower_deref_instrs_tex(nir_tex_instr *tex)
142 {
143 bool progress = false;
144
145 /* Remove the instruction before we modify it. This way we won't mess up
146 * use-def chains when we move sources around.
147 */
148 nir_cursor cursor = nir_instr_remove(&tex->instr);
149
150 unsigned new_num_srcs = 0;
151 for (unsigned i = 0; i < tex->num_srcs; i++) {
152 if (tex->src[i].src_type == nir_tex_src_texture_deref) {
153 tex->texture = nir_deref_src_to_deref(tex->src[i].src, tex);
154 progress = true;
155 continue;
156 } else if (tex->src[i].src_type == nir_tex_src_sampler_deref) {
157 tex->sampler = nir_deref_src_to_deref(tex->src[i].src, tex);
158 progress = true;
159 continue;
160 }
161
162 /* Compact the sources down to remove the deref sources */
163 assert(new_num_srcs <= i);
164 tex->src[new_num_srcs++] = tex->src[i];
165 }
166 tex->num_srcs = new_num_srcs;
167
168 nir_instr_insert(cursor, &tex->instr);
169
170 return progress;
171 }
172
173 static bool
174 nir_lower_deref_instrs_intrin(nir_intrinsic_instr *intrin,
175 enum nir_lower_deref_flags flags)
176 {
177 nir_intrinsic_op deref_op = intrin->intrinsic;
178 nir_intrinsic_op var_op;
179
180 switch (deref_op) {
181 #define CASE(a) \
182 case nir_intrinsic_##a##_deref: \
183 if (!(flags & nir_lower_load_store_derefs)) \
184 return false; \
185 var_op = nir_intrinsic_##a##_var; \
186 break;
187 CASE(load)
188 CASE(store)
189 CASE(copy)
190 #undef CASE
191
192 #define CASE(a) \
193 case nir_intrinsic_interp_deref_##a: \
194 if (!(flags & nir_lower_interp_derefs)) \
195 return false; \
196 var_op = nir_intrinsic_interp_var_##a; \
197 break;
198 CASE(at_centroid)
199 CASE(at_sample)
200 CASE(at_offset)
201 #undef CASE
202
203 #define CASE(a) \
204 case nir_intrinsic_atomic_counter_##a##_deref: \
205 if (!(flags & nir_lower_atomic_counter_derefs)) \
206 return false; \
207 var_op = nir_intrinsic_atomic_counter_##a##_var; \
208 break;
209 CASE(inc)
210 CASE(dec)
211 CASE(read)
212 CASE(add)
213 CASE(min)
214 CASE(max)
215 CASE(and)
216 CASE(or)
217 CASE(xor)
218 CASE(exchange)
219 CASE(comp_swap)
220 #undef CASE
221
222 #define CASE(a) \
223 case nir_intrinsic_deref_atomic_##a: \
224 if (!(flags & nir_lower_atomic_derefs)) \
225 return false; \
226 var_op = nir_intrinsic_var_atomic_##a; \
227 break;
228 CASE(add)
229 CASE(imin)
230 CASE(umin)
231 CASE(imax)
232 CASE(umax)
233 CASE(and)
234 CASE(or)
235 CASE(xor)
236 CASE(exchange)
237 CASE(comp_swap)
238 #undef CASE
239
240 #define CASE(a) \
241 case nir_intrinsic_image_deref_##a: \
242 if (!(flags & nir_lower_image_derefs)) \
243 return false; \
244 var_op = nir_intrinsic_image_var_##a; \
245 break;
246 CASE(load)
247 CASE(store)
248 CASE(atomic_add)
249 CASE(atomic_min)
250 CASE(atomic_max)
251 CASE(atomic_and)
252 CASE(atomic_or)
253 CASE(atomic_xor)
254 CASE(atomic_exchange)
255 CASE(atomic_comp_swap)
256 CASE(size)
257 CASE(samples)
258 #undef CASE
259
260 default:
261 return false;
262 }
263
264 /* Remove the instruction before we modify it. This way we won't mess up
265 * use-def chains when we move sources around.
266 */
267 nir_cursor cursor = nir_instr_remove(&intrin->instr);
268
269 unsigned num_derefs = nir_intrinsic_infos[var_op].num_variables;
270 assert(nir_intrinsic_infos[var_op].num_srcs + num_derefs ==
271 nir_intrinsic_infos[deref_op].num_srcs);
272
273 /* Move deref sources to variables */
274 for (unsigned i = 0; i < num_derefs; i++)
275 intrin->variables[i] = nir_deref_src_to_deref(intrin->src[i], intrin);
276
277 /* Shift all the other sources down */
278 for (unsigned i = 0; i < nir_intrinsic_infos[var_op].num_srcs; i++)
279 nir_src_copy(&intrin->src[i], &intrin->src[i + num_derefs], intrin);
280
281 /* Rewrite the extra sources to NIR_SRC_INIT just in case */
282 for (unsigned i = 0; i < num_derefs; i++)
283 intrin->src[nir_intrinsic_infos[var_op].num_srcs + i] = NIR_SRC_INIT;
284
285 /* It's safe to just stomp the intrinsic to var intrinsic since every
286 * intrinsic has room for some variables and the number of sources only
287 * shrinks.
288 */
289 intrin->intrinsic = var_op;
290
291 nir_instr_insert(cursor, &intrin->instr);
292
293 return true;
294 }
295
296 static bool
297 nir_lower_deref_instrs_impl(nir_function_impl *impl,
298 enum nir_lower_deref_flags flags)
299 {
300 bool progress = false;
301
302 /* Walk the instructions in reverse order so that we can safely clean up
303 * the deref instructions after we clean up their uses.
304 */
305 nir_foreach_block_reverse(block, impl) {
306 nir_foreach_instr_reverse_safe(instr, block) {
307 switch (instr->type) {
308 case nir_instr_type_deref:
309 if (list_empty(&nir_instr_as_deref(instr)->dest.ssa.uses)) {
310 nir_instr_remove(instr);
311 progress = true;
312 }
313 break;
314
315 case nir_instr_type_tex:
316 if (flags & nir_lower_texture_derefs)
317 progress |= nir_lower_deref_instrs_tex(nir_instr_as_tex(instr));
318 break;
319
320 case nir_instr_type_intrinsic:
321 progress |=
322 nir_lower_deref_instrs_intrin(nir_instr_as_intrinsic(instr),
323 flags);
324 break;
325
326 default:
327 break; /* Nothing to do */
328 }
329 }
330 }
331
332 if (progress) {
333 nir_metadata_preserve(impl, nir_metadata_block_index |
334 nir_metadata_dominance);
335 }
336
337 return progress;
338 }
339
340 bool
341 nir_lower_deref_instrs(nir_shader *shader,
342 enum nir_lower_deref_flags flags)
343 {
344 bool progress = false;
345
346 nir_foreach_function(function, shader) {
347 if (!function->impl)
348 continue;
349
350 progress |= nir_lower_deref_instrs_impl(function->impl, flags);
351 }
352
353 shader->lowered_derefs |= flags;
354
355 return progress;
356 }