Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / glsl / opt_algebraic.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 opt_algebraic.cpp
26  *
27  * Takes advantage of association, commutivity, and other algebraic
28  * properties to simplify expressions.
29  */
30
31 #include "ir.h"
32 #include "ir_visitor.h"
33 #include "ir_rvalue_visitor.h"
34 #include "ir_optimization.h"
35 #include "glsl_types.h"
36
37 /**
38  * Visitor class for replacing expressions with ir_constant values.
39  */
40
41 class ir_algebraic_visitor : public ir_rvalue_visitor {
42 public:
43    ir_algebraic_visitor()
44    {
45       this->progress = false;
46       this->mem_ctx = NULL;
47    }
48
49    virtual ~ir_algebraic_visitor()
50    {
51    }
52
53    ir_rvalue *handle_expression(ir_expression *ir);
54    void handle_rvalue(ir_rvalue **rvalue);
55    bool reassociate_constant(ir_expression *ir1,
56                              int const_index,
57                              ir_constant *constant,
58                              ir_expression *ir2);
59    void reassociate_operands(ir_expression *ir1,
60                              int op1,
61                              ir_expression *ir2,
62                              int op2);
63    ir_rvalue *swizzle_if_required(ir_expression *expr,
64                                   ir_rvalue *operand);
65
66    void *mem_ctx;
67
68    bool progress;
69 };
70
71 static inline bool
72 is_vec_zero(ir_constant *ir)
73 {
74    return (ir == NULL) ? false : ir->is_zero();
75 }
76
77 static inline bool
78 is_vec_one(ir_constant *ir)
79 {
80    return (ir == NULL) ? false : ir->is_one();
81 }
82
83 static void
84 update_type(ir_expression *ir)
85 {
86    if (ir->operands[0]->type->is_vector())
87       ir->type = ir->operands[0]->type;
88    else
89       ir->type = ir->operands[1]->type;
90 }
91
92 void
93 ir_algebraic_visitor::reassociate_operands(ir_expression *ir1,
94                                            int op1,
95                                            ir_expression *ir2,
96                                            int op2)
97 {
98    ir_rvalue *temp = ir2->operands[op2];
99    ir2->operands[op2] = ir1->operands[op1];
100    ir1->operands[op1] = temp;
101
102    /* Update the type of ir2.  The type of ir1 won't have changed --
103     * base types matched, and at least one of the operands of the 2
104     * binops is still a vector if any of them were.
105     */
106    update_type(ir2);
107
108    this->progress = true;
109 }
110
111 /**
112  * Reassociates a constant down a tree of adds or multiplies.
113  *
114  * Consider (2 * (a * (b * 0.5))).  We want to send up with a * b.
115  */
116 bool
117 ir_algebraic_visitor::reassociate_constant(ir_expression *ir1, int const_index,
118                                            ir_constant *constant,
119                                            ir_expression *ir2)
120 {
121    if (!ir2 || ir1->operation != ir2->operation)
122       return false;
123
124    /* Don't want to even think about matrices. */
125    if (ir1->operands[0]->type->is_matrix() ||
126        ir1->operands[1]->type->is_matrix() ||
127        ir2->operands[0]->type->is_matrix() ||
128        ir2->operands[1]->type->is_matrix())
129       return false;
130
131    ir_constant *ir2_const[2];
132    ir2_const[0] = ir2->operands[0]->constant_expression_value();
133    ir2_const[1] = ir2->operands[1]->constant_expression_value();
134
135    if (ir2_const[0] && ir2_const[1])
136       return false;
137
138    if (ir2_const[0]) {
139       reassociate_operands(ir1, const_index, ir2, 1);
140       return true;
141    } else if (ir2_const[1]) {
142       reassociate_operands(ir1, const_index, ir2, 0);
143       return true;
144    }
145
146    if (reassociate_constant(ir1, const_index, constant,
147                             ir2->operands[0]->as_expression())) {
148       update_type(ir2);
149       return true;
150    }
151
152    if (reassociate_constant(ir1, const_index, constant,
153                             ir2->operands[1]->as_expression())) {
154       update_type(ir2);
155       return true;
156    }
157
158    return false;
159 }
160
161 /* When eliminating an expression and just returning one of its operands,
162  * we may need to swizzle that operand out to a vector if the expression was
163  * vector type.
164  */
165 ir_rvalue *
166 ir_algebraic_visitor::swizzle_if_required(ir_expression *expr,
167                                           ir_rvalue *operand)
168 {
169    if (expr->type->is_vector() && operand->type->is_scalar()) {
170       return new(mem_ctx) ir_swizzle(operand, 0, 0, 0, 0,
171                                      expr->type->vector_elements);
172    } else
173       return operand;
174 }
175
176 ir_rvalue *
177 ir_algebraic_visitor::handle_expression(ir_expression *ir)
178 {
179    ir_constant *op_const[2] = {NULL, NULL};
180    ir_expression *op_expr[2] = {NULL, NULL};
181    ir_expression *temp;
182    unsigned int i;
183
184    assert(ir->get_num_operands() <= 2);
185    for (i = 0; i < ir->get_num_operands(); i++) {
186       if (ir->operands[i]->type->is_matrix())
187          return ir;
188
189       op_const[i] = ir->operands[i]->constant_expression_value();
190       op_expr[i] = ir->operands[i]->as_expression();
191    }
192
193    if (this->mem_ctx == NULL)
194       this->mem_ctx = ralloc_parent(ir);
195
196    switch (ir->operation) {
197    case ir_unop_logic_not: {
198       enum ir_expression_operation new_op = ir_unop_logic_not;
199
200       if (op_expr[0] == NULL)
201          break;
202
203       switch (op_expr[0]->operation) {
204       case ir_binop_less:    new_op = ir_binop_gequal;  break;
205       case ir_binop_greater: new_op = ir_binop_lequal;  break;
206       case ir_binop_lequal:  new_op = ir_binop_greater; break;
207       case ir_binop_gequal:  new_op = ir_binop_less;    break;
208       case ir_binop_equal:   new_op = ir_binop_nequal;  break;
209       case ir_binop_nequal:  new_op = ir_binop_equal;   break;
210       case ir_binop_all_equal:   new_op = ir_binop_any_nequal;  break;
211       case ir_binop_any_nequal:  new_op = ir_binop_all_equal;   break;
212
213       default:
214          /* The default case handler is here to silence a warning from GCC.
215           */
216          break;
217       }
218
219       if (new_op != ir_unop_logic_not) {
220          this->progress = true;
221          return new(mem_ctx) ir_expression(new_op,
222                                            ir->type,
223                                            op_expr[0]->operands[0],
224                                            op_expr[0]->operands[1]);
225       }
226
227       break;
228    }
229
230    case ir_binop_add:
231       if (is_vec_zero(op_const[0])) {
232          this->progress = true;
233          return swizzle_if_required(ir, ir->operands[1]);
234       }
235       if (is_vec_zero(op_const[1])) {
236          this->progress = true;
237          return swizzle_if_required(ir, ir->operands[0]);
238       }
239
240       /* Reassociate addition of constants so that we can do constant
241        * folding.
242        */
243       if (op_const[0] && !op_const[1])
244          reassociate_constant(ir, 0, op_const[0],
245                               ir->operands[1]->as_expression());
246       if (op_const[1] && !op_const[0])
247          reassociate_constant(ir, 1, op_const[1],
248                               ir->operands[0]->as_expression());
249       break;
250
251    case ir_binop_sub:
252       if (is_vec_zero(op_const[0])) {
253          this->progress = true;
254          temp = new(mem_ctx) ir_expression(ir_unop_neg,
255                                            ir->operands[1]->type,
256                                            ir->operands[1],
257                                            NULL);
258          return swizzle_if_required(ir, temp);
259       }
260       if (is_vec_zero(op_const[1])) {
261          this->progress = true;
262          return swizzle_if_required(ir, ir->operands[0]);
263       }
264       break;
265
266    case ir_binop_mul:
267       if (is_vec_one(op_const[0])) {
268          this->progress = true;
269          return swizzle_if_required(ir, ir->operands[1]);
270       }
271       if (is_vec_one(op_const[1])) {
272          this->progress = true;
273          return swizzle_if_required(ir, ir->operands[0]);
274       }
275
276       if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
277          this->progress = true;
278          return ir_constant::zero(ir, ir->type);
279       }
280
281       /* Reassociate multiplication of constants so that we can do
282        * constant folding.
283        */
284       if (op_const[0] && !op_const[1])
285          reassociate_constant(ir, 0, op_const[0],
286                               ir->operands[1]->as_expression());
287       if (op_const[1] && !op_const[0])
288          reassociate_constant(ir, 1, op_const[1],
289                               ir->operands[0]->as_expression());
290
291       break;
292
293    case ir_binop_div:
294       if (is_vec_one(op_const[0]) && ir->type->base_type == GLSL_TYPE_FLOAT) {
295          this->progress = true;
296          temp = new(mem_ctx) ir_expression(ir_unop_rcp,
297                                            ir->operands[1]->type,
298                                            ir->operands[1],
299                                            NULL);
300          return swizzle_if_required(ir, temp);
301       }
302       if (is_vec_one(op_const[1])) {
303          this->progress = true;
304          return swizzle_if_required(ir, ir->operands[0]);
305       }
306       break;
307
308    case ir_binop_logic_and:
309       /* FINISHME: Also simplify (a && a) to (a). */
310       if (is_vec_one(op_const[0])) {
311          this->progress = true;
312          return ir->operands[1];
313       } else if (is_vec_one(op_const[1])) {
314          this->progress = true;
315          return ir->operands[0];
316       } else if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
317          this->progress = true;
318          return ir_constant::zero(mem_ctx, ir->type);
319       }
320       break;
321
322    case ir_binop_logic_xor:
323       /* FINISHME: Also simplify (a ^^ a) to (false). */
324       if (is_vec_zero(op_const[0])) {
325          this->progress = true;
326          return ir->operands[1];
327       } else if (is_vec_zero(op_const[1])) {
328          this->progress = true;
329          return ir->operands[0];
330       } else if (is_vec_one(op_const[0])) {
331          this->progress = true;
332          return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
333                                            ir->operands[1], NULL);
334       } else if (is_vec_one(op_const[1])) {
335          this->progress = true;
336          return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
337                                            ir->operands[0], NULL);
338       }
339       break;
340
341    case ir_binop_logic_or:
342       /* FINISHME: Also simplify (a || a) to (a). */
343       if (is_vec_zero(op_const[0])) {
344          this->progress = true;
345          return ir->operands[1];
346       } else if (is_vec_zero(op_const[1])) {
347          this->progress = true;
348          return ir->operands[0];
349       } else if (is_vec_one(op_const[0]) || is_vec_one(op_const[1])) {
350          ir_constant_data data;
351
352          for (unsigned i = 0; i < 16; i++)
353             data.b[i] = true;
354
355          this->progress = true;
356          return new(mem_ctx) ir_constant(ir->type, &data);
357       }
358       break;
359
360    case ir_unop_rcp:
361       if (op_expr[0] && op_expr[0]->operation == ir_unop_rcp) {
362          this->progress = true;
363          return op_expr[0]->operands[0];
364       }
365
366       /* FINISHME: We should do rcp(rsq(x)) -> sqrt(x) for some
367        * backends, except that some backends will have done sqrt ->
368        * rcp(rsq(x)) and we don't want to undo it for them.
369        */
370
371       /* As far as we know, all backends are OK with rsq. */
372       if (op_expr[0] && op_expr[0]->operation == ir_unop_sqrt) {
373          this->progress = true;
374          temp = new(mem_ctx) ir_expression(ir_unop_rsq,
375                                            op_expr[0]->operands[0]->type,
376                                            op_expr[0]->operands[0],
377                                            NULL);
378          return swizzle_if_required(ir, temp);
379       }
380
381       break;
382
383    default:
384       break;
385    }
386
387    return ir;
388 }
389
390 void
391 ir_algebraic_visitor::handle_rvalue(ir_rvalue **rvalue)
392 {
393    if (!*rvalue)
394       return;
395
396    ir_expression *expr = (*rvalue)->as_expression();
397    if (!expr || expr->operation == ir_quadop_vector)
398       return;
399
400    *rvalue = handle_expression(expr);
401 }
402
403 bool
404 do_algebraic(exec_list *instructions)
405 {
406    ir_algebraic_visitor v;
407
408    visit_list_elements(&v, instructions);
409
410    return v.progress;
411 }