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