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_function_inlining.cpp
     26  *
     27  * Replaces calls to functions with the body of the function.
     28  */
     29 
     30 #include "ir.h"
     31 #include "ir_visitor.h"
     32 #include "ir_function_inlining.h"
     33 #include "ir_expression_flattening.h"
     34 #include "glsl_types.h"
     35 #include "program/hash_table.h"
     36 
     37 static void
     38 do_sampler_replacement(exec_list *instructions,
     39 		       ir_variable *sampler,
     40 		       ir_dereference *deref);
     41 
     42 namespace {
     43 
     44 class ir_function_inlining_visitor : public ir_hierarchical_visitor {
     45 public:
     46    ir_function_inlining_visitor()
     47    {
     48       progress = false;
     49    }
     50 
     51    virtual ~ir_function_inlining_visitor()
     52    {
     53       /* empty */
     54    }
     55 
     56    virtual ir_visitor_status visit_enter(ir_expression *);
     57    virtual ir_visitor_status visit_enter(ir_call *);
     58    virtual ir_visitor_status visit_enter(ir_return *);
     59    virtual ir_visitor_status visit_enter(ir_texture *);
     60    virtual ir_visitor_status visit_enter(ir_swizzle *);
     61 
     62    bool progress;
     63 };
     64 
     65 } /* unnamed namespace */
     66 
     67 bool
     68 do_function_inlining(exec_list *instructions)
     69 {
     70    ir_function_inlining_visitor v;
     71 
     72    v.run(instructions);
     73 
     74    return v.progress;
     75 }
     76 
     77 static void
     78 replace_return_with_assignment(ir_instruction *ir, void *data)
     79 {
     80    void *ctx = ralloc_parent(ir);
     81    ir_dereference *orig_deref = (ir_dereference *) data;
     82    ir_return *ret = ir->as_return();
     83 
     84    if (ret) {
     85       if (ret->value) {
     86 	 ir_rvalue *lhs = orig_deref->clone(ctx, NULL);
     87 	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
     88       } else {
     89 	 /* un-valued return has to be the last return, or we shouldn't
     90 	  * have reached here. (see can_inline()).
     91 	  */
     92 	 assert(ret->next->is_tail_sentinel());
     93 	 ret->remove();
     94       }
     95    }
     96 }
     97 
     98 void
     99 ir_call::generate_inline(ir_instruction *next_ir)
    100 {
    101    void *ctx = ralloc_parent(this);
    102    ir_variable **parameters;
    103    int num_parameters;
    104    int i;
    105    struct hash_table *ht;
    106 
    107    ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
    108 
    109    num_parameters = 0;
    110    foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
    111       num_parameters++;
    112 
    113    parameters = new ir_variable *[num_parameters];
    114 
    115    /* Generate the declarations for the parameters to our inlined code,
    116     * and set up the mapping of real function body variables to ours.
    117     */
    118    i = 0;
    119    exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
    120    exec_list_iterator param_iter = this->actual_parameters.iterator();
    121    for (i = 0; i < num_parameters; i++) {
    122       ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
    123       ir_rvalue *param = (ir_rvalue *) param_iter.get();
    124 
    125       /* Generate a new variable for the parameter. */
    126       if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
    127 	 /* For samplers, we want the inlined sampler references
    128 	  * referencing the passed in sampler variable, since that
    129 	  * will have the location information, which an assignment of
    130 	  * a sampler wouldn't.  Fix it up below.
    131 	  */
    132 	 parameters[i] = NULL;
    133       } else {
    134 	 parameters[i] = sig_param->clone(ctx, ht);
    135 	 parameters[i]->mode = ir_var_auto;
    136 
    137 	 /* Remove the read-only decoration becuase we're going to write
    138 	  * directly to this variable.  If the cloned variable is left
    139 	  * read-only and the inlined function is inside a loop, the loop
    140 	  * analysis code will get confused.
    141 	  */
    142 	 parameters[i]->read_only = false;
    143 	 next_ir->insert_before(parameters[i]);
    144       }
    145 
    146       /* Move the actual param into our param variable if it's an 'in' type. */
    147       if (parameters[i] && (sig_param->mode == ir_var_in ||
    148 			    sig_param->mode == ir_var_const_in ||
    149 			    sig_param->mode == ir_var_inout)) {
    150 	 ir_assignment *assign;
    151 
    152 	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
    153 					 param, NULL);
    154 	 next_ir->insert_before(assign);
    155       }
    156 
    157       sig_param_iter.next();
    158       param_iter.next();
    159    }
    160 
    161    exec_list new_instructions;
    162 
    163    /* Generate the inlined body of the function to a new list */
    164    foreach_iter(exec_list_iterator, iter, callee->body) {
    165       ir_instruction *ir = (ir_instruction *)iter.get();
    166       ir_instruction *new_ir = ir->clone(ctx, ht);
    167 
    168       new_instructions.push_tail(new_ir);
    169       visit_tree(new_ir, replace_return_with_assignment, this->return_deref);
    170    }
    171 
    172    /* If any samplers were passed in, replace any deref of the sampler
    173     * with a deref of the sampler argument.
    174     */
    175    param_iter = this->actual_parameters.iterator();
    176    sig_param_iter = this->callee->parameters.iterator();
    177    for (i = 0; i < num_parameters; i++) {
    178       ir_instruction *const param = (ir_instruction *) param_iter.get();
    179       ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
    180 
    181       if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
    182 	 ir_dereference *deref = param->as_dereference();
    183 
    184 	 assert(deref);
    185 	 do_sampler_replacement(&new_instructions, sig_param, deref);
    186       }
    187       param_iter.next();
    188       sig_param_iter.next();
    189    }
    190 
    191    /* Now push those new instructions in. */
    192    next_ir->insert_before(&new_instructions);
    193 
    194    /* Copy back the value of any 'out' parameters from the function body
    195     * variables to our own.
    196     */
    197    i = 0;
    198    param_iter = this->actual_parameters.iterator();
    199    sig_param_iter = this->callee->parameters.iterator();
    200    for (i = 0; i < num_parameters; i++) {
    201       ir_instruction *const param = (ir_instruction *) param_iter.get();
    202       const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
    203 
    204       /* Move our param variable into the actual param if it's an 'out' type. */
    205       if (parameters[i] && (sig_param->mode == ir_var_out ||
    206 			    sig_param->mode == ir_var_inout)) {
    207 	 ir_assignment *assign;
    208 
    209 	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
    210 					 new(ctx) ir_dereference_variable(parameters[i]),
    211 					 NULL);
    212 	 next_ir->insert_before(assign);
    213       }
    214 
    215       param_iter.next();
    216       sig_param_iter.next();
    217    }
    218 
    219    delete [] parameters;
    220 
    221    hash_table_dtor(ht);
    222 }
    223 
    224 
    225 ir_visitor_status
    226 ir_function_inlining_visitor::visit_enter(ir_expression *ir)
    227 {
    228    (void) ir;
    229    return visit_continue_with_parent;
    230 }
    231 
    232 
    233 ir_visitor_status
    234 ir_function_inlining_visitor::visit_enter(ir_return *ir)
    235 {
    236    (void) ir;
    237    return visit_continue_with_parent;
    238 }
    239 
    240 
    241 ir_visitor_status
    242 ir_function_inlining_visitor::visit_enter(ir_texture *ir)
    243 {
    244    (void) ir;
    245    return visit_continue_with_parent;
    246 }
    247 
    248 
    249 ir_visitor_status
    250 ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
    251 {
    252    (void) ir;
    253    return visit_continue_with_parent;
    254 }
    255 
    256 
    257 ir_visitor_status
    258 ir_function_inlining_visitor::visit_enter(ir_call *ir)
    259 {
    260    if (can_inline(ir)) {
    261       ir->generate_inline(ir);
    262       ir->remove();
    263       this->progress = true;
    264    }
    265 
    266    return visit_continue;
    267 }
    268 
    269 
    270 /**
    271  * Replaces references to the "sampler" variable with a clone of "deref."
    272  *
    273  * From the spec, samplers can appear in the tree as function
    274  * (non-out) parameters and as the result of array indexing and
    275  * structure field selection.  In our builtin implementation, they
    276  * also appear in the sampler field of an ir_tex instruction.
    277  */
    278 
    279 class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
    280 public:
    281    ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
    282    {
    283       this->sampler = sampler;
    284       this->deref = deref;
    285    }
    286 
    287    virtual ~ir_sampler_replacement_visitor()
    288    {
    289    }
    290 
    291    virtual ir_visitor_status visit_leave(ir_call *);
    292    virtual ir_visitor_status visit_leave(ir_dereference_array *);
    293    virtual ir_visitor_status visit_leave(ir_dereference_record *);
    294    virtual ir_visitor_status visit_leave(ir_texture *);
    295 
    296    void replace_deref(ir_dereference **deref);
    297    void replace_rvalue(ir_rvalue **rvalue);
    298 
    299    ir_variable *sampler;
    300    ir_dereference *deref;
    301 };
    302 
    303 void
    304 ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
    305 {
    306    ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
    307    if (deref_var && deref_var->var == this->sampler) {
    308       *deref = this->deref->clone(ralloc_parent(*deref), NULL);
    309    }
    310 }
    311 
    312 void
    313 ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
    314 {
    315    if (!*rvalue)
    316       return;
    317 
    318    ir_dereference *deref = (*rvalue)->as_dereference();
    319 
    320    if (!deref)
    321       return;
    322 
    323    replace_deref(&deref);
    324    *rvalue = deref;
    325 }
    326 
    327 ir_visitor_status
    328 ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
    329 {
    330    replace_deref(&ir->sampler);
    331 
    332    return visit_continue;
    333 }
    334 
    335 ir_visitor_status
    336 ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
    337 {
    338    replace_rvalue(&ir->array);
    339    return visit_continue;
    340 }
    341 
    342 ir_visitor_status
    343 ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
    344 {
    345    replace_rvalue(&ir->record);
    346    return visit_continue;
    347 }
    348 
    349 ir_visitor_status
    350 ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
    351 {
    352    foreach_iter(exec_list_iterator, iter, *ir) {
    353       ir_rvalue *param = (ir_rvalue *)iter.get();
    354       ir_rvalue *new_param = param;
    355       replace_rvalue(&new_param);
    356 
    357       if (new_param != param) {
    358 	 param->replace_with(new_param);
    359       }
    360    }
    361    return visit_continue;
    362 }
    363 
    364 static void
    365 do_sampler_replacement(exec_list *instructions,
    366 		       ir_variable *sampler,
    367 		       ir_dereference *deref)
    368 {
    369    ir_sampler_replacement_visitor v(sampler, deref);
    370 
    371    visit_list_elements(&v, instructions);
    372 }
    373