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 lower_vec_index_to_cond_assign.cpp
     26  *
     27  * Turns indexing into vector types to a series of conditional moves
     28  * of each channel's swizzle into a temporary.
     29  *
     30  * Most GPUs don't have a native way to do this operation, and this
     31  * works around that.  For drivers using both this pass and
     32  * ir_vec_index_to_swizzle, there's a risk that this pass will happen
     33  * before sufficient constant folding to find that the array index is
     34  * constant.  However, we hope that other optimization passes,
     35  * particularly constant folding of assignment conditions and copy
     36  * propagation, will result in the same code in the end.
     37  */
     38 
     39 #include "ir.h"
     40 #include "ir_visitor.h"
     41 #include "ir_optimization.h"
     42 #include "glsl_types.h"
     43 
     44 /**
     45  * Visitor class for replacing expressions with ir_constant values.
     46  */
     47 
     48 class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor {
     49 public:
     50    ir_vec_index_to_cond_assign_visitor()
     51    {
     52       progress = false;
     53    }
     54 
     55    ir_rvalue *convert_vec_index_to_cond_assign(ir_rvalue *val);
     56 
     57    virtual ir_visitor_status visit_enter(ir_expression *);
     58    virtual ir_visitor_status visit_enter(ir_swizzle *);
     59    virtual ir_visitor_status visit_leave(ir_assignment *);
     60    virtual ir_visitor_status visit_enter(ir_return *);
     61    virtual ir_visitor_status visit_enter(ir_call *);
     62    virtual ir_visitor_status visit_enter(ir_if *);
     63 
     64    bool progress;
     65 };
     66 
     67 ir_rvalue *
     68 ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue *ir)
     69 {
     70    ir_dereference_array *orig_deref = ir->as_dereference_array();
     71    ir_assignment *assign;
     72    ir_variable *index, *var;
     73    ir_dereference *deref;
     74    ir_expression *condition;
     75    ir_swizzle *swizzle;
     76    int i;
     77 
     78    if (!orig_deref)
     79       return ir;
     80 
     81    if (orig_deref->array->type->is_matrix() ||
     82        orig_deref->array->type->is_array())
     83       return ir;
     84 
     85    void *mem_ctx = hieralloc_parent(ir);
     86 
     87    assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
     88 
     89    /* Store the index to a temporary to avoid reusing its tree. */
     90    index = new(base_ir) ir_variable(glsl_type::int_type,
     91 				    "vec_index_tmp_i",
     92 				    ir_var_temporary);
     93    base_ir->insert_before(index);
     94    deref = new(base_ir) ir_dereference_variable(index);
     95    assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL);
     96    base_ir->insert_before(assign);
     97 
     98    /* Temporary where we store whichever value we swizzle out. */
     99    var = new(base_ir) ir_variable(ir->type, "vec_index_tmp_v",
    100 				  ir_var_temporary);
    101    base_ir->insert_before(var);
    102 
    103    /* Generate a conditional move of each vector element to the temp. */
    104    for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
    105       deref = new(base_ir) ir_dereference_variable(index);
    106       condition = new(base_ir) ir_expression(ir_binop_equal,
    107 					     glsl_type::bool_type,
    108 					     deref,
    109 					     new(base_ir) ir_constant(i));
    110 
    111       /* Just clone the rest of the deref chain when trying to get at the
    112        * underlying variable.
    113        */
    114       swizzle = new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
    115 					i, 0, 0, 0, 1);
    116 
    117       deref = new(base_ir) ir_dereference_variable(var);
    118       assign = new(base_ir) ir_assignment(deref, swizzle, condition);
    119       base_ir->insert_before(assign);
    120    }
    121 
    122    this->progress = true;
    123    return new(base_ir) ir_dereference_variable(var);
    124 }
    125 
    126 ir_visitor_status
    127 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir)
    128 {
    129    unsigned int i;
    130 
    131    for (i = 0; i < ir->get_num_operands(); i++) {
    132       ir->operands[i] = convert_vec_index_to_cond_assign(ir->operands[i]);
    133    }
    134 
    135    return visit_continue;
    136 }
    137 
    138 ir_visitor_status
    139 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir)
    140 {
    141    /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which
    142     * the result of indexing a vector is.  But maybe at some point we'll end up
    143     * using swizzling of scalars for vector construction.
    144     */
    145    ir->val = convert_vec_index_to_cond_assign(ir->val);
    146 
    147    return visit_continue;
    148 }
    149 
    150 ir_visitor_status
    151 ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir)
    152 {
    153    ir_variable *index, *var;
    154    ir_dereference_variable *deref;
    155    ir_assignment *assign;
    156    int i;
    157 
    158    ir->rhs = convert_vec_index_to_cond_assign(ir->rhs);
    159    if (ir->condition)
    160       ir->condition = convert_vec_index_to_cond_assign(ir->condition);
    161 
    162    /* Last, handle the LHS */
    163    ir_dereference_array *orig_deref = ir->lhs->as_dereference_array();
    164 
    165    if (!orig_deref ||
    166        orig_deref->array->type->is_matrix() ||
    167        orig_deref->array->type->is_array())
    168       return visit_continue;
    169 
    170    void *mem_ctx = hieralloc_parent(ir);
    171 
    172    assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
    173 
    174    /* Store the index to a temporary to avoid reusing its tree. */
    175    index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i",
    176 			       ir_var_temporary);
    177    ir->insert_before(index);
    178    deref = new(ir) ir_dereference_variable(index);
    179    assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL);
    180    ir->insert_before(assign);
    181 
    182    /* Store the RHS to a temporary to avoid reusing its tree. */
    183    var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v",
    184 			     ir_var_temporary);
    185    ir->insert_before(var);
    186    deref = new(ir) ir_dereference_variable(var);
    187    assign = new(ir) ir_assignment(deref, ir->rhs, NULL);
    188    ir->insert_before(assign);
    189 
    190    /* Generate a conditional move of each vector element to the temp. */
    191    for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
    192       ir_rvalue *condition, *swizzle;
    193 
    194       deref = new(ir) ir_dereference_variable(index);
    195       condition = new(ir) ir_expression(ir_binop_equal,
    196 					glsl_type::bool_type,
    197 					deref,
    198 					new(ir) ir_constant(i));
    199 
    200       /* Just clone the rest of the deref chain when trying to get at the
    201        * underlying variable.
    202        */
    203       swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
    204 				   i, 0, 0, 0, 1);
    205 
    206       deref = new(ir) ir_dereference_variable(var);
    207       assign = new(ir) ir_assignment(swizzle, deref, condition);
    208       ir->insert_before(assign);
    209    }
    210    ir->remove();
    211 
    212    this->progress = true;
    213 
    214    return visit_continue;
    215 }
    216 
    217 ir_visitor_status
    218 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir)
    219 {
    220    foreach_iter(exec_list_iterator, iter, *ir) {
    221       ir_rvalue *param = (ir_rvalue *)iter.get();
    222       ir_rvalue *new_param = convert_vec_index_to_cond_assign(param);
    223 
    224       if (new_param != param) {
    225 	 param->replace_with(new_param);
    226       }
    227    }
    228 
    229    return visit_continue;
    230 }
    231 
    232 ir_visitor_status
    233 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir)
    234 {
    235    if (ir->value) {
    236       ir->value = convert_vec_index_to_cond_assign(ir->value);
    237    }
    238 
    239    return visit_continue;
    240 }
    241 
    242 ir_visitor_status
    243 ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir)
    244 {
    245    ir->condition = convert_vec_index_to_cond_assign(ir->condition);
    246 
    247    return visit_continue;
    248 }
    249 
    250 bool
    251 do_vec_index_to_cond_assign(exec_list *instructions)
    252 {
    253    ir_vec_index_to_cond_assign_visitor v;
    254 
    255    visit_list_elements(&v, instructions);
    256 
    257    return v.progress;
    258 }
    259