2 * Copyright © 2010 Intel Corporation
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:
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
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.
25 * \file lower_mat_op_to_vec.cpp
27 * Breaks matrix operation expressions down to a series of vector operations.
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
35 #include "ir_expression_flattening.h"
36 #include "compiler/glsl_types.h"
40 class ir_mat_op_to_vec_visitor
: public ir_hierarchical_visitor
{
42 ir_mat_op_to_vec_visitor()
44 this->made_progress
= false;
48 ir_visitor_status
visit_leave(ir_assignment
*);
50 ir_dereference
*get_column(ir_dereference
*val
, int col
);
51 ir_rvalue
*get_element(ir_dereference
*val
, int col
, int row
);
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
);
68 } /* anonymous namespace */
71 mat_op_to_vec_predicate(ir_instruction
*ir
)
73 ir_expression
*expr
= ir
->as_expression();
79 for (i
= 0; i
< expr
->num_operands
; i
++) {
80 if (expr
->operands
[i
]->type
->is_matrix())
88 do_mat_op_to_vec(exec_list
*instructions
)
90 ir_mat_op_to_vec_visitor v
;
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.
96 do_expression_flattening(instructions
, mat_op_to_vec_predicate
);
98 visit_list_elements(&v
, instructions
);
100 return v
.made_progress
;
104 ir_mat_op_to_vec_visitor::get_element(ir_dereference
*val
, int col
, int row
)
106 val
= get_column(val
, col
);
108 return new(mem_ctx
) ir_swizzle(val
, row
, 0, 0, 0, 1);
112 ir_mat_op_to_vec_visitor::get_column(ir_dereference
*val
, int row
)
114 val
= val
->clone(mem_ctx
, NULL
);
116 if (val
->type
->is_matrix()) {
117 val
= new(mem_ctx
) ir_dereference_array(val
,
118 new(mem_ctx
) ir_constant(row
));
125 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference
*result
,
130 ir_assignment
*assign
;
133 for (b_col
= 0; b_col
< b
->type
->matrix_columns
; b_col
++) {
135 expr
= new(mem_ctx
) ir_expression(ir_binop_mul
,
137 get_element(b
, b_col
, 0));
139 /* following columns */
140 for (i
= 1; i
< a
->type
->matrix_columns
; i
++) {
141 ir_expression
*mul_expr
;
143 mul_expr
= new(mem_ctx
) ir_expression(ir_binop_mul
,
145 get_element(b
, b_col
, i
));
146 expr
= new(mem_ctx
) ir_expression(ir_binop_add
,
151 assign
= new(mem_ctx
) ir_assignment(get_column(result
, b_col
), expr
);
152 base_ir
->insert_before(assign
);
157 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference
*result
,
162 ir_assignment
*assign
;
166 expr
= new(mem_ctx
) ir_expression(ir_binop_mul
,
168 get_element(b
, 0, 0));
170 /* following columns */
171 for (i
= 1; i
< a
->type
->matrix_columns
; i
++) {
172 ir_expression
*mul_expr
;
174 mul_expr
= new(mem_ctx
) ir_expression(ir_binop_mul
,
176 get_element(b
, 0, i
));
177 expr
= new(mem_ctx
) ir_expression(ir_binop_add
, expr
, mul_expr
);
180 result
= result
->clone(mem_ctx
, NULL
);
181 assign
= new(mem_ctx
) ir_assignment(result
, expr
);
182 base_ir
->insert_before(assign
);
186 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference
*result
,
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
;
197 column_result
= result
->clone(mem_ctx
, NULL
);
198 column_result
= new(mem_ctx
) ir_swizzle(column_result
, i
, 0, 0, 0, 1);
200 column_expr
= new(mem_ctx
) ir_expression(ir_binop_dot
,
201 a
->clone(mem_ctx
, NULL
),
204 column_assign
= new(mem_ctx
) ir_assignment(column_result
,
206 base_ir
->insert_before(column_assign
);
211 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference
*result
,
217 for (i
= 0; i
< a
->type
->matrix_columns
; i
++) {
218 ir_expression
*column_expr
;
219 ir_assignment
*column_assign
;
221 column_expr
= new(mem_ctx
) ir_expression(ir_binop_mul
,
223 b
->clone(mem_ctx
, NULL
));
225 column_assign
= new(mem_ctx
) ir_assignment(get_column(result
, i
),
227 base_ir
->insert_before(column_assign
);
232 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference
*result
,
237 /* This essentially implements the following GLSL:
239 * bool equal(mat4 a, mat4 b)
241 * return !any(bvec4(a[0] != b[0],
247 * bool nequal(mat4 a, mat4 b)
249 * return any(bvec4(a[0] != b[0],
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);
259 ir_variable
*const tmp_bvec
=
260 new(this->mem_ctx
) ir_variable(bvec_type
, "mat_cmp_bvec",
262 this->base_ir
->insert_before(tmp_bvec
);
264 for (unsigned i
= 0; i
< columns
; i
++) {
265 ir_expression
*const cmp
=
266 new(this->mem_ctx
) ir_expression(ir_binop_any_nequal
,
270 ir_dereference
*const lhs
=
271 new(this->mem_ctx
) ir_dereference_variable(tmp_bvec
);
273 ir_assignment
*const assign
=
274 new(this->mem_ctx
) ir_assignment(lhs
, cmp
, NULL
, (1U << i
));
276 this->base_ir
->insert_before(assign
);
279 ir_rvalue
*const val
= new(this->mem_ctx
) ir_dereference_variable(tmp_bvec
);
280 uint8_t vec_elems
= val
->type
->vector_elements
;
282 new(this->mem_ctx
) ir_expression(ir_binop_any_nequal
, val
,
283 new(this->mem_ctx
) ir_constant(false,
287 any
= new(this->mem_ctx
) ir_expression(ir_unop_logic_not
, any
);
289 ir_assignment
*const assign
=
290 new(mem_ctx
) ir_assignment(result
->clone(mem_ctx
, NULL
), any
);
291 base_ir
->insert_before(assign
);
295 has_matrix_operand(const ir_expression
*expr
, unsigned &columns
)
297 for (unsigned i
= 0; i
< expr
->num_operands
; i
++) {
298 if (expr
->operands
[i
]->type
->is_matrix()) {
299 columns
= expr
->operands
[i
]->type
->matrix_columns
;
309 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment
*orig_assign
)
311 ir_expression
*orig_expr
= orig_assign
->rhs
->as_expression();
312 unsigned int i
, matrix_columns
= 1;
313 ir_dereference
*op
[2];
316 return visit_continue
;
318 if (!has_matrix_operand(orig_expr
, matrix_columns
))
319 return visit_continue
;
321 assert(orig_expr
->num_operands
<= 2);
323 mem_ctx
= ralloc_parent(orig_assign
);
325 ir_dereference_variable
*result
=
326 orig_assign
->lhs
->as_dereference_variable();
329 /* Store the expression operands in temps so we can use them
332 for (i
= 0; i
< orig_expr
->num_operands
; i
++) {
333 ir_assignment
*assign
;
334 ir_dereference
*deref
= orig_expr
->operands
[i
]->as_dereference();
336 /* Avoid making a temporary if we don't need to to avoid aliasing. */
338 deref
->variable_referenced() != result
->variable_referenced()) {
343 /* Otherwise, store the operand in a temporary generally if it's
346 ir_variable
*var
= new(mem_ctx
) ir_variable(orig_expr
->operands
[i
]->type
,
349 base_ir
->insert_before(var
);
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.
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
);
359 /* OK, time to break down this matrix operation. */
360 switch (orig_expr
->operation
) {
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
;
369 column_expr
= new(mem_ctx
) ir_expression(orig_expr
->operation
,
370 get_column(op
[0], i
));
372 column_assign
= new(mem_ctx
) ir_assignment(get_column(result
, i
),
374 assert(column_assign
->write_mask
!= 0);
375 base_ir
->insert_before(column_assign
);
383 /* For most operations, the matrix version is just going
384 * column-wise through and applying the operation to each column
387 for (i
= 0; i
< matrix_columns
; i
++) {
388 ir_expression
*column_expr
;
389 ir_assignment
*column_assign
;
391 column_expr
= new(mem_ctx
) ir_expression(orig_expr
->operation
,
392 get_column(op
[0], i
),
393 get_column(op
[1], i
));
395 column_assign
= new(mem_ctx
) ir_assignment(get_column(result
, i
),
397 assert(column_assign
->write_mask
!= 0);
398 base_ir
->insert_before(column_assign
);
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]);
409 assert(op
[1]->type
->is_scalar());
410 do_mul_mat_scalar(result
, op
[0], op
[1]);
413 assert(op
[1]->type
->is_matrix());
414 if (op
[0]->type
->is_vector()) {
415 do_mul_vec_mat(result
, op
[0], op
[1]);
417 assert(op
[0]->type
->is_scalar());
418 do_mul_mat_scalar(result
, op
[1], op
[0]);
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
));
430 printf("FINISHME: Handle matrix operation for %s\n",
431 ir_expression_operation_strings
[orig_expr
->operation
]);
434 orig_assign
->remove();
435 this->made_progress
= true;
437 return visit_continue
;