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  * constant 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, constant, 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 constantright 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 CONSTANTRIGHT 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_constant_propagation.cpp
     26  *
     27  * Tracks assignments of constants to channels of variables, and
     28  * usage of those constant channels with direct usage of the constants.
     29  *
     30  * This can lead to constant folding and algebraic optimizations in
     31  * those later expressions, while causing no increase in instruction
     32  * count (due to constants being generally free to load from a
     33  * constant push buffer or as instruction immediate values) and
     34  * possibly reducing register pressure.
     35  */
     36 
     37 #include "ir.h"
     38 #include "ir_visitor.h"
     39 #include "ir_rvalue_visitor.h"
     40 #include "ir_basic_block.h"
     41 #include "ir_optimization.h"
     42 #include "glsl_types.h"
     43 
     44 class acp_entry : public exec_node
     45 {
     46 public:
     47    acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant)
     48    {
     49       assert(var);
     50       assert(constant);
     51       this->var = var;
     52       this->write_mask = write_mask;
     53       this->constant = constant;
     54    }
     55 
     56    ir_variable *var;
     57    ir_constant *constant;
     58    unsigned write_mask;
     59 };
     60 
     61 
     62 class kill_entry : public exec_node
     63 {
     64 public:
     65    kill_entry(ir_variable *var, unsigned write_mask)
     66    {
     67       assert(var);
     68       this->var = var;
     69       this->write_mask = write_mask;
     70    }
     71 
     72    ir_variable *var;
     73    unsigned write_mask;
     74 };
     75 
     76 class ir_constant_propagation_visitor : public ir_rvalue_visitor {
     77 public:
     78    ir_constant_propagation_visitor()
     79    {
     80       progress = false;
     81       mem_ctx = hieralloc_new(0);
     82       this->acp = new(mem_ctx) exec_list;
     83       this->kills = new(mem_ctx) exec_list;
     84    }
     85    ~ir_constant_propagation_visitor()
     86    {
     87       hieralloc_free(mem_ctx);
     88    }
     89 
     90    virtual ir_visitor_status visit_enter(class ir_loop *);
     91    virtual ir_visitor_status visit_enter(class ir_function_signature *);
     92    virtual ir_visitor_status visit_enter(class ir_function *);
     93    virtual ir_visitor_status visit_leave(class ir_assignment *);
     94    virtual ir_visitor_status visit_enter(class ir_call *);
     95    virtual ir_visitor_status visit_enter(class ir_if *);
     96 
     97    void add_constant(ir_assignment *ir);
     98    void kill(ir_variable *ir, unsigned write_mask);
     99    void handle_if_block(exec_list *instructions);
    100    void handle_rvalue(ir_rvalue **rvalue);
    101 
    102    /** List of acp_entry: The available constants to propagate */
    103    exec_list *acp;
    104 
    105    /**
    106     * List of kill_entry: The masks of variables whose values were
    107     * killed in this block.
    108     */
    109    exec_list *kills;
    110 
    111    bool progress;
    112 
    113    bool killed_all;
    114 
    115    void *mem_ctx;
    116 };
    117 
    118 
    119 void
    120 ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue)
    121 {
    122    if (this->in_assignee || !*rvalue)
    123       return;
    124 
    125    const glsl_type *type = (*rvalue)->type;
    126    if (!type->is_scalar() && !type->is_vector())
    127       return;
    128 
    129    ir_swizzle *swiz = NULL;
    130    ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
    131    if (!deref) {
    132       swiz = (*rvalue)->as_swizzle();
    133       if (!swiz)
    134 	 return;
    135 
    136       deref = swiz->val->as_dereference_variable();
    137       if (!deref)
    138 	 return;
    139    }
    140 
    141    ir_constant_data data;
    142    memset(&data, 0, sizeof(data));
    143 
    144    for (unsigned int i = 0; i < type->components(); i++) {
    145       int channel;
    146       acp_entry *found = NULL;
    147 
    148       if (swiz) {
    149 	 switch (i) {
    150 	 case 0: channel = swiz->mask.x; break;
    151 	 case 1: channel = swiz->mask.y; break;
    152 	 case 2: channel = swiz->mask.z; break;
    153 	 case 3: channel = swiz->mask.w; break;
    154 	 default: assert(!"shouldn't be reached"); channel = 0; break;
    155 	 }
    156       } else {
    157 	 channel = i;
    158       }
    159 
    160       foreach_iter(exec_list_iterator, iter, *this->acp) {
    161 	 acp_entry *entry = (acp_entry *)iter.get();
    162 	 if (entry->var == deref->var && entry->write_mask & (1 << channel)) {
    163 	    found = entry;
    164 	    break;
    165 	 }
    166       }
    167 
    168       if (!found)
    169 	 return;
    170 
    171       int rhs_channel = 0;
    172       for (int j = 0; j < 4; j++) {
    173 	 if (j == channel)
    174 	    break;
    175 	 if (found->write_mask & (1 << j))
    176 	    rhs_channel++;
    177       }
    178 
    179       switch (type->base_type) {
    180       case GLSL_TYPE_FLOAT:
    181 	 data.f[i] = found->constant->value.f[rhs_channel];
    182 	 break;
    183       case GLSL_TYPE_INT:
    184 	 data.i[i] = found->constant->value.i[rhs_channel];
    185 	 break;
    186       case GLSL_TYPE_UINT:
    187 	 data.u[i] = found->constant->value.u[rhs_channel];
    188 	 break;
    189       case GLSL_TYPE_BOOL:
    190 	 data.b[i] = found->constant->value.b[rhs_channel];
    191 	 break;
    192       default:
    193 	 assert(!"not reached");
    194 	 break;
    195       }
    196    }
    197 
    198    *rvalue = new(hieralloc_parent(deref)) ir_constant(type, &data);
    199    this->progress = true;
    200 }
    201 
    202 ir_visitor_status
    203 ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)
    204 {
    205    /* Treat entry into a function signature as a completely separate
    206     * block.  Any instructions at global scope will be shuffled into
    207     * main() at link time, so they're irrelevant to us.
    208     */
    209    exec_list *orig_acp = this->acp;
    210    exec_list *orig_kills = this->kills;
    211    bool orig_killed_all = this->killed_all;
    212 
    213    this->acp = new(mem_ctx) exec_list;
    214    this->kills = new(mem_ctx) exec_list;
    215    this->killed_all = false;
    216 
    217    visit_list_elements(this, &ir->body);
    218 
    219    this->kills = orig_kills;
    220    this->acp = orig_acp;
    221    this->killed_all = orig_killed_all;
    222 
    223    return visit_continue_with_parent;
    224 }
    225 
    226 ir_visitor_status
    227 ir_constant_propagation_visitor::visit_leave(ir_assignment *ir)
    228 {
    229    if (this->in_assignee)
    230       return visit_continue;
    231 
    232    kill(ir->lhs->variable_referenced(), ir->write_mask);
    233 
    234    add_constant(ir);
    235 
    236    return visit_continue;
    237 }
    238 
    239 ir_visitor_status
    240 ir_constant_propagation_visitor::visit_enter(ir_function *ir)
    241 {
    242    (void) ir;
    243    return visit_continue;
    244 }
    245 
    246 ir_visitor_status
    247 ir_constant_propagation_visitor::visit_enter(ir_call *ir)
    248 {
    249    /* Do constant propagation on call parameters, but skip any out params */
    250    exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
    251    foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
    252       ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
    253       ir_rvalue *param = (ir_rvalue *)iter.get();
    254       if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
    255 	 ir_rvalue *new_param = param;
    256 	 handle_rvalue(&new_param);
    257          if (new_param != param)
    258 	    param->replace_with(new_param);
    259 	 else
    260 	    param->accept(this);
    261       }
    262       sig_param_iter.next();
    263    }
    264 
    265    /* Since we're unlinked, we don't (necssarily) know the side effects of
    266     * this call.  So kill all copies.
    267     */
    268    acp->make_empty();
    269    this->killed_all = true;
    270 
    271    return visit_continue_with_parent;
    272 }
    273 
    274 void
    275 ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)
    276 {
    277    exec_list *orig_acp = this->acp;
    278    exec_list *orig_kills = this->kills;
    279    bool orig_killed_all = this->killed_all;
    280 
    281    this->acp = new(mem_ctx) exec_list;
    282    this->kills = new(mem_ctx) exec_list;
    283    this->killed_all = false;
    284 
    285    /* Populate the initial acp with a constant of the original */
    286    foreach_iter(exec_list_iterator, iter, *orig_acp) {
    287       acp_entry *a = (acp_entry *)iter.get();
    288       this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask,
    289 							a->constant));
    290    }
    291 
    292    visit_list_elements(this, instructions);
    293 
    294    if (this->killed_all) {
    295       orig_acp->make_empty();
    296    }
    297 
    298    exec_list *new_kills = this->kills;
    299    this->kills = orig_kills;
    300    this->acp = orig_acp;
    301    this->killed_all = this->killed_all || orig_killed_all;
    302 
    303    foreach_iter(exec_list_iterator, iter, *new_kills) {
    304       kill_entry *k = (kill_entry *)iter.get();
    305       kill(k->var, k->write_mask);
    306    }
    307 }
    308 
    309 ir_visitor_status
    310 ir_constant_propagation_visitor::visit_enter(ir_if *ir)
    311 {
    312    ir->condition->accept(this);
    313    handle_rvalue(&ir->condition);
    314 
    315    handle_if_block(&ir->then_instructions);
    316    handle_if_block(&ir->else_instructions);
    317 
    318    /* handle_if_block() already descended into the children. */
    319    return visit_continue_with_parent;
    320 }
    321 
    322 ir_visitor_status
    323 ir_constant_propagation_visitor::visit_enter(ir_loop *ir)
    324 {
    325    exec_list *orig_acp = this->acp;
    326    exec_list *orig_kills = this->kills;
    327    bool orig_killed_all = this->killed_all;
    328 
    329    /* FINISHME: For now, the initial acp for loops is totally empty.
    330     * We could go through once, then go through again with the acp
    331     * cloned minus the killed entries after the first run through.
    332     */
    333    this->acp = new(mem_ctx) exec_list;
    334    this->kills = new(mem_ctx) exec_list;
    335    this->killed_all = false;
    336 
    337    visit_list_elements(this, &ir->body_instructions);
    338 
    339    if (this->killed_all) {
    340       orig_acp->make_empty();
    341    }
    342 
    343    exec_list *new_kills = this->kills;
    344    this->kills = orig_kills;
    345    this->acp = orig_acp;
    346    this->killed_all = this->killed_all || orig_killed_all;
    347 
    348    foreach_iter(exec_list_iterator, iter, *new_kills) {
    349       kill_entry *k = (kill_entry *)iter.get();
    350       kill(k->var, k->write_mask);
    351    }
    352 
    353    /* already descended into the children. */
    354    return visit_continue_with_parent;
    355 }
    356 
    357 void
    358 ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask)
    359 {
    360    assert(var != NULL);
    361 
    362    /* We don't track non-vectors. */
    363    if (!var->type->is_vector() && !var->type->is_scalar())
    364       return;
    365 
    366    /* Remove any entries currently in the ACP for this kill. */
    367    foreach_iter(exec_list_iterator, iter, *this->acp) {
    368       acp_entry *entry = (acp_entry *)iter.get();
    369 
    370       if (entry->var == var) {
    371 	 entry->write_mask &= ~write_mask;
    372 	 if (entry->write_mask == 0)
    373 	    entry->remove();
    374       }
    375    }
    376 
    377    /* Add this writemask of the variable to the list of killed
    378     * variables in this block.
    379     */
    380    foreach_iter(exec_list_iterator, iter, *this->kills) {
    381       kill_entry *entry = (kill_entry *)iter.get();
    382 
    383       if (entry->var == var) {
    384 	 entry->write_mask |= write_mask;
    385 	 return;
    386       }
    387    }
    388    /* Not already in the list.  Make new entry. */
    389    this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask));
    390 }
    391 
    392 /**
    393  * Adds an entry to the available constant list if it's a plain assignment
    394  * of a variable to a variable.
    395  */
    396 void
    397 ir_constant_propagation_visitor::add_constant(ir_assignment *ir)
    398 {
    399    acp_entry *entry;
    400 
    401    if (ir->condition) {
    402       ir_constant *condition = ir->condition->as_constant();
    403       if (!condition || !condition->value.b[0])
    404 	 return;
    405    }
    406 
    407    if (!ir->write_mask)
    408       return;
    409 
    410    ir_dereference_variable *deref = ir->lhs->as_dereference_variable();
    411    ir_constant *constant = ir->rhs->as_constant();
    412 
    413    if (!deref || !constant)
    414       return;
    415 
    416    /* Only do constant propagation on vectors.  Constant matrices,
    417     * arrays, or structures would require more work elsewhere.
    418     */
    419    if (!deref->var->type->is_vector() && !deref->var->type->is_scalar())
    420       return;
    421 
    422    entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant);
    423    this->acp->push_tail(entry);
    424 }
    425 
    426 /**
    427  * Does a constant propagation pass on the code present in the instruction stream.
    428  */
    429 bool
    430 do_constant_propagation(exec_list *instructions)
    431 {
    432    ir_constant_propagation_visitor v;
    433 
    434    visit_list_elements(&v, instructions);
    435 
    436    return v.progress;
    437 }
    438