Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 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: |
||
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 |
||
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 |
||
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 * |
||
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 * |
||
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 |
||
121 | ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result, |
||
122 | ir_dereference *a, |
||
123 | ir_dereference *b) |
||
124 | { |
||
125 | unsigned 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 |
||
153 | ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result, |
||
154 | ir_dereference *a, |
||
155 | ir_dereference *b) |
||
156 | { |
||
157 | unsigned 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 |
||
182 | ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result, |
||
183 | ir_dereference *a, |
||
184 | ir_dereference *b) |
||
185 | { |
||
186 | unsigned 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 |
||
207 | ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result, |
||
208 | ir_dereference *a, |
||
209 | ir_dereference *b) |
||
210 | { |
||
211 | unsigned 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 |
||
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 |
||
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 |
||
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 | }>>>=>>><>>>>>>>> |