Home | History | Annotate | Download | only in nir
      1 /*
      2  * Copyright  2016 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
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 #include "nir.h"
     25 
     26 static void
     27 add_src(nir_src *src, struct set *invariants)
     28 {
     29    if (src->is_ssa) {
     30       _mesa_set_add(invariants, src->ssa);
     31    } else {
     32       _mesa_set_add(invariants, src->reg.reg);
     33    }
     34 }
     35 
     36 static bool
     37 add_src_cb(nir_src *src, void *state)
     38 {
     39    add_src(src, state);
     40    return true;
     41 }
     42 
     43 static bool
     44 dest_is_invariant(nir_dest *dest, struct set *invariants)
     45 {
     46    if (dest->is_ssa) {
     47       return _mesa_set_search(invariants, &dest->ssa);
     48    } else {
     49       return _mesa_set_search(invariants, dest->reg.reg);
     50    }
     51 }
     52 
     53 static void
     54 add_cf_node(nir_cf_node *cf, struct set *invariants)
     55 {
     56    if (cf->type == nir_cf_node_if) {
     57       nir_if *if_stmt = nir_cf_node_as_if(cf);
     58       add_src(&if_stmt->condition, invariants);
     59    }
     60 
     61    if (cf->parent)
     62       add_cf_node(cf->parent, invariants);
     63 }
     64 
     65 static void
     66 add_var(nir_variable *var, struct set *invariants)
     67 {
     68    _mesa_set_add(invariants, var);
     69 }
     70 
     71 static bool
     72 var_is_invariant(nir_variable *var, struct set * invariants)
     73 {
     74    return var->data.invariant || _mesa_set_search(invariants, var);
     75 }
     76 
     77 static void
     78 propagate_invariant_instr(nir_instr *instr, struct set *invariants)
     79 {
     80    switch (instr->type) {
     81    case nir_instr_type_alu: {
     82       nir_alu_instr *alu = nir_instr_as_alu(instr);
     83       if (!dest_is_invariant(&alu->dest.dest, invariants))
     84          break;
     85 
     86       alu->exact = true;
     87       nir_foreach_src(instr, add_src_cb, invariants);
     88       break;
     89    }
     90 
     91    case nir_instr_type_tex: {
     92       nir_tex_instr *tex = nir_instr_as_tex(instr);
     93       if (dest_is_invariant(&tex->dest, invariants))
     94          nir_foreach_src(instr, add_src_cb, invariants);
     95       break;
     96    }
     97 
     98    case nir_instr_type_intrinsic: {
     99       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
    100       switch (intrin->intrinsic) {
    101       case nir_intrinsic_copy_var:
    102          /* If the destination is invariant then so is the source */
    103          if (var_is_invariant(intrin->variables[0]->var, invariants))
    104             add_var(intrin->variables[1]->var, invariants);
    105          break;
    106 
    107       case nir_intrinsic_load_var:
    108          if (dest_is_invariant(&intrin->dest, invariants))
    109             add_var(intrin->variables[0]->var, invariants);
    110          break;
    111 
    112       case nir_intrinsic_store_var:
    113          if (var_is_invariant(intrin->variables[0]->var, invariants))
    114             add_src(&intrin->src[0], invariants);
    115          break;
    116 
    117       default:
    118          /* Nothing to do */
    119          break;
    120       }
    121    }
    122 
    123    case nir_instr_type_jump:
    124    case nir_instr_type_ssa_undef:
    125    case nir_instr_type_load_const:
    126       break; /* Nothing to do */
    127 
    128    case nir_instr_type_phi: {
    129       nir_phi_instr *phi = nir_instr_as_phi(instr);
    130       if (!dest_is_invariant(&phi->dest, invariants))
    131          break;
    132 
    133       nir_foreach_phi_src(src, phi) {
    134          add_src(&src->src, invariants);
    135          add_cf_node(&src->pred->cf_node, invariants);
    136       }
    137       break;
    138    }
    139 
    140    case nir_instr_type_call:
    141       unreachable("This pass must be run after function inlining");
    142 
    143    case nir_instr_type_parallel_copy:
    144    default:
    145       unreachable("Cannot have this instruction type");
    146    }
    147 }
    148 
    149 static bool
    150 propagate_invariant_impl(nir_function_impl *impl, struct set *invariants)
    151 {
    152    bool progress = false;
    153 
    154    while (true) {
    155       uint32_t prev_entries = invariants->entries;
    156 
    157       nir_foreach_block_reverse(block, impl) {
    158          nir_foreach_instr_reverse(instr, block)
    159             propagate_invariant_instr(instr, invariants);
    160       }
    161 
    162       /* Keep running until we make no more progress. */
    163       if (invariants->entries > prev_entries) {
    164          progress = true;
    165          continue;
    166       } else {
    167          break;
    168       }
    169    }
    170 
    171    if (progress) {
    172       nir_metadata_preserve(impl, nir_metadata_block_index |
    173                                   nir_metadata_dominance |
    174                                   nir_metadata_live_ssa_defs);
    175    }
    176 
    177    return progress;
    178 }
    179 
    180 bool
    181 nir_propagate_invariant(nir_shader *shader)
    182 {
    183    /* Hash set of invariant things */
    184    struct set *invariants = _mesa_set_create(NULL, _mesa_hash_pointer,
    185                                              _mesa_key_pointer_equal);
    186 
    187    bool progress = false;
    188    nir_foreach_function(function, shader) {
    189       if (function->impl && propagate_invariant_impl(function->impl, invariants))
    190          progress = true;
    191    }
    192 
    193    _mesa_set_destroy(invariants, NULL);
    194 
    195    return progress;
    196 }
    197