1 /*
2  * Copyright © 2015 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 #include "nir_control_flow.h"
27 
28 static bool inline_function_impl(nir_function_impl *impl, struct set *inlined);
29 
30 static void
convert_deref_to_param_deref(nir_instr * instr,nir_deref_var ** deref,nir_call_instr * call)31 convert_deref_to_param_deref(nir_instr *instr, nir_deref_var **deref,
32                              nir_call_instr *call)
33 {
34    /* This isn't a parameter, just return the deref */
35    if ((*deref)->var->data.mode != nir_var_param)
36       return;
37 
38    int param_idx = (*deref)->var->data.location;
39 
40    nir_deref_var *call_deref;
41    if (param_idx >= 0) {
42       assert(param_idx < call->callee->num_params);
43       call_deref = call->params[param_idx];
44    } else {
45       call_deref = call->return_deref;
46    }
47    assert(call_deref);
48 
49    /* Now we make a new deref by concatenating the deref in the call's
50     * parameter with the deref we were given.
51     */
52    nir_deref_var *new_deref = nir_deref_var_clone(call_deref, instr);
53    nir_deref *new_tail = nir_deref_tail(&new_deref->deref);
54    new_tail->child = (*deref)->deref.child;
55    ralloc_steal(new_tail, new_tail->child);
56    *deref = new_deref;
57 }
58 
59 static void
rewrite_param_derefs(nir_instr * instr,nir_call_instr * call)60 rewrite_param_derefs(nir_instr *instr, nir_call_instr *call)
61 {
62    switch (instr->type) {
63    case nir_instr_type_intrinsic: {
64       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
65 
66       for (unsigned i = 0;
67            i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) {
68          convert_deref_to_param_deref(instr, &intrin->variables[i], call);
69       }
70       break;
71    }
72 
73    case nir_instr_type_tex: {
74       nir_tex_instr *tex = nir_instr_as_tex(instr);
75       if (tex->texture)
76          convert_deref_to_param_deref(&tex->instr, &tex->texture, call);
77       if (tex->sampler)
78          convert_deref_to_param_deref(&tex->instr, &tex->sampler, call);
79       break;
80    }
81 
82    default:
83       break; /* Nothing else has derefs */
84    }
85 }
86 
87 static void
lower_param_to_local(nir_variable * param,nir_function_impl * impl,bool write)88 lower_param_to_local(nir_variable *param, nir_function_impl *impl, bool write)
89 {
90    if (param->data.mode != nir_var_param)
91       return;
92 
93    nir_parameter_type param_type;
94    if (param->data.location >= 0) {
95       assert(param->data.location < impl->num_params);
96       param_type = impl->function->params[param->data.location].param_type;
97    } else {
98       /* Return variable */
99       param_type = nir_parameter_out;
100    }
101 
102    if ((write && param_type == nir_parameter_in) ||
103        (!write && param_type == nir_parameter_out)) {
104       /* In this case, we need a shadow copy.  Turn it into a local */
105       param->data.mode = nir_var_local;
106       exec_list_push_tail(&impl->locals, &param->node);
107    }
108 }
109 
110 static bool
lower_params_to_locals_block(nir_block * block,nir_function_impl * impl)111 lower_params_to_locals_block(nir_block *block, nir_function_impl *impl)
112 {
113    nir_foreach_instr(instr, block) {
114       if (instr->type != nir_instr_type_intrinsic)
115          continue;
116 
117       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
118 
119       switch (intrin->intrinsic) {
120       case nir_intrinsic_store_var:
121          lower_param_to_local(intrin->variables[0]->var, impl, true);
122          break;
123 
124       case nir_intrinsic_copy_var:
125          lower_param_to_local(intrin->variables[0]->var, impl, true);
126          lower_param_to_local(intrin->variables[1]->var, impl, false);
127          break;
128 
129       case nir_intrinsic_load_var:
130          /* All other intrinsics which access variables (image_load_store)
131           * do so in a read-only fasion.
132           */
133          for (unsigned i = 0;
134               i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) {
135             lower_param_to_local(intrin->variables[i]->var, impl, false);
136          }
137          break;
138 
139       default:
140          continue;
141       }
142    }
143 
144    return true;
145 }
146 
147 static bool
inline_functions_block(nir_block * block,nir_builder * b,struct set * inlined)148 inline_functions_block(nir_block *block, nir_builder *b,
149                        struct set *inlined)
150 {
151    bool progress = false;
152    /* This is tricky.  We're iterating over instructions in a block but, as
153     * we go, the block and its instruction list are being split into
154     * pieces.  However, this *should* be safe since foreach_safe always
155     * stashes the next thing in the iteration.  That next thing will
156     * properly get moved to the next block when it gets split, and we
157     * continue iterating there.
158     */
159    nir_foreach_instr_safe(instr, block) {
160       if (instr->type != nir_instr_type_call)
161          continue;
162 
163       progress = true;
164 
165       nir_call_instr *call = nir_instr_as_call(instr);
166       assert(call->callee->impl);
167 
168       inline_function_impl(call->callee->impl, inlined);
169 
170       nir_function_impl *callee_copy =
171          nir_function_impl_clone(call->callee->impl);
172       callee_copy->function = call->callee;
173 
174       /* Add copies of all in parameters */
175       assert(call->num_params == callee_copy->num_params);
176 
177       exec_list_append(&b->impl->locals, &callee_copy->locals);
178       exec_list_append(&b->impl->registers, &callee_copy->registers);
179 
180       b->cursor = nir_before_instr(&call->instr);
181 
182       /* We now need to tie the two functions together using the
183        * parameters.  There are two ways we do this: One is to turn the
184        * parameter into a local variable and do a shadow-copy.  The other
185        * is to treat the parameter as a "proxy" and rewrite derefs to use
186        * the actual variable that comes from the call instruction.  We
187        * implement both schemes.  The first is needed in the case where we
188        * have an in parameter that we write or similar.  The second case is
189        * needed for handling things such as images and uniforms properly.
190        */
191 
192       /* Figure out when we need to lower to a shadow local */
193       nir_foreach_block(block, callee_copy) {
194          lower_params_to_locals_block(block, callee_copy);
195       }
196 
197       for (unsigned i = 0; i < callee_copy->num_params; i++) {
198          nir_variable *param = callee_copy->params[i];
199 
200          if (param->data.mode == nir_var_local &&
201              call->callee->params[i].param_type != nir_parameter_out) {
202             nir_copy_deref_var(b, nir_deref_var_create(b->shader, param),
203                                   call->params[i]);
204          }
205       }
206 
207       nir_foreach_block(block, callee_copy) {
208          nir_foreach_instr(instr, block)
209             rewrite_param_derefs(instr, call);
210       }
211 
212       /* Pluck the body out of the function and place it here */
213       nir_cf_list body;
214       nir_cf_list_extract(&body, &callee_copy->body);
215       nir_cf_reinsert(&body, b->cursor);
216 
217       b->cursor = nir_before_instr(&call->instr);
218 
219       /* Add copies of all out parameters and the return */
220       assert(call->num_params == callee_copy->num_params);
221       for (unsigned i = 0; i < callee_copy->num_params; i++) {
222          nir_variable *param = callee_copy->params[i];
223 
224          if (param->data.mode == nir_var_local &&
225              call->callee->params[i].param_type != nir_parameter_in) {
226             nir_copy_deref_var(b, call->params[i],
227                                   nir_deref_var_create(b->shader, param));
228          }
229       }
230       if (!glsl_type_is_void(call->callee->return_type) &&
231           callee_copy->return_var->data.mode == nir_var_local) {
232          nir_copy_deref_var(b, call->return_deref,
233                                nir_deref_var_create(b->shader,
234                                                     callee_copy->return_var));
235       }
236 
237       nir_instr_remove(&call->instr);
238    }
239 
240    return progress;
241 }
242 
243 static bool
inline_function_impl(nir_function_impl * impl,struct set * inlined)244 inline_function_impl(nir_function_impl *impl, struct set *inlined)
245 {
246    if (_mesa_set_search(inlined, impl))
247       return false; /* Already inlined */
248 
249    nir_builder b;
250    nir_builder_init(&b, impl);
251 
252    bool progress = false;
253    nir_foreach_block_safe(block, impl) {
254       progress |= inline_functions_block(block, &b, inlined);
255    }
256 
257    if (progress) {
258       /* SSA and register indices are completely messed up now */
259       nir_index_ssa_defs(impl);
260       nir_index_local_regs(impl);
261 
262       nir_metadata_preserve(impl, nir_metadata_none);
263    }
264 
265    _mesa_set_add(inlined, impl);
266 
267    return progress;
268 }
269 
270 bool
nir_inline_functions(nir_shader * shader)271 nir_inline_functions(nir_shader *shader)
272 {
273    struct set *inlined = _mesa_set_create(NULL, _mesa_hash_pointer,
274                                           _mesa_key_pointer_equal);
275    bool progress = false;
276 
277    nir_foreach_function(function, shader) {
278       if (function->impl)
279          progress = inline_function_impl(function->impl, inlined) || progress;
280    }
281 
282    _mesa_set_destroy(inlined, NULL);
283 
284    return progress;
285 }
286