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_mat_op_to_vec.cpp
     26  *
     27  * Breaks matrix operation expressions down to a series of vector operations.
     28  *
     29  * Generally this is how we have to codegen matrix operations for a
     30  * GPU, so this gives us the chance to constant fold operations on a
     31  * column or row.
     32  */
     33 
     34 #include "ir.h"
     35 #include "ir_expression_flattening.h"
     36 #include "compiler/glsl_types.h"
     37 
     38 namespace {
     39 
     40 class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
     41 public:
     42    ir_mat_op_to_vec_visitor()
     43    {
     44       this->made_progress = false;
     45       this->mem_ctx = NULL;
     46    }
     47 
     48    ir_visitor_status visit_leave(ir_assignment *);
     49 
     50    ir_dereference *get_column(ir_dereference *val, int col);
     51    ir_rvalue *get_element(ir_dereference *val, int col, int row);
     52 
     53    void do_mul_mat_mat(ir_dereference *result,
     54 		       ir_dereference *a, ir_dereference *b);
     55    void do_mul_mat_vec(ir_dereference *result,
     56 		       ir_dereference *a, ir_dereference *b);
     57    void do_mul_vec_mat(ir_dereference *result,
     58 		       ir_dereference *a, ir_dereference *b);
     59    void do_mul_mat_scalar(ir_dereference *result,
     60 			  ir_dereference *a, ir_dereference *b);
     61    void do_equal_mat_mat(ir_dereference *result, ir_dereference *a,
     62 			 ir_dereference *b, bool test_equal);
     63 
     64    void *mem_ctx;
     65    bool made_progress;
     66 };
     67 
     68 } /* anonymous namespace */
     69 
     70 static bool
     71 mat_op_to_vec_predicate(ir_instruction *ir)
     72 {
     73    ir_expression *expr = ir->as_expression();
     74    unsigned int i;
     75 
     76    if (!expr)
     77       return false;
     78 
     79    for (i = 0; i < expr->get_num_operands(); i++) {
     80       if (expr->operands[i]->type->is_matrix())
     81 	 return true;
     82    }
     83 
     84    return false;
     85 }
     86 
     87 bool
     88 do_mat_op_to_vec(exec_list *instructions)
     89 {
     90    ir_mat_op_to_vec_visitor v;
     91 
     92    /* Pull out any matrix expression to a separate assignment to a
     93     * temp.  This will make our handling of the breakdown to
     94     * operations on the matrix's vector components much easier.
     95     */
     96    do_expression_flattening(instructions, mat_op_to_vec_predicate);
     97 
     98    visit_list_elements(&v, instructions);
     99 
    100    return v.made_progress;
    101 }
    102 
    103 ir_rvalue *
    104 ir_mat_op_to_vec_visitor::get_element(ir_dereference *val, int col, int row)
    105 {
    106    val = get_column(val, col);
    107 
    108    return new(mem_ctx) ir_swizzle(val, row, 0, 0, 0, 1);
    109 }
    110 
    111 ir_dereference *
    112 ir_mat_op_to_vec_visitor::get_column(ir_dereference *val, int row)
    113 {
    114    val = val->clone(mem_ctx, NULL);
    115 
    116    if (val->type->is_matrix()) {
    117       val = new(mem_ctx) ir_dereference_array(val,
    118 					      new(mem_ctx) ir_constant(row));
    119    }
    120 
    121    return val;
    122 }
    123 
    124 void
    125 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result,
    126 					 ir_dereference *a,
    127 					 ir_dereference *b)
    128 {
    129    unsigned b_col, i;
    130    ir_assignment *assign;
    131    ir_expression *expr;
    132 
    133    for (b_col = 0; b_col < b->type->matrix_columns; b_col++) {
    134       /* first column */
    135       expr = new(mem_ctx) ir_expression(ir_binop_mul,
    136 					get_column(a, 0),
    137 					get_element(b, b_col, 0));
    138 
    139       /* following columns */
    140       for (i = 1; i < a->type->matrix_columns; i++) {
    141 	 ir_expression *mul_expr;
    142 
    143 	 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    144 					       get_column(a, i),
    145 					       get_element(b, b_col, i));
    146 	 expr = new(mem_ctx) ir_expression(ir_binop_add,
    147 					   expr,
    148 					   mul_expr);
    149       }
    150 
    151       assign = new(mem_ctx) ir_assignment(get_column(result, b_col), expr);
    152       base_ir->insert_before(assign);
    153    }
    154 }
    155 
    156 void
    157 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result,
    158 					 ir_dereference *a,
    159 					 ir_dereference *b)
    160 {
    161    unsigned i;
    162    ir_assignment *assign;
    163    ir_expression *expr;
    164 
    165    /* first column */
    166    expr = new(mem_ctx) ir_expression(ir_binop_mul,
    167 				     get_column(a, 0),
    168 				     get_element(b, 0, 0));
    169 
    170    /* following columns */
    171    for (i = 1; i < a->type->matrix_columns; i++) {
    172       ir_expression *mul_expr;
    173 
    174       mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    175 					    get_column(a, i),
    176 					    get_element(b, 0, i));
    177       expr = new(mem_ctx) ir_expression(ir_binop_add, expr, mul_expr);
    178    }
    179 
    180    result = result->clone(mem_ctx, NULL);
    181    assign = new(mem_ctx) ir_assignment(result, expr);
    182    base_ir->insert_before(assign);
    183 }
    184 
    185 void
    186 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result,
    187 					 ir_dereference *a,
    188 					 ir_dereference *b)
    189 {
    190    unsigned i;
    191 
    192    for (i = 0; i < b->type->matrix_columns; i++) {
    193       ir_rvalue *column_result;
    194       ir_expression *column_expr;
    195       ir_assignment *column_assign;
    196 
    197       column_result = result->clone(mem_ctx, NULL);
    198       column_result = new(mem_ctx) ir_swizzle(column_result, i, 0, 0, 0, 1);
    199 
    200       column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
    201 					       a->clone(mem_ctx, NULL),
    202 					       get_column(b, i));
    203 
    204       column_assign = new(mem_ctx) ir_assignment(column_result,
    205 						 column_expr);
    206       base_ir->insert_before(column_assign);
    207    }
    208 }
    209 
    210 void
    211 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result,
    212 					    ir_dereference *a,
    213 					    ir_dereference *b)
    214 {
    215    unsigned i;
    216 
    217    for (i = 0; i < a->type->matrix_columns; i++) {
    218       ir_expression *column_expr;
    219       ir_assignment *column_assign;
    220 
    221       column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    222 					       get_column(a, i),
    223 					       b->clone(mem_ctx, NULL));
    224 
    225       column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    226 						 column_expr);
    227       base_ir->insert_before(column_assign);
    228    }
    229 }
    230 
    231 void
    232 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference *result,
    233 					   ir_dereference *a,
    234 					   ir_dereference *b,
    235 					   bool test_equal)
    236 {
    237    /* This essentially implements the following GLSL:
    238     *
    239     * bool equal(mat4 a, mat4 b)
    240     * {
    241     *   return !any(bvec4(a[0] != b[0],
    242     *                     a[1] != b[1],
    243     *                     a[2] != b[2],
    244     *                     a[3] != b[3]);
    245     * }
    246     *
    247     * bool nequal(mat4 a, mat4 b)
    248     * {
    249     *   return any(bvec4(a[0] != b[0],
    250     *                    a[1] != b[1],
    251     *                    a[2] != b[2],
    252     *                    a[3] != b[3]);
    253     * }
    254     */
    255    const unsigned columns = a->type->matrix_columns;
    256    const glsl_type *const bvec_type =
    257       glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
    258 
    259    ir_variable *const tmp_bvec =
    260       new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
    261 				     ir_var_temporary);
    262    this->base_ir->insert_before(tmp_bvec);
    263 
    264    for (unsigned i = 0; i < columns; i++) {
    265       ir_expression *const cmp =
    266 	 new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
    267 					  get_column(a, i),
    268 					  get_column(b, i));
    269 
    270       ir_dereference *const lhs =
    271 	 new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    272 
    273       ir_assignment *const assign =
    274 	 new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
    275 
    276       this->base_ir->insert_before(assign);
    277    }
    278 
    279    ir_rvalue *const val = new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    280    uint8_t vec_elems = val->type->vector_elements;
    281    ir_expression *any =
    282       new(this->mem_ctx) ir_expression(ir_binop_any_nequal, val,
    283                                        new(this->mem_ctx) ir_constant(false,
    284                                                                       vec_elems));
    285 
    286    if (test_equal)
    287       any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, any);
    288 
    289    ir_assignment *const assign =
    290       new(mem_ctx) ir_assignment(result->clone(mem_ctx, NULL), any);
    291    base_ir->insert_before(assign);
    292 }
    293 
    294 static bool
    295 has_matrix_operand(const ir_expression *expr, unsigned &columns)
    296 {
    297    for (unsigned i = 0; i < expr->get_num_operands(); i++) {
    298       if (expr->operands[i]->type->is_matrix()) {
    299 	 columns = expr->operands[i]->type->matrix_columns;
    300 	 return true;
    301       }
    302    }
    303 
    304    return false;
    305 }
    306 
    307 
    308 ir_visitor_status
    309 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
    310 {
    311    ir_expression *orig_expr = orig_assign->rhs->as_expression();
    312    unsigned int i, matrix_columns = 1;
    313    ir_dereference *op[2];
    314 
    315    if (!orig_expr)
    316       return visit_continue;
    317 
    318    if (!has_matrix_operand(orig_expr, matrix_columns))
    319       return visit_continue;
    320 
    321    assert(orig_expr->get_num_operands() <= 2);
    322 
    323    mem_ctx = ralloc_parent(orig_assign);
    324 
    325    ir_dereference_variable *result =
    326       orig_assign->lhs->as_dereference_variable();
    327    assert(result);
    328 
    329    /* Store the expression operands in temps so we can use them
    330     * multiple times.
    331     */
    332    for (i = 0; i < orig_expr->get_num_operands(); i++) {
    333       ir_assignment *assign;
    334       ir_dereference *deref = orig_expr->operands[i]->as_dereference();
    335 
    336       /* Avoid making a temporary if we don't need to to avoid aliasing. */
    337       if (deref &&
    338 	  deref->variable_referenced() != result->variable_referenced()) {
    339 	 op[i] = deref;
    340 	 continue;
    341       }
    342 
    343       /* Otherwise, store the operand in a temporary generally if it's
    344        * not a dereference.
    345        */
    346       ir_variable *var = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
    347 						  "mat_op_to_vec",
    348 						  ir_var_temporary);
    349       base_ir->insert_before(var);
    350 
    351       /* Note that we use this dereference for the assignment.  That means
    352        * that others that want to use op[i] have to clone the deref.
    353        */
    354       op[i] = new(mem_ctx) ir_dereference_variable(var);
    355       assign = new(mem_ctx) ir_assignment(op[i], orig_expr->operands[i]);
    356       base_ir->insert_before(assign);
    357    }
    358 
    359    /* OK, time to break down this matrix operation. */
    360    switch (orig_expr->operation) {
    361    case ir_unop_d2f:
    362    case ir_unop_f2d:
    363    case ir_unop_neg: {
    364       /* Apply the operation to each column.*/
    365       for (i = 0; i < matrix_columns; i++) {
    366 	 ir_expression *column_expr;
    367 	 ir_assignment *column_assign;
    368 
    369 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    370 						  get_column(op[0], i));
    371 
    372 	 column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    373 						    column_expr);
    374 	 assert(column_assign->write_mask != 0);
    375 	 base_ir->insert_before(column_assign);
    376       }
    377       break;
    378    }
    379    case ir_binop_add:
    380    case ir_binop_sub:
    381    case ir_binop_div:
    382    case ir_binop_mod: {
    383       /* For most operations, the matrix version is just going
    384        * column-wise through and applying the operation to each column
    385        * if available.
    386        */
    387       for (i = 0; i < matrix_columns; i++) {
    388 	 ir_expression *column_expr;
    389 	 ir_assignment *column_assign;
    390 
    391 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    392 						  get_column(op[0], i),
    393 						  get_column(op[1], i));
    394 
    395 	 column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    396 						    column_expr);
    397 	 assert(column_assign->write_mask != 0);
    398 	 base_ir->insert_before(column_assign);
    399       }
    400       break;
    401    }
    402    case ir_binop_mul:
    403       if (op[0]->type->is_matrix()) {
    404 	 if (op[1]->type->is_matrix()) {
    405 	    do_mul_mat_mat(result, op[0], op[1]);
    406 	 } else if (op[1]->type->is_vector()) {
    407 	    do_mul_mat_vec(result, op[0], op[1]);
    408 	 } else {
    409 	    assert(op[1]->type->is_scalar());
    410 	    do_mul_mat_scalar(result, op[0], op[1]);
    411 	 }
    412       } else {
    413 	 assert(op[1]->type->is_matrix());
    414 	 if (op[0]->type->is_vector()) {
    415 	    do_mul_vec_mat(result, op[0], op[1]);
    416 	 } else {
    417 	    assert(op[0]->type->is_scalar());
    418 	    do_mul_mat_scalar(result, op[1], op[0]);
    419 	 }
    420       }
    421       break;
    422 
    423    case ir_binop_all_equal:
    424    case ir_binop_any_nequal:
    425       do_equal_mat_mat(result, op[1], op[0],
    426 		       (orig_expr->operation == ir_binop_all_equal));
    427       break;
    428 
    429    default:
    430       printf("FINISHME: Handle matrix operation for %s\n",
    431 	     ir_expression_operation_strings[orig_expr->operation]);
    432       abort();
    433    }
    434    orig_assign->remove();
    435    this->made_progress = true;
    436 
    437    return visit_continue;
    438 }
    439