glsl2: Generate masked assignments in some expanded matrix operations
[mesa.git] / src / glsl / ir_mat_op_to_vec.cpp
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 ir_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:
40 ir_mat_op_to_vec_visitor()
41 {
42 this->made_progress = false;
43 }
44
45 ir_visitor_status visit_leave(ir_assignment *);
46
47 ir_dereference *get_column(ir_variable *var, int col);
48 ir_rvalue *get_element(ir_variable *var, int col, int row);
49
50 void do_mul_mat_mat(ir_variable *result_var,
51 ir_variable *a_var, ir_variable *b_var);
52 void do_mul_mat_vec(ir_variable *result_var,
53 ir_variable *a_var, ir_variable *b_var);
54 void do_mul_vec_mat(ir_variable *result_var,
55 ir_variable *a_var, ir_variable *b_var);
56 void do_mul_mat_scalar(ir_variable *result_var,
57 ir_variable *a_var, ir_variable *b_var);
58
59 bool made_progress;
60 };
61
62 static bool
63 mat_op_to_vec_predicate(ir_instruction *ir)
64 {
65 ir_expression *expr = ir->as_expression();
66 unsigned int i;
67
68 if (!expr)
69 return false;
70
71 for (i = 0; i < expr->get_num_operands(); i++) {
72 if (expr->operands[i]->type->is_matrix())
73 return true;
74 }
75
76 return false;
77 }
78
79 bool
80 do_mat_op_to_vec(exec_list *instructions)
81 {
82 ir_mat_op_to_vec_visitor v;
83
84 /* Pull out any matrix expression to a separate assignment to a
85 * temp. This will make our handling of the breakdown to
86 * operations on the matrix's vector components much easier.
87 */
88 do_expression_flattening(instructions, mat_op_to_vec_predicate);
89
90 visit_list_elements(&v, instructions);
91
92 return v.made_progress;
93 }
94
95 ir_rvalue *
96 ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
97 {
98 ir_dereference *deref;
99
100 deref = new(base_ir) ir_dereference_variable(var);
101
102 if (var->type->is_matrix()) {
103 deref = new(base_ir) ir_dereference_array(var,
104 new(base_ir) ir_constant(col));
105 } else {
106 assert(col == 0);
107 }
108
109 return new(base_ir) ir_swizzle(deref, row, 0, 0, 0, 1);
110 }
111
112 ir_dereference *
113 ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
114 {
115 ir_dereference *deref;
116
117 if (!var->type->is_matrix()) {
118 deref = new(base_ir) ir_dereference_variable(var);
119 } else {
120 deref = new(base_ir) ir_dereference_variable(var);
121 deref = new(base_ir) ir_dereference_array(deref,
122 new(base_ir) ir_constant(row));
123 }
124
125 return deref;
126 }
127
128 void
129 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
130 ir_variable *a_var,
131 ir_variable *b_var)
132 {
133 int b_col, i;
134 ir_assignment *assign;
135 ir_expression *expr;
136
137 for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
138 ir_rvalue *a = get_column(a_var, 0);
139 ir_rvalue *b = get_element(b_var, b_col, 0);
140
141 /* first column */
142 expr = new(base_ir) ir_expression(ir_binop_mul,
143 a->type,
144 a,
145 b);
146
147 /* following columns */
148 for (i = 1; i < a_var->type->matrix_columns; i++) {
149 ir_expression *mul_expr;
150
151 a = get_column(a_var, i);
152 b = get_element(b_var, b_col, i);
153
154 mul_expr = new(base_ir) ir_expression(ir_binop_mul,
155 a->type,
156 a,
157 b);
158 expr = new(base_ir) ir_expression(ir_binop_add,
159 a->type,
160 expr,
161 mul_expr);
162 }
163
164 ir_rvalue *result = get_column(result_var, b_col);
165 assign = new(base_ir) ir_assignment(result,
166 expr,
167 NULL);
168 base_ir->insert_before(assign);
169 }
170 }
171
172 void
173 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
174 ir_variable *a_var,
175 ir_variable *b_var)
176 {
177 int i;
178 ir_rvalue *a = get_column(a_var, 0);
179 ir_rvalue *b = get_element(b_var, 0, 0);
180 ir_assignment *assign;
181 ir_expression *expr;
182
183 /* first column */
184 expr = new(base_ir) ir_expression(ir_binop_mul,
185 result_var->type,
186 a,
187 b);
188
189 /* following columns */
190 for (i = 1; i < a_var->type->matrix_columns; i++) {
191 ir_expression *mul_expr;
192
193 a = get_column(a_var, i);
194 b = get_element(b_var, 0, i);
195
196 mul_expr = new(base_ir) ir_expression(ir_binop_mul,
197 result_var->type,
198 a,
199 b);
200 expr = new(base_ir) ir_expression(ir_binop_add,
201 result_var->type,
202 expr,
203 mul_expr);
204 }
205
206 ir_rvalue *result = new(base_ir) ir_dereference_variable(result_var);
207 assign = new(base_ir) ir_assignment(result,
208 expr,
209 NULL);
210 base_ir->insert_before(assign);
211 }
212
213 void
214 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
215 ir_variable *a_var,
216 ir_variable *b_var)
217 {
218 int i;
219
220 for (i = 0; i < b_var->type->matrix_columns; i++) {
221 ir_rvalue *a = new(base_ir) ir_dereference_variable(a_var);
222 ir_rvalue *b = get_column(b_var, i);
223 ir_rvalue *result;
224 ir_expression *column_expr;
225 ir_assignment *column_assign;
226
227 result = new(base_ir) ir_dereference_variable(result_var);
228 result = new(base_ir) ir_swizzle(result, i, 0, 0, 0, 1);
229
230 column_expr = new(base_ir) ir_expression(ir_binop_dot,
231 result->type,
232 a,
233 b);
234
235 column_assign = new(base_ir) ir_assignment(result,
236 column_expr,
237 NULL);
238 base_ir->insert_before(column_assign);
239 }
240 }
241
242 void
243 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
244 ir_variable *a_var,
245 ir_variable *b_var)
246 {
247 int i;
248
249 for (i = 0; i < a_var->type->matrix_columns; i++) {
250 ir_rvalue *a = get_column(a_var, i);
251 ir_rvalue *b = new(base_ir) ir_dereference_variable(b_var);
252 ir_rvalue *result = get_column(result_var, i);
253 ir_expression *column_expr;
254 ir_assignment *column_assign;
255
256 column_expr = new(base_ir) ir_expression(ir_binop_mul,
257 result->type,
258 a,
259 b);
260
261 column_assign = new(base_ir) ir_assignment(result,
262 column_expr,
263 NULL);
264 base_ir->insert_before(column_assign);
265 }
266 }
267
268 ir_visitor_status
269 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *assign)
270 {
271 ir_expression *expr = assign->rhs->as_expression();
272 bool found_matrix = false;
273 unsigned int i, matrix_columns = 1;
274 ir_variable *op_var[2];
275
276 if (!expr)
277 return visit_continue;
278
279 for (i = 0; i < expr->get_num_operands(); i++) {
280 if (expr->operands[i]->type->is_matrix()) {
281 found_matrix = true;
282 matrix_columns = expr->operands[i]->type->matrix_columns;
283 break;
284 }
285 }
286 if (!found_matrix)
287 return visit_continue;
288
289 ir_dereference_variable *lhs_deref = assign->lhs->as_dereference_variable();
290 assert(lhs_deref);
291
292 ir_variable *result_var = lhs_deref->var;
293
294 /* Store the expression operands in temps so we can use them
295 * multiple times.
296 */
297 for (i = 0; i < expr->get_num_operands(); i++) {
298 ir_assignment *assign;
299
300 op_var[i] = new(base_ir) ir_variable(expr->operands[i]->type,
301 "mat_op_to_vec",
302 ir_var_temporary);
303 base_ir->insert_before(op_var[i]);
304
305 lhs_deref = new(base_ir) ir_dereference_variable(op_var[i]);
306 assign = new(base_ir) ir_assignment(lhs_deref,
307 expr->operands[i],
308 NULL);
309 base_ir->insert_before(assign);
310 }
311
312 /* OK, time to break down this matrix operation. */
313 switch (expr->operation) {
314 case ir_binop_add:
315 case ir_binop_sub:
316 case ir_binop_div:
317 case ir_binop_mod: {
318 const unsigned mask = (1U << result_var->type->vector_elements) - 1;
319
320 /* For most operations, the matrix version is just going
321 * column-wise through and applying the operation to each column
322 * if available.
323 */
324 for (i = 0; i < matrix_columns; i++) {
325 ir_rvalue *op0 = get_column(op_var[0], i);
326 ir_rvalue *op1 = get_column(op_var[1], i);
327 ir_dereference *result = get_column(result_var, i);
328 ir_expression *column_expr;
329 ir_assignment *column_assign;
330
331 column_expr = new(base_ir) ir_expression(expr->operation,
332 result->type,
333 op0,
334 op1);
335
336 column_assign = new(base_ir) ir_assignment(result,
337 column_expr,
338 NULL,
339 mask);
340 assert(column_assign->write_mask != 0);
341 base_ir->insert_before(column_assign);
342 }
343 break;
344 }
345 case ir_binop_mul:
346 if (op_var[0]->type->is_matrix()) {
347 if (op_var[1]->type->is_matrix()) {
348 do_mul_mat_mat(result_var, op_var[0], op_var[1]);
349 } else if (op_var[1]->type->is_vector()) {
350 do_mul_mat_vec(result_var, op_var[0], op_var[1]);
351 } else {
352 assert(op_var[1]->type->is_scalar());
353 do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
354 }
355 } else {
356 assert(op_var[1]->type->is_matrix());
357 if (op_var[0]->type->is_vector()) {
358 do_mul_vec_mat(result_var, op_var[0], op_var[1]);
359 } else {
360 assert(op_var[0]->type->is_scalar());
361 do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
362 }
363 }
364 break;
365 default:
366 printf("FINISHME: Handle matrix operation for %s\n", expr->operator_string());
367 abort();
368 }
369 assign->remove();
370 this->made_progress = true;
371
372 return visit_continue;
373 }