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