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 28static bool inline_function_impl(nir_function_impl *impl, struct set *inlined); 29 30static void 31convert_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 59static void 60rewrite_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 87static void 88lower_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, ¶m->node); 107 } 108} 109 110static bool 111lower_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 147static bool 148inline_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 243static bool 244inline_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 270bool 271nir_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