1 /* 2 * Copyright 2010 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 /** 25 * \file opt_dead_code_local.cpp 26 * 27 * Eliminates local dead assignments from the code. 28 * 29 * This operates on basic blocks, tracking assignments and finding if 30 * they're used before the variable is completely reassigned. 31 * 32 * Compare this to ir_dead_code.cpp, which operates globally looking 33 * for assignments to variables that are never read. 34 */ 35 36 #include "ir.h" 37 #include "ir_basic_block.h" 38 #include "ir_optimization.h" 39 #include "compiler/glsl_types.h" 40 41 static bool debug = false; 42 43 namespace { 44 45 class assignment_entry : public exec_node 46 { 47 public: 48 /* override operator new from exec_node */ 49 DECLARE_LINEAR_ZALLOC_CXX_OPERATORS(assignment_entry) 50 51 assignment_entry(ir_variable *lhs, ir_assignment *ir) 52 { 53 assert(lhs); 54 assert(ir); 55 this->lhs = lhs; 56 this->ir = ir; 57 this->unused = ir->write_mask; 58 } 59 60 ir_variable *lhs; 61 ir_assignment *ir; 62 63 /* bitmask of xyzw channels written that haven't been used so far. */ 64 int unused; 65 }; 66 67 class kill_for_derefs_visitor : public ir_hierarchical_visitor { 68 public: 69 kill_for_derefs_visitor(exec_list *assignments) 70 { 71 this->assignments = assignments; 72 } 73 74 void use_channels(ir_variable *const var, int used) 75 { 76 foreach_in_list_safe(assignment_entry, entry, this->assignments) { 77 if (entry->lhs == var) { 78 if (var->type->is_scalar() || var->type->is_vector()) { 79 if (debug) 80 printf("used %s (0x%01x - 0x%01x)\n", entry->lhs->name, 81 entry->unused, used & 0xf); 82 entry->unused &= ~used; 83 if (!entry->unused) 84 entry->remove(); 85 } else { 86 if (debug) 87 printf("used %s\n", entry->lhs->name); 88 entry->remove(); 89 } 90 } 91 } 92 } 93 94 virtual ir_visitor_status visit(ir_dereference_variable *ir) 95 { 96 use_channels(ir->var, ~0); 97 98 return visit_continue; 99 } 100 101 virtual ir_visitor_status visit(ir_swizzle *ir) 102 { 103 ir_dereference_variable *deref = ir->val->as_dereference_variable(); 104 if (!deref) 105 return visit_continue; 106 107 int used = 0; 108 used |= 1 << ir->mask.x; 109 if (ir->mask.num_components > 1) 110 used |= 1 << ir->mask.y; 111 if (ir->mask.num_components > 2) 112 used |= 1 << ir->mask.z; 113 if (ir->mask.num_components > 3) 114 used |= 1 << ir->mask.w; 115 116 use_channels(deref->var, used); 117 118 return visit_continue_with_parent; 119 } 120 121 virtual ir_visitor_status visit_leave(ir_emit_vertex *) 122 { 123 /* For the purpose of dead code elimination, emitting a vertex counts as 124 * "reading" all of the currently assigned output variables. 125 */ 126 foreach_in_list_safe(assignment_entry, entry, this->assignments) { 127 if (entry->lhs->data.mode == ir_var_shader_out) { 128 if (debug) 129 printf("kill %s\n", entry->lhs->name); 130 entry->remove(); 131 } 132 } 133 134 return visit_continue; 135 } 136 137 private: 138 exec_list *assignments; 139 }; 140 141 class array_index_visit : public ir_hierarchical_visitor { 142 public: 143 array_index_visit(ir_hierarchical_visitor *v) 144 { 145 this->visitor = v; 146 } 147 148 virtual ir_visitor_status visit_enter(class ir_dereference_array *ir) 149 { 150 ir->array_index->accept(visitor); 151 return visit_continue; 152 } 153 154 static void run(ir_instruction *ir, ir_hierarchical_visitor *v) 155 { 156 array_index_visit top_visit(v); 157 ir->accept(& top_visit); 158 } 159 160 ir_hierarchical_visitor *visitor; 161 }; 162 163 } /* unnamed namespace */ 164 165 /** 166 * Adds an entry to the available copy list if it's a plain assignment 167 * of a variable to a variable. 168 */ 169 static bool 170 process_assignment(void *lin_ctx, ir_assignment *ir, exec_list *assignments) 171 { 172 ir_variable *var = NULL; 173 bool progress = false; 174 kill_for_derefs_visitor v(assignments); 175 176 /* Kill assignment entries for things used to produce this assignment. */ 177 ir->rhs->accept(&v); 178 if (ir->condition) { 179 ir->condition->accept(&v); 180 } 181 182 /* Kill assignment enties used as array indices. 183 */ 184 array_index_visit::run(ir->lhs, &v); 185 var = ir->lhs->variable_referenced(); 186 assert(var); 187 188 /* Now, check if we did a whole-variable assignment. */ 189 if (!ir->condition) { 190 ir_dereference_variable *deref_var = ir->lhs->as_dereference_variable(); 191 192 /* If it's a vector type, we can do per-channel elimination of 193 * use of the RHS. 194 */ 195 if (deref_var && (deref_var->var->type->is_scalar() || 196 deref_var->var->type->is_vector())) { 197 198 if (debug) 199 printf("looking for %s.0x%01x to remove\n", var->name, 200 ir->write_mask); 201 202 foreach_in_list_safe(assignment_entry, entry, assignments) { 203 if (entry->lhs != var) 204 continue; 205 206 /* Skip if the assignment we're trying to eliminate isn't a plain 207 * variable deref. */ 208 if (entry->ir->lhs->ir_type != ir_type_dereference_variable) 209 continue; 210 211 int remove = entry->unused & ir->write_mask; 212 if (debug) { 213 printf("%s 0x%01x - 0x%01x = 0x%01x\n", 214 var->name, 215 entry->ir->write_mask, 216 remove, entry->ir->write_mask & ~remove); 217 } 218 if (remove) { 219 progress = true; 220 221 if (debug) { 222 printf("rewriting:\n "); 223 entry->ir->print(); 224 printf("\n"); 225 } 226 227 entry->ir->write_mask &= ~remove; 228 entry->unused &= ~remove; 229 if (entry->ir->write_mask == 0) { 230 /* Delete the dead assignment. */ 231 entry->ir->remove(); 232 entry->remove(); 233 } else { 234 void *mem_ctx = ralloc_parent(entry->ir); 235 /* Reswizzle the RHS arguments according to the new 236 * write_mask. 237 */ 238 unsigned components[4]; 239 unsigned channels = 0; 240 unsigned next = 0; 241 242 for (int i = 0; i < 4; i++) { 243 if ((entry->ir->write_mask | remove) & (1 << i)) { 244 if (!(remove & (1 << i))) 245 components[channels++] = next; 246 next++; 247 } 248 } 249 250 entry->ir->rhs = new(mem_ctx) ir_swizzle(entry->ir->rhs, 251 components, 252 channels); 253 if (debug) { 254 printf("to:\n "); 255 entry->ir->print(); 256 printf("\n"); 257 } 258 } 259 } 260 } 261 } else if (ir->whole_variable_written() != NULL) { 262 /* We did a whole-variable assignment. So, any instruction in 263 * the assignment list with the same LHS is dead. 264 */ 265 if (debug) 266 printf("looking for %s to remove\n", var->name); 267 foreach_in_list_safe(assignment_entry, entry, assignments) { 268 if (entry->lhs == var) { 269 if (debug) 270 printf("removing %s\n", var->name); 271 entry->ir->remove(); 272 entry->remove(); 273 progress = true; 274 } 275 } 276 } 277 } 278 279 /* Add this instruction to the assignment list available to be removed. */ 280 assignment_entry *entry = new(lin_ctx) assignment_entry(var, ir); 281 assignments->push_tail(entry); 282 283 if (debug) { 284 printf("add %s\n", var->name); 285 286 printf("current entries\n"); 287 foreach_in_list(assignment_entry, entry, assignments) { 288 printf(" %s (0x%01x)\n", entry->lhs->name, entry->unused); 289 } 290 } 291 292 return progress; 293 } 294 295 static void 296 dead_code_local_basic_block(ir_instruction *first, 297 ir_instruction *last, 298 void *data) 299 { 300 ir_instruction *ir, *ir_next; 301 /* List of avaialble_copy */ 302 exec_list assignments; 303 bool *out_progress = (bool *)data; 304 bool progress = false; 305 306 void *ctx = ralloc_context(NULL); 307 void *lin_ctx = linear_alloc_parent(ctx, 0); 308 309 /* Safe looping, since process_assignment */ 310 for (ir = first, ir_next = (ir_instruction *)first->next;; 311 ir = ir_next, ir_next = (ir_instruction *)ir->next) { 312 ir_assignment *ir_assign = ir->as_assignment(); 313 314 if (debug) { 315 ir->print(); 316 printf("\n"); 317 } 318 319 if (ir_assign) { 320 progress = process_assignment(lin_ctx, ir_assign, &assignments) || 321 progress; 322 } else { 323 kill_for_derefs_visitor kill(&assignments); 324 ir->accept(&kill); 325 } 326 327 if (ir == last) 328 break; 329 } 330 *out_progress = progress; 331 ralloc_free(ctx); 332 } 333 334 /** 335 * Does a copy propagation pass on the code present in the instruction stream. 336 */ 337 bool 338 do_dead_code_local(exec_list *instructions) 339 { 340 bool progress = false; 341 342 call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress); 343 344 return progress; 345 } 346