Home | History | Annotate | Download | only in nir
      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 (at) gmail.com)
     25  *
     26  */
     27 
     28 #include "nir.h"
     29 #include <main/imports.h>
     30 
     31 /**
     32  * SSA-based copy propagation
     33  */
     34 
     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 
     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
     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
    102 copy_prop_src(nir_src *src, nir_instr *parent_instr, nir_if *parent_if)
    103 {
    104    if (!src->is_ssa) {
    105       if (src->reg.indirect)
    106          return copy_prop_src(src->reg.indirect, parent_instr, parent_if);
    107       return false;
    108    }
    109 
    110    nir_instr *src_instr = src->ssa->parent_instr;
    111    if (src_instr->type != nir_instr_type_alu)
    112       return false;
    113 
    114    nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
    115    if (!is_swizzleless_move(alu_instr))
    116       return false;
    117 
    118    /* Don't let copy propagation land us with a phi that has more
    119     * components in its source than it has in its destination.  That badly
    120     * messes up out-of-ssa.
    121     */
    122    if (parent_instr && parent_instr->type == nir_instr_type_phi) {
    123       nir_phi_instr *phi = nir_instr_as_phi(parent_instr);
    124       assert(phi->dest.is_ssa);
    125       if (phi->dest.ssa.num_components !=
    126           alu_instr->src[0].src.ssa->num_components)
    127          return false;
    128    }
    129 
    130    if (parent_instr) {
    131       nir_instr_rewrite_src(parent_instr, src,
    132                             nir_src_for_ssa(alu_instr->src[0].src.ssa));
    133    } else {
    134       assert(src == &parent_if->condition);
    135       nir_if_rewrite_condition(parent_if,
    136                                nir_src_for_ssa(alu_instr->src[0].src.ssa));
    137    }
    138 
    139    return true;
    140 }
    141 
    142 static bool
    143 copy_prop_alu_src(nir_alu_instr *parent_alu_instr, unsigned index)
    144 {
    145    nir_alu_src *src = &parent_alu_instr->src[index];
    146    if (!src->src.is_ssa) {
    147       if (src->src.reg.indirect)
    148          return copy_prop_src(src->src.reg.indirect, &parent_alu_instr->instr,
    149                               NULL);
    150       return false;
    151    }
    152 
    153    nir_instr *src_instr =  src->src.ssa->parent_instr;
    154    if (src_instr->type != nir_instr_type_alu)
    155       return false;
    156 
    157    nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
    158    if (!is_move(alu_instr) && !is_vec(alu_instr))
    159       return false;
    160 
    161    nir_ssa_def *def;
    162    unsigned new_swizzle[4] = {0, 0, 0, 0};
    163 
    164    if (alu_instr->op == nir_op_fmov ||
    165        alu_instr->op == nir_op_imov) {
    166       for (unsigned i = 0; i < 4; i++)
    167          new_swizzle[i] = alu_instr->src[0].swizzle[src->swizzle[i]];
    168       def = alu_instr->src[0].src.ssa;
    169    } else {
    170       def = NULL;
    171 
    172       for (unsigned i = 0; i < 4; i++) {
    173          if (!nir_alu_instr_channel_used(parent_alu_instr, index, i))
    174             continue;
    175 
    176          nir_ssa_def *new_def = alu_instr->src[src->swizzle[i]].src.ssa;
    177          if (def == NULL)
    178             def = new_def;
    179          else {
    180             if (def != new_def)
    181                return false;
    182          }
    183          new_swizzle[i] = alu_instr->src[src->swizzle[i]].swizzle[0];
    184       }
    185    }
    186 
    187    for (unsigned i = 0; i < 4; i++)
    188       src->swizzle[i] = new_swizzle[i];
    189 
    190    nir_instr_rewrite_src(&parent_alu_instr->instr, &src->src,
    191                          nir_src_for_ssa(def));
    192 
    193    return true;
    194 }
    195 
    196 typedef struct {
    197    nir_instr *parent_instr;
    198    bool progress;
    199 } copy_prop_state;
    200 
    201 static bool
    202 copy_prop_src_cb(nir_src *src, void *_state)
    203 {
    204    copy_prop_state *state = (copy_prop_state *) _state;
    205    while (copy_prop_src(src, state->parent_instr, NULL))
    206       state->progress = true;
    207 
    208    return true;
    209 }
    210 
    211 static bool
    212 copy_prop_instr(nir_instr *instr)
    213 {
    214    if (instr->type == nir_instr_type_alu) {
    215       nir_alu_instr *alu_instr = nir_instr_as_alu(instr);
    216       bool progress = false;
    217 
    218       for (unsigned i = 0; i < nir_op_infos[alu_instr->op].num_inputs; i++)
    219          while (copy_prop_alu_src(alu_instr, i))
    220             progress = true;
    221 
    222       if (!alu_instr->dest.dest.is_ssa && alu_instr->dest.dest.reg.indirect)
    223          while (copy_prop_src(alu_instr->dest.dest.reg.indirect, instr, NULL))
    224             progress = true;
    225 
    226       return progress;
    227    }
    228 
    229    copy_prop_state state;
    230    state.parent_instr = instr;
    231    state.progress = false;
    232    nir_foreach_src(instr, copy_prop_src_cb, &state);
    233 
    234    return state.progress;
    235 }
    236 
    237 static bool
    238 copy_prop_if(nir_if *if_stmt)
    239 {
    240    return copy_prop_src(&if_stmt->condition, NULL, if_stmt);
    241 }
    242 
    243 static bool
    244 nir_copy_prop_impl(nir_function_impl *impl)
    245 {
    246    bool progress = false;
    247 
    248    nir_foreach_block(block, impl) {
    249       nir_foreach_instr(instr, block) {
    250          if (copy_prop_instr(instr))
    251             progress = true;
    252       }
    253 
    254       nir_if *if_stmt = nir_block_get_following_if(block);
    255       if (if_stmt && copy_prop_if(if_stmt))
    256          progress = true;
    257       }
    258 
    259    if (progress) {
    260       nir_metadata_preserve(impl, nir_metadata_block_index |
    261                                   nir_metadata_dominance);
    262    }
    263 
    264    return progress;
    265 }
    266 
    267 bool
    268 nir_copy_prop(nir_shader *shader)
    269 {
    270    bool progress = false;
    271 
    272    nir_foreach_function(function, shader) {
    273       if (function->impl && nir_copy_prop_impl(function->impl))
    274          progress = true;
    275    }
    276 
    277    return progress;
    278 }
    279