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 41static bool debug = false; 42 43namespace { 44 45class assignment_entry : public exec_node 46{ 47public: 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 67class kill_for_derefs_visitor : public ir_hierarchical_visitor { 68public: 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 137private: 138 exec_list *assignments; 139}; 140 141class array_index_visit : public ir_hierarchical_visitor { 142public: 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 */ 169static bool 170process_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 if (ir->condition == NULL) { 177 /* If this is an assignment of the form "foo = foo;", remove the whole 178 * instruction and be done with it. 179 */ 180 const ir_variable *const lhs_var = ir->whole_variable_written(); 181 if (lhs_var != NULL && lhs_var == ir->rhs->whole_variable_referenced()) { 182 ir->remove(); 183 return true; 184 } 185 } 186 187 /* Kill assignment entries for things used to produce this assignment. */ 188 ir->rhs->accept(&v); 189 if (ir->condition) { 190 ir->condition->accept(&v); 191 } 192 193 /* Kill assignment enties used as array indices. 194 */ 195 array_index_visit::run(ir->lhs, &v); 196 var = ir->lhs->variable_referenced(); 197 assert(var); 198 199 /* Now, check if we did a whole-variable assignment. */ 200 if (!ir->condition) { 201 ir_dereference_variable *deref_var = ir->lhs->as_dereference_variable(); 202 203 /* If it's a vector type, we can do per-channel elimination of 204 * use of the RHS. 205 */ 206 if (deref_var && (deref_var->var->type->is_scalar() || 207 deref_var->var->type->is_vector())) { 208 209 if (debug) 210 printf("looking for %s.0x%01x to remove\n", var->name, 211 ir->write_mask); 212 213 foreach_in_list_safe(assignment_entry, entry, assignments) { 214 if (entry->lhs != var) 215 continue; 216 217 /* Skip if the assignment we're trying to eliminate isn't a plain 218 * variable deref. */ 219 if (entry->ir->lhs->ir_type != ir_type_dereference_variable) 220 continue; 221 222 int remove = entry->unused & ir->write_mask; 223 if (debug) { 224 printf("%s 0x%01x - 0x%01x = 0x%01x\n", 225 var->name, 226 entry->ir->write_mask, 227 remove, entry->ir->write_mask & ~remove); 228 } 229 if (remove) { 230 progress = true; 231 232 if (debug) { 233 printf("rewriting:\n "); 234 entry->ir->print(); 235 printf("\n"); 236 } 237 238 entry->ir->write_mask &= ~remove; 239 entry->unused &= ~remove; 240 if (entry->ir->write_mask == 0) { 241 /* Delete the dead assignment. */ 242 entry->ir->remove(); 243 entry->remove(); 244 } else { 245 void *mem_ctx = ralloc_parent(entry->ir); 246 /* Reswizzle the RHS arguments according to the new 247 * write_mask. 248 */ 249 unsigned components[4]; 250 unsigned channels = 0; 251 unsigned next = 0; 252 253 for (int i = 0; i < 4; i++) { 254 if ((entry->ir->write_mask | remove) & (1 << i)) { 255 if (!(remove & (1 << i))) 256 components[channels++] = next; 257 next++; 258 } 259 } 260 261 entry->ir->rhs = new(mem_ctx) ir_swizzle(entry->ir->rhs, 262 components, 263 channels); 264 if (debug) { 265 printf("to:\n "); 266 entry->ir->print(); 267 printf("\n"); 268 } 269 } 270 } 271 } 272 } else if (ir->whole_variable_written() != NULL) { 273 /* We did a whole-variable assignment. So, any instruction in 274 * the assignment list with the same LHS is dead. 275 */ 276 if (debug) 277 printf("looking for %s to remove\n", var->name); 278 foreach_in_list_safe(assignment_entry, entry, assignments) { 279 if (entry->lhs == var) { 280 if (debug) 281 printf("removing %s\n", var->name); 282 entry->ir->remove(); 283 entry->remove(); 284 progress = true; 285 } 286 } 287 } 288 } 289 290 /* Add this instruction to the assignment list available to be removed. */ 291 assignment_entry *entry = new(lin_ctx) assignment_entry(var, ir); 292 assignments->push_tail(entry); 293 294 if (debug) { 295 printf("add %s\n", var->name); 296 297 printf("current entries\n"); 298 foreach_in_list(assignment_entry, entry, assignments) { 299 printf(" %s (0x%01x)\n", entry->lhs->name, entry->unused); 300 } 301 } 302 303 return progress; 304} 305 306static void 307dead_code_local_basic_block(ir_instruction *first, 308 ir_instruction *last, 309 void *data) 310{ 311 ir_instruction *ir, *ir_next; 312 /* List of avaialble_copy */ 313 exec_list assignments; 314 bool *out_progress = (bool *)data; 315 bool progress = false; 316 317 void *ctx = ralloc_context(NULL); 318 void *lin_ctx = linear_alloc_parent(ctx, 0); 319 320 /* Safe looping, since process_assignment */ 321 for (ir = first, ir_next = (ir_instruction *)first->next;; 322 ir = ir_next, ir_next = (ir_instruction *)ir->next) { 323 ir_assignment *ir_assign = ir->as_assignment(); 324 325 if (debug) { 326 ir->print(); 327 printf("\n"); 328 } 329 330 if (ir_assign) { 331 progress = process_assignment(lin_ctx, ir_assign, &assignments) || 332 progress; 333 } else { 334 kill_for_derefs_visitor kill(&assignments); 335 ir->accept(&kill); 336 } 337 338 if (ir == last) 339 break; 340 } 341 *out_progress = progress; 342 ralloc_free(ctx); 343} 344 345/** 346 * Does a copy propagation pass on the code present in the instruction stream. 347 */ 348bool 349do_dead_code_local(exec_list *instructions) 350{ 351 bool progress = false; 352 353 call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress); 354 355 return progress; 356} 357