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