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