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