ir_algebraic: Support other comparisons in ir_unop_logic_not
[mesa.git] / src / glsl / ir_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 ir_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_optimization.h"
34 #include "glsl_types.h"
35
36 /**
37 * Visitor class for replacing expressions with ir_constant values.
38 */
39
40 class ir_algebraic_visitor : public ir_hierarchical_visitor {
41 public:
42 ir_algebraic_visitor()
43 {
44 this->progress = false;
45 }
46
47 virtual ~ir_algebraic_visitor()
48 {
49 }
50
51 virtual ir_visitor_status visit_leave(ir_assignment *);
52 virtual ir_visitor_status visit_leave(ir_call *);
53 virtual ir_visitor_status visit_leave(ir_dereference_array *);
54 virtual ir_visitor_status visit_leave(ir_expression *);
55 virtual ir_visitor_status visit_leave(ir_if *);
56 virtual ir_visitor_status visit_leave(ir_return *);
57 virtual ir_visitor_status visit_leave(ir_swizzle *);
58 virtual ir_visitor_status visit_leave(ir_texture *);
59
60 ir_rvalue *handle_expression(ir_rvalue *in_ir);
61
62 bool progress;
63 };
64
65 static bool
66 is_vec_zero(ir_constant *ir)
67 {
68 int c;
69
70 if (!ir)
71 return false;
72 if (!ir->type->is_scalar() &&
73 !ir->type->is_vector())
74 return false;
75
76 for (c = 0; c < ir->type->vector_elements; c++) {
77 switch (ir->type->base_type) {
78 case GLSL_TYPE_FLOAT:
79 if (ir->value.f[c] != 0.0)
80 return false;
81 break;
82 case GLSL_TYPE_INT:
83 if (ir->value.i[c] != 0)
84 return false;
85 break;
86 case GLSL_TYPE_UINT:
87 if (ir->value.u[c] != 0)
88 return false;
89 break;
90 case GLSL_TYPE_BOOL:
91 if (ir->value.b[c] != false)
92 return false;
93 break;
94 default:
95 assert(!"bad base type");
96 return false;
97 }
98 }
99
100 return true;
101 }
102
103 static bool
104 is_vec_one(ir_constant *ir)
105 {
106 int c;
107
108 if (!ir)
109 return false;
110 if (!ir->type->is_scalar() &&
111 !ir->type->is_vector())
112 return false;
113
114 for (c = 0; c < ir->type->vector_elements; c++) {
115 switch (ir->type->base_type) {
116 case GLSL_TYPE_FLOAT:
117 if (ir->value.f[c] != 1.0)
118 return false;
119 break;
120 case GLSL_TYPE_INT:
121 if (ir->value.i[c] != 1)
122 return false;
123 break;
124 case GLSL_TYPE_UINT:
125 if (ir->value.u[c] != 1)
126 return false;
127 break;
128 case GLSL_TYPE_BOOL:
129 if (ir->value.b[c] != true)
130 return false;
131 break;
132 default:
133 assert(!"bad base type");
134 return false;
135 }
136 }
137
138 return true;
139 }
140
141 ir_rvalue *
142 ir_algebraic_visitor::handle_expression(ir_rvalue *in_ir)
143 {
144 ir_expression *ir = (ir_expression *)in_ir;
145 ir_constant *op_const[2] = {NULL, NULL};
146 ir_expression *op_expr[2] = {NULL, NULL};
147 unsigned int i;
148
149 if (!in_ir)
150 return NULL;
151
152 if (in_ir->ir_type != ir_type_expression)
153 return in_ir;
154
155 for (i = 0; i < ir->get_num_operands(); i++) {
156 if (ir->operands[i]->type->is_matrix())
157 return in_ir;
158
159 op_const[i] = ir->operands[i]->constant_expression_value();
160 op_expr[i] = ir->operands[i]->as_expression();
161 }
162
163 switch (ir->operation) {
164 case ir_unop_logic_not: {
165 enum ir_expression_operation new_op = ir_unop_logic_not;
166
167 if (op_expr[0] == NULL)
168 break;
169
170 switch (op_expr[0]->operation) {
171 case ir_binop_less: new_op = ir_binop_gequal; break;
172 case ir_binop_greater: new_op = ir_binop_lequal; break;
173 case ir_binop_lequal: new_op = ir_binop_greater; break;
174 case ir_binop_gequal: new_op = ir_binop_less; break;
175 case ir_binop_equal: new_op = ir_binop_nequal; break;
176 case ir_binop_nequal: new_op = ir_binop_equal; break;
177
178 default:
179 /* The default case handler is here to silence a warning from GCC.
180 */
181 break;
182 }
183
184 if (new_op != ir_unop_logic_not) {
185 this->progress = true;
186 return new(ir) ir_expression(new_op,
187 ir->type,
188 op_expr[0]->operands[0],
189 op_expr[0]->operands[1]);
190 }
191
192 break;
193 }
194
195 case ir_binop_add:
196 if (is_vec_zero(op_const[0])) {
197 this->progress = true;
198 return ir->operands[1];
199 }
200 if (is_vec_zero(op_const[1])) {
201 this->progress = true;
202 return ir->operands[0];
203 }
204 break;
205
206 case ir_binop_sub:
207 if (is_vec_zero(op_const[0])) {
208 this->progress = true;
209 return new(ir) ir_expression(ir_unop_neg,
210 ir->type,
211 ir->operands[1],
212 NULL);
213 }
214 if (is_vec_zero(op_const[1])) {
215 this->progress = true;
216 return ir->operands[0];
217 }
218 break;
219
220 case ir_binop_mul:
221 if (is_vec_one(op_const[0])) {
222 this->progress = true;
223 return ir->operands[1];
224 }
225 if (is_vec_one(op_const[1])) {
226 this->progress = true;
227 return ir->operands[0];
228 }
229
230 if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
231 this->progress = true;
232 return ir_constant::zero(ir, ir->type);
233 }
234 break;
235
236 case ir_binop_div:
237 if (is_vec_one(op_const[0]) && ir->type->base_type == GLSL_TYPE_FLOAT) {
238 this->progress = true;
239 return new(ir) ir_expression(ir_unop_rcp,
240 ir->type,
241 ir->operands[1],
242 NULL);
243 }
244 if (is_vec_one(op_const[1])) {
245 this->progress = true;
246 return ir->operands[0];
247 }
248 break;
249
250 case ir_unop_rcp:
251 if (op_expr[0] && op_expr[0]->operation == ir_unop_rcp) {
252 this->progress = true;
253 return op_expr[0]->operands[0];
254 }
255
256 /* FINISHME: We should do rcp(rsq(x)) -> sqrt(x) for some
257 * backends, except that some backends will have done sqrt ->
258 * rcp(rsq(x)) and we don't want to undo it for them.
259 */
260
261 /* As far as we know, all backends are OK with rsq. */
262 if (op_expr[0] && op_expr[0]->operation == ir_unop_sqrt) {
263 this->progress = true;
264 return new(ir) ir_expression(ir_unop_rsq,
265 ir->type,
266 op_expr[0]->operands[0],
267 NULL);
268 }
269
270 break;
271
272 default:
273 break;
274 }
275
276 return in_ir;
277 }
278
279 ir_visitor_status
280 ir_algebraic_visitor::visit_leave(ir_expression *ir)
281 {
282 unsigned int operand;
283
284 for (operand = 0; operand < ir->get_num_operands(); operand++) {
285 ir->operands[operand] = handle_expression(ir->operands[operand]);
286 }
287
288 return visit_continue;
289 }
290
291 ir_visitor_status
292 ir_algebraic_visitor::visit_leave(ir_texture *ir)
293 {
294 ir->coordinate = handle_expression(ir->coordinate);
295 ir->projector = handle_expression(ir->projector);
296 ir->shadow_comparitor = handle_expression(ir->shadow_comparitor);
297
298 switch (ir->op) {
299 case ir_tex:
300 break;
301 case ir_txb:
302 ir->lod_info.bias = handle_expression(ir->lod_info.bias);
303 break;
304 case ir_txf:
305 case ir_txl:
306 ir->lod_info.lod = handle_expression(ir->lod_info.lod);
307 break;
308 case ir_txd:
309 ir->lod_info.grad.dPdx = handle_expression(ir->lod_info.grad.dPdx);
310 ir->lod_info.grad.dPdy = handle_expression(ir->lod_info.grad.dPdy);
311 break;
312 }
313
314 return visit_continue;
315 }
316
317 ir_visitor_status
318 ir_algebraic_visitor::visit_leave(ir_swizzle *ir)
319 {
320 ir->val = handle_expression(ir->val);
321 return visit_continue;
322 }
323
324 ir_visitor_status
325 ir_algebraic_visitor::visit_leave(ir_dereference_array *ir)
326 {
327 ir->array_index = handle_expression(ir->array_index);
328 return visit_continue;
329 }
330
331 ir_visitor_status
332 ir_algebraic_visitor::visit_leave(ir_assignment *ir)
333 {
334 ir->rhs = handle_expression(ir->rhs);
335 ir->condition = handle_expression(ir->condition);
336 return visit_continue;
337 }
338
339 ir_visitor_status
340 ir_algebraic_visitor::visit_leave(ir_call *ir)
341 {
342 foreach_iter(exec_list_iterator, iter, *ir) {
343 ir_rvalue *param = (ir_rvalue *)iter.get();
344 ir_rvalue *new_param = handle_expression(param);
345
346 if (new_param != param) {
347 param->replace_with(new_param);
348 }
349 }
350 return visit_continue;
351 }
352
353 ir_visitor_status
354 ir_algebraic_visitor::visit_leave(ir_return *ir)
355 {
356 ir->value = handle_expression(ir->value);;
357 return visit_continue;
358 }
359
360 ir_visitor_status
361 ir_algebraic_visitor::visit_leave(ir_if *ir)
362 {
363 ir->condition = handle_expression(ir->condition);
364 return visit_continue;
365 }
366
367
368 bool
369 do_algebraic(exec_list *instructions)
370 {
371 ir_algebraic_visitor v;
372
373 visit_list_elements(&v, instructions);
374
375 return v.progress;
376 }