• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 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  * Authors:
24  *    Connor Abbott (cwabbott0@gmail.com)
25  *
26  */
27 
28 #include "nir.h"
29 #include <main/imports.h>
30 
31 /**
32  * SSA-based copy propagation
33  */
34 
is_move(nir_alu_instr * instr)35 static bool is_move(nir_alu_instr *instr)
36 {
37    if (instr->op != nir_op_fmov &&
38        instr->op != nir_op_imov)
39       return false;
40 
41    if (instr->dest.saturate)
42       return false;
43 
44    /* we handle modifiers in a separate pass */
45 
46    if (instr->src[0].abs || instr->src[0].negate)
47       return false;
48 
49    if (!instr->src[0].src.is_ssa)
50       return false;
51 
52    return true;
53 
54 }
55 
is_vec(nir_alu_instr * instr)56 static bool is_vec(nir_alu_instr *instr)
57 {
58    for (unsigned i = 0; i < nir_op_infos[instr->op].num_inputs; i++) {
59       if (!instr->src[i].src.is_ssa)
60          return false;
61 
62       /* we handle modifiers in a separate pass */
63       if (instr->src[i].abs || instr->src[i].negate)
64          return false;
65    }
66 
67    return instr->op == nir_op_vec2 ||
68           instr->op == nir_op_vec3 ||
69           instr->op == nir_op_vec4;
70 }
71 
72 static bool
is_swizzleless_move(nir_alu_instr * instr)73 is_swizzleless_move(nir_alu_instr *instr)
74 {
75    if (is_move(instr)) {
76       for (unsigned i = 0; i < 4; i++) {
77          if (!((instr->dest.write_mask >> i) & 1))
78             break;
79          if (instr->src[0].swizzle[i] != i)
80             return false;
81       }
82       return true;
83    } else if (is_vec(instr)) {
84       nir_ssa_def *def = NULL;
85       for (unsigned i = 0; i < nir_op_infos[instr->op].num_inputs; i++) {
86          if (instr->src[i].swizzle[0] != i)
87             return false;
88 
89          if (def == NULL) {
90             def = instr->src[i].src.ssa;
91          } else if (instr->src[i].src.ssa != def) {
92             return false;
93          }
94       }
95       return true;
96    } else {
97       return false;
98    }
99 }
100 
101 static bool
copy_prop_src(nir_src * src,nir_instr * parent_instr,nir_if * parent_if,unsigned num_components)102 copy_prop_src(nir_src *src, nir_instr *parent_instr, nir_if *parent_if,
103               unsigned num_components)
104 {
105    if (!src->is_ssa) {
106       if (src->reg.indirect)
107          return copy_prop_src(src->reg.indirect, parent_instr, parent_if, 1);
108       return false;
109    }
110 
111    nir_instr *src_instr = src->ssa->parent_instr;
112    if (src_instr->type != nir_instr_type_alu)
113       return false;
114 
115    nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
116    if (!is_swizzleless_move(alu_instr))
117       return false;
118 
119    if (alu_instr->src[0].src.ssa->num_components != num_components)
120       return false;
121 
122    if (parent_instr) {
123       nir_instr_rewrite_src(parent_instr, src,
124                             nir_src_for_ssa(alu_instr->src[0].src.ssa));
125    } else {
126       assert(src == &parent_if->condition);
127       nir_if_rewrite_condition(parent_if,
128                                nir_src_for_ssa(alu_instr->src[0].src.ssa));
129    }
130 
131    return true;
132 }
133 
134 static bool
copy_prop_alu_src(nir_alu_instr * parent_alu_instr,unsigned index)135 copy_prop_alu_src(nir_alu_instr *parent_alu_instr, unsigned index)
136 {
137    nir_alu_src *src = &parent_alu_instr->src[index];
138    if (!src->src.is_ssa) {
139       if (src->src.reg.indirect)
140          return copy_prop_src(src->src.reg.indirect, &parent_alu_instr->instr,
141                               NULL, 1);
142       return false;
143    }
144 
145    nir_instr *src_instr =  src->src.ssa->parent_instr;
146    if (src_instr->type != nir_instr_type_alu)
147       return false;
148 
149    nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
150    if (!is_move(alu_instr) && !is_vec(alu_instr))
151       return false;
152 
153    nir_ssa_def *def;
154    unsigned new_swizzle[4] = {0, 0, 0, 0};
155 
156    if (alu_instr->op == nir_op_fmov ||
157        alu_instr->op == nir_op_imov) {
158       for (unsigned i = 0; i < 4; i++)
159          new_swizzle[i] = alu_instr->src[0].swizzle[src->swizzle[i]];
160       def = alu_instr->src[0].src.ssa;
161    } else {
162       def = NULL;
163 
164       for (unsigned i = 0; i < 4; i++) {
165          if (!nir_alu_instr_channel_used(parent_alu_instr, index, i))
166             continue;
167 
168          nir_ssa_def *new_def = alu_instr->src[src->swizzle[i]].src.ssa;
169          if (def == NULL)
170             def = new_def;
171          else {
172             if (def != new_def)
173                return false;
174          }
175          new_swizzle[i] = alu_instr->src[src->swizzle[i]].swizzle[0];
176       }
177    }
178 
179    for (unsigned i = 0; i < 4; i++)
180       src->swizzle[i] = new_swizzle[i];
181 
182    nir_instr_rewrite_src(&parent_alu_instr->instr, &src->src,
183                          nir_src_for_ssa(def));
184 
185    return true;
186 }
187 
188 static bool
copy_prop_dest(nir_dest * dest,nir_instr * instr)189 copy_prop_dest(nir_dest *dest, nir_instr *instr)
190 {
191    if (!dest->is_ssa && dest->reg.indirect)
192       return copy_prop_src(dest->reg.indirect, instr, NULL, 1);
193 
194    return false;
195 }
196 
197 static bool
copy_prop_deref_var(nir_instr * instr,nir_deref_var * deref_var)198 copy_prop_deref_var(nir_instr *instr, nir_deref_var *deref_var)
199 {
200    if (!deref_var)
201       return false;
202 
203    bool progress = false;
204    for (nir_deref *deref = deref_var->deref.child;
205         deref; deref = deref->child) {
206       if (deref->deref_type != nir_deref_type_array)
207          continue;
208 
209       nir_deref_array *arr = nir_deref_as_array(deref);
210       if (arr->deref_array_type != nir_deref_array_type_indirect)
211          continue;
212 
213       while (copy_prop_src(&arr->indirect, instr, NULL, 1))
214          progress = true;
215    }
216    return progress;
217 }
218 
219 static bool
copy_prop_instr(nir_instr * instr)220 copy_prop_instr(nir_instr *instr)
221 {
222    bool progress = false;
223    switch (instr->type) {
224    case nir_instr_type_alu: {
225       nir_alu_instr *alu_instr = nir_instr_as_alu(instr);
226 
227       for (unsigned i = 0; i < nir_op_infos[alu_instr->op].num_inputs; i++)
228          while (copy_prop_alu_src(alu_instr, i))
229             progress = true;
230 
231       while (copy_prop_dest(&alu_instr->dest.dest, instr))
232          progress = true;
233 
234       return progress;
235    }
236 
237    case nir_instr_type_tex: {
238       nir_tex_instr *tex = nir_instr_as_tex(instr);
239       for (unsigned i = 0; i < tex->num_srcs; i++) {
240          unsigned num_components = nir_tex_instr_src_size(tex, i);
241          while (copy_prop_src(&tex->src[i].src, instr, NULL, num_components))
242             progress = true;
243       }
244 
245       if (copy_prop_deref_var(instr, tex->texture))
246          progress = true;
247       if (copy_prop_deref_var(instr, tex->sampler))
248          progress = true;
249 
250       while (copy_prop_dest(&tex->dest, instr))
251          progress = true;
252 
253       return progress;
254    }
255 
256    case nir_instr_type_intrinsic: {
257       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
258       for (unsigned i = 0;
259            i < nir_intrinsic_infos[intrin->intrinsic].num_srcs; i++) {
260          unsigned num_components =
261             nir_intrinsic_infos[intrin->intrinsic].src_components[i];
262          if (!num_components)
263             num_components = intrin->num_components;
264 
265          while (copy_prop_src(&intrin->src[i], instr, NULL, num_components))
266             progress = true;
267       }
268 
269       for (unsigned i = 0;
270            i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) {
271          if (copy_prop_deref_var(instr, intrin->variables[i]))
272             progress = true;
273       }
274 
275       if (nir_intrinsic_infos[intrin->intrinsic].has_dest) {
276          while (copy_prop_dest(&intrin->dest, instr))
277             progress = true;
278       }
279 
280       return progress;
281    }
282 
283    case nir_instr_type_phi: {
284       nir_phi_instr *phi = nir_instr_as_phi(instr);
285       assert(phi->dest.is_ssa);
286       unsigned num_components = phi->dest.ssa.num_components;
287       nir_foreach_phi_src(src, phi) {
288          while (copy_prop_src(&src->src, instr, NULL, num_components))
289             progress = true;
290       }
291 
292       return progress;
293    }
294 
295    default:
296       return false;
297    }
298 }
299 
300 static bool
copy_prop_if(nir_if * if_stmt)301 copy_prop_if(nir_if *if_stmt)
302 {
303    return copy_prop_src(&if_stmt->condition, NULL, if_stmt, 1);
304 }
305 
306 static bool
nir_copy_prop_impl(nir_function_impl * impl)307 nir_copy_prop_impl(nir_function_impl *impl)
308 {
309    bool progress = false;
310 
311    nir_foreach_block(block, impl) {
312       nir_foreach_instr(instr, block) {
313          if (copy_prop_instr(instr))
314             progress = true;
315       }
316 
317       nir_if *if_stmt = nir_block_get_following_if(block);
318       if (if_stmt && copy_prop_if(if_stmt))
319          progress = true;
320       }
321 
322    if (progress) {
323       nir_metadata_preserve(impl, nir_metadata_block_index |
324                                   nir_metadata_dominance);
325    }
326 
327    return progress;
328 }
329 
330 bool
nir_copy_prop(nir_shader * shader)331 nir_copy_prop(nir_shader *shader)
332 {
333    bool progress = false;
334 
335    nir_foreach_function(function, shader) {
336       if (function->impl && nir_copy_prop_impl(function->impl))
337          progress = true;
338    }
339 
340    return progress;
341 }
342