Home | History | Annotate | Download | only in nir
      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
     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
     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
     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
    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
    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
    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
    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