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_copy_propagation.cpp
     26  *
     27  * Moves usage of recently-copied variables to the previous copy of
     28  * the variable.
     29  *
     30  * This should reduce the number of MOV instructions in the generated
     31  * programs unless copy propagation is also done on the LIR, and may
     32  * help anyway by triggering other optimizations that live in the HIR.
     33  */
     34 
     35 #include "ir.h"
     36 #include "ir_visitor.h"
     37 #include "ir_basic_block.h"
     38 #include "ir_optimization.h"
     39 #include "glsl_types.h"
     40 
     41 namespace {
     42 
     43 class acp_entry : public exec_node
     44 {
     45 public:
     46    acp_entry(ir_variable *lhs, ir_variable *rhs)
     47    {
     48       assert(lhs);
     49       assert(rhs);
     50       this->lhs = lhs;
     51       this->rhs = rhs;
     52    }
     53 
     54    ir_variable *lhs;
     55    ir_variable *rhs;
     56 };
     57 
     58 
     59 class kill_entry : public exec_node
     60 {
     61 public:
     62    kill_entry(ir_variable *var)
     63    {
     64       assert(var);
     65       this->var = var;
     66    }
     67 
     68    ir_variable *var;
     69 };
     70 
     71 class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
     72 public:
     73    ir_copy_propagation_visitor()
     74    {
     75       progress = false;
     76       mem_ctx = ralloc_context(0);
     77       this->acp = new(mem_ctx) exec_list;
     78       this->kills = new(mem_ctx) exec_list;
     79    }
     80    ~ir_copy_propagation_visitor()
     81    {
     82       ralloc_free(mem_ctx);
     83    }
     84 
     85    virtual ir_visitor_status visit(class ir_dereference_variable *);
     86    virtual ir_visitor_status visit_enter(class ir_loop *);
     87    virtual ir_visitor_status visit_enter(class ir_function_signature *);
     88    virtual ir_visitor_status visit_enter(class ir_function *);
     89    virtual ir_visitor_status visit_leave(class ir_assignment *);
     90    virtual ir_visitor_status visit_enter(class ir_call *);
     91    virtual ir_visitor_status visit_enter(class ir_if *);
     92 
     93    void add_copy(ir_assignment *ir);
     94    void kill(ir_variable *ir);
     95    void handle_if_block(exec_list *instructions);
     96 
     97    /** List of acp_entry: The available copies to propagate */
     98    exec_list *acp;
     99    /**
    100     * List of kill_entry: The variables whose values were killed in this
    101     * block.
    102     */
    103    exec_list *kills;
    104 
    105    bool progress;
    106 
    107    bool killed_all;
    108 
    109    void *mem_ctx;
    110 };
    111 
    112 } /* unnamed namespace */
    113 
    114 ir_visitor_status
    115 ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
    116 {
    117    /* Treat entry into a function signature as a completely separate
    118     * block.  Any instructions at global scope will be shuffled into
    119     * main() at link time, so they're irrelevant to us.
    120     */
    121    exec_list *orig_acp = this->acp;
    122    exec_list *orig_kills = this->kills;
    123    bool orig_killed_all = this->killed_all;
    124 
    125    this->acp = new(mem_ctx) exec_list;
    126    this->kills = new(mem_ctx) exec_list;
    127    this->killed_all = false;
    128 
    129    visit_list_elements(this, &ir->body);
    130 
    131    this->kills = orig_kills;
    132    this->acp = orig_acp;
    133    this->killed_all = orig_killed_all;
    134 
    135    return visit_continue_with_parent;
    136 }
    137 
    138 ir_visitor_status
    139 ir_copy_propagation_visitor::visit_leave(ir_assignment *ir)
    140 {
    141    kill(ir->lhs->variable_referenced());
    142 
    143    add_copy(ir);
    144 
    145    return visit_continue;
    146 }
    147 
    148 ir_visitor_status
    149 ir_copy_propagation_visitor::visit_enter(ir_function *ir)
    150 {
    151    (void) ir;
    152    return visit_continue;
    153 }
    154 
    155 /**
    156  * Replaces dereferences of ACP RHS variables with ACP LHS variables.
    157  *
    158  * This is where the actual copy propagation occurs.  Note that the
    159  * rewriting of ir_dereference means that the ir_dereference instance
    160  * must not be shared by multiple IR operations!
    161  */
    162 ir_visitor_status
    163 ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
    164 {
    165    if (this->in_assignee)
    166       return visit_continue;
    167 
    168    ir_variable *var = ir->var;
    169 
    170    foreach_iter(exec_list_iterator, iter, *this->acp) {
    171       acp_entry *entry = (acp_entry *)iter.get();
    172 
    173       if (var == entry->lhs) {
    174 	 ir->var = entry->rhs;
    175 	 this->progress = true;
    176 	 break;
    177       }
    178    }
    179 
    180    return visit_continue;
    181 }
    182 
    183 
    184 ir_visitor_status
    185 ir_copy_propagation_visitor::visit_enter(ir_call *ir)
    186 {
    187    /* Do copy propagation on call parameters, but skip any out params */
    188    exec_list_iterator sig_param_iter = ir->callee->parameters.iterator();
    189    foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
    190       ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
    191       ir_instruction *ir = (ir_instruction *)iter.get();
    192       if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
    193          ir->accept(this);
    194       }
    195       sig_param_iter.next();
    196    }
    197 
    198    /* Since we're unlinked, we don't (necessarily) know the side effects of
    199     * this call.  So kill all copies.
    200     */
    201    acp->make_empty();
    202    this->killed_all = true;
    203 
    204    return visit_continue_with_parent;
    205 }
    206 
    207 void
    208 ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
    209 {
    210    exec_list *orig_acp = this->acp;
    211    exec_list *orig_kills = this->kills;
    212    bool orig_killed_all = this->killed_all;
    213 
    214    this->acp = new(mem_ctx) exec_list;
    215    this->kills = new(mem_ctx) exec_list;
    216    this->killed_all = false;
    217 
    218    /* Populate the initial acp with a copy of the original */
    219    foreach_iter(exec_list_iterator, iter, *orig_acp) {
    220       acp_entry *a = (acp_entry *)iter.get();
    221       this->acp->push_tail(new(this->mem_ctx) acp_entry(a->lhs, a->rhs));
    222    }
    223 
    224    visit_list_elements(this, instructions);
    225 
    226    if (this->killed_all) {
    227       orig_acp->make_empty();
    228    }
    229 
    230    exec_list *new_kills = this->kills;
    231    this->kills = orig_kills;
    232    this->acp = orig_acp;
    233    this->killed_all = this->killed_all || orig_killed_all;
    234 
    235    foreach_iter(exec_list_iterator, iter, *new_kills) {
    236       kill_entry *k = (kill_entry *)iter.get();
    237       kill(k->var);
    238    }
    239 }
    240 
    241 ir_visitor_status
    242 ir_copy_propagation_visitor::visit_enter(ir_if *ir)
    243 {
    244    ir->condition->accept(this);
    245 
    246    handle_if_block(&ir->then_instructions);
    247    handle_if_block(&ir->else_instructions);
    248 
    249    /* handle_if_block() already descended into the children. */
    250    return visit_continue_with_parent;
    251 }
    252 
    253 ir_visitor_status
    254 ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
    255 {
    256    exec_list *orig_acp = this->acp;
    257    exec_list *orig_kills = this->kills;
    258    bool orig_killed_all = this->killed_all;
    259 
    260    /* FINISHME: For now, the initial acp for loops is totally empty.
    261     * We could go through once, then go through again with the acp
    262     * cloned minus the killed entries after the first run through.
    263     */
    264    this->acp = new(mem_ctx) exec_list;
    265    this->kills = new(mem_ctx) exec_list;
    266    this->killed_all = false;
    267 
    268    visit_list_elements(this, &ir->body_instructions);
    269 
    270    if (this->killed_all) {
    271       orig_acp->make_empty();
    272    }
    273 
    274    exec_list *new_kills = this->kills;
    275    this->kills = orig_kills;
    276    this->acp = orig_acp;
    277    this->killed_all = this->killed_all || orig_killed_all;
    278 
    279    foreach_iter(exec_list_iterator, iter, *new_kills) {
    280       kill_entry *k = (kill_entry *)iter.get();
    281       kill(k->var);
    282    }
    283 
    284    /* already descended into the children. */
    285    return visit_continue_with_parent;
    286 }
    287 
    288 void
    289 ir_copy_propagation_visitor::kill(ir_variable *var)
    290 {
    291    assert(var != NULL);
    292 
    293    /* Remove any entries currently in the ACP for this kill. */
    294    foreach_iter(exec_list_iterator, iter, *acp) {
    295       acp_entry *entry = (acp_entry *)iter.get();
    296 
    297       if (entry->lhs == var || entry->rhs == var) {
    298 	 entry->remove();
    299       }
    300    }
    301 
    302    /* Add the LHS variable to the list of killed variables in this block.
    303     */
    304    this->kills->push_tail(new(this->mem_ctx) kill_entry(var));
    305 }
    306 
    307 /**
    308  * Adds an entry to the available copy list if it's a plain assignment
    309  * of a variable to a variable.
    310  */
    311 void
    312 ir_copy_propagation_visitor::add_copy(ir_assignment *ir)
    313 {
    314    acp_entry *entry;
    315 
    316    if (ir->condition)
    317       return;
    318 
    319    ir_variable *lhs_var = ir->whole_variable_written();
    320    ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
    321 
    322    if ((lhs_var != NULL) && (rhs_var != NULL)) {
    323       if (lhs_var == rhs_var) {
    324 	 /* This is a dumb assignment, but we've conveniently noticed
    325 	  * it here.  Removing it now would mess up the loop iteration
    326 	  * calling us.  Just flag it to not execute, and someone else
    327 	  * will clean up the mess.
    328 	  */
    329 	 ir->condition = new(ralloc_parent(ir)) ir_constant(false);
    330 	 this->progress = true;
    331       } else {
    332 	 entry = new(this->mem_ctx) acp_entry(lhs_var, rhs_var);
    333 	 this->acp->push_tail(entry);
    334       }
    335    }
    336 }
    337 
    338 /**
    339  * Does a copy propagation pass on the code present in the instruction stream.
    340  */
    341 bool
    342 do_copy_propagation(exec_list *instructions)
    343 {
    344    ir_copy_propagation_visitor v;
    345 
    346    visit_list_elements(&v, instructions);
    347 
    348    return v.progress;
    349 }
    350