| /* |
| * Copyright © 2010 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * \file opt_algebraic.cpp |
| * |
| * Takes advantage of association, commutivity, and other algebraic |
| * properties to simplify expressions. |
| */ |
| |
| #include "ir.h" |
| #include "ir_visitor.h" |
| #include "ir_rvalue_visitor.h" |
| #include "ir_optimization.h" |
| #include "ir_builder.h" |
| #include "compiler/glsl_types.h" |
| #include "main/consts_exts.h" |
| |
| using namespace ir_builder; |
| |
| namespace { |
| |
| /** |
| * Visitor class for replacing expressions with ir_constant values. |
| */ |
| |
| class ir_algebraic_visitor : public ir_rvalue_visitor { |
| public: |
| ir_algebraic_visitor(bool native_integers, |
| const struct gl_shader_compiler_options *options) |
| : options(options) |
| { |
| this->progress = false; |
| this->mem_ctx = NULL; |
| this->native_integers = native_integers; |
| } |
| |
| virtual ~ir_algebraic_visitor() |
| { |
| } |
| |
| virtual ir_visitor_status visit_enter(ir_assignment *ir); |
| |
| ir_rvalue *handle_expression(ir_expression *ir); |
| void handle_rvalue(ir_rvalue **rvalue); |
| bool reassociate_constant(ir_expression *ir1, |
| int const_index, |
| ir_constant *constant, |
| ir_expression *ir2); |
| void reassociate_operands(ir_expression *ir1, |
| int op1, |
| ir_expression *ir2, |
| int op2); |
| ir_rvalue *swizzle_if_required(ir_expression *expr, |
| ir_rvalue *operand); |
| |
| const struct gl_shader_compiler_options *options; |
| void *mem_ctx; |
| |
| bool native_integers; |
| bool progress; |
| }; |
| |
| } /* unnamed namespace */ |
| |
| ir_visitor_status |
| ir_algebraic_visitor::visit_enter(ir_assignment *ir) |
| { |
| ir_variable *var = ir->lhs->variable_referenced(); |
| if (var->data.invariant || var->data.precise) { |
| /* If we're assigning to an invariant or precise variable, just bail. |
| * Most of the algebraic optimizations aren't precision-safe. |
| * |
| * FINISHME: Find out which optimizations are precision-safe and enable |
| * then only for invariant or precise trees. |
| */ |
| return visit_continue_with_parent; |
| } else { |
| return visit_continue; |
| } |
| } |
| |
| static inline bool |
| is_valid_vec_const(ir_constant *ir) |
| { |
| if (ir == NULL) |
| return false; |
| |
| if (!glsl_type_is_scalar(ir->type) && !glsl_type_is_vector(ir->type)) |
| return false; |
| |
| return true; |
| } |
| |
| static inline bool |
| is_less_than_one(ir_constant *ir) |
| { |
| assert(glsl_type_is_float(ir->type)); |
| |
| if (!is_valid_vec_const(ir)) |
| return false; |
| |
| unsigned component = 0; |
| for (int c = 0; c < ir->type->vector_elements; c++) { |
| if (ir->get_float_component(c) < 1.0f) |
| component++; |
| } |
| |
| return (component == ir->type->vector_elements); |
| } |
| |
| static inline bool |
| is_greater_than_zero(ir_constant *ir) |
| { |
| assert(glsl_type_is_float(ir->type)); |
| |
| if (!is_valid_vec_const(ir)) |
| return false; |
| |
| unsigned component = 0; |
| for (int c = 0; c < ir->type->vector_elements; c++) { |
| if (ir->get_float_component(c) > 0.0f) |
| component++; |
| } |
| |
| return (component == ir->type->vector_elements); |
| } |
| |
| static void |
| update_type(ir_expression *ir) |
| { |
| if (glsl_type_is_vector(ir->operands[0]->type)) |
| ir->type = ir->operands[0]->type; |
| else |
| ir->type = ir->operands[1]->type; |
| } |
| |
| void |
| ir_algebraic_visitor::reassociate_operands(ir_expression *ir1, |
| int op1, |
| ir_expression *ir2, |
| int op2) |
| { |
| ir_rvalue *temp = ir2->operands[op2]; |
| ir2->operands[op2] = ir1->operands[op1]; |
| ir1->operands[op1] = temp; |
| |
| /* Update the type of ir2. The type of ir1 won't have changed -- |
| * base types matched, and at least one of the operands of the 2 |
| * binops is still a vector if any of them were. |
| */ |
| update_type(ir2); |
| |
| this->progress = true; |
| } |
| |
| /** |
| * Reassociates a constant down a tree of adds or multiplies. |
| * |
| * Consider (2 * (a * (b * 0.5))). We want to end up with a * b. |
| */ |
| bool |
| ir_algebraic_visitor::reassociate_constant(ir_expression *ir1, int const_index, |
| ir_constant *constant, |
| ir_expression *ir2) |
| { |
| if (!ir2 || ir1->operation != ir2->operation) |
| return false; |
| |
| /* Don't want to even think about matrices. */ |
| if (glsl_type_is_matrix(ir1->operands[0]->type) || |
| glsl_type_is_matrix(ir1->operands[1]->type) || |
| glsl_type_is_matrix(ir2->operands[0]->type) || |
| glsl_type_is_matrix(ir2->operands[1]->type)) |
| return false; |
| |
| void *mem_ctx = ralloc_parent(ir2); |
| |
| ir_constant *ir2_const[2]; |
| ir2_const[0] = ir2->operands[0]->constant_expression_value(mem_ctx); |
| ir2_const[1] = ir2->operands[1]->constant_expression_value(mem_ctx); |
| |
| if (ir2_const[0] && ir2_const[1]) |
| return false; |
| |
| if (ir2_const[0]) { |
| reassociate_operands(ir1, const_index, ir2, 1); |
| return true; |
| } else if (ir2_const[1]) { |
| reassociate_operands(ir1, const_index, ir2, 0); |
| return true; |
| } |
| |
| if (reassociate_constant(ir1, const_index, constant, |
| ir2->operands[0]->as_expression())) { |
| update_type(ir2); |
| return true; |
| } |
| |
| if (reassociate_constant(ir1, const_index, constant, |
| ir2->operands[1]->as_expression())) { |
| update_type(ir2); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* When eliminating an expression and just returning one of its operands, |
| * we may need to swizzle that operand out to a vector if the expression was |
| * vector type. |
| */ |
| ir_rvalue * |
| ir_algebraic_visitor::swizzle_if_required(ir_expression *expr, |
| ir_rvalue *operand) |
| { |
| if (glsl_type_is_vector(expr->type) && glsl_type_is_scalar(operand->type)) { |
| return new(mem_ctx) ir_swizzle(operand, 0, 0, 0, 0, |
| expr->type->vector_elements); |
| } else |
| return operand; |
| } |
| |
| ir_rvalue * |
| ir_algebraic_visitor::handle_expression(ir_expression *ir) |
| { |
| ir_constant *op_const[4] = {NULL, NULL, NULL, NULL}; |
| ir_expression *op_expr[4] = {NULL, NULL, NULL, NULL}; |
| |
| if (ir->operation == ir_binop_mul && |
| glsl_type_is_matrix(ir->operands[0]->type) && |
| glsl_type_is_vector(ir->operands[1]->type)) { |
| ir_expression *matrix_mul = ir->operands[0]->as_expression(); |
| |
| if (matrix_mul && matrix_mul->operation == ir_binop_mul && |
| glsl_type_is_matrix(matrix_mul->operands[0]->type) && |
| glsl_type_is_matrix(matrix_mul->operands[1]->type)) { |
| |
| return mul(matrix_mul->operands[0], |
| mul(matrix_mul->operands[1], ir->operands[1])); |
| } |
| } |
| |
| assert(ir->num_operands <= 4); |
| for (unsigned i = 0; i < ir->num_operands; i++) { |
| if (glsl_type_is_matrix(ir->operands[i]->type)) |
| return ir; |
| |
| op_const[i] = |
| ir->operands[i]->constant_expression_value(ralloc_parent(ir)); |
| op_expr[i] = ir->operands[i]->as_expression(); |
| } |
| |
| if (this->mem_ctx == NULL) |
| this->mem_ctx = ralloc_parent(ir); |
| |
| switch (ir->operation) { |
| case ir_binop_add: |
| /* Reassociate addition of constants so that we can do constant |
| * folding. |
| */ |
| if (op_const[0] && !op_const[1]) |
| reassociate_constant(ir, 0, op_const[0], op_expr[1]); |
| if (op_const[1] && !op_const[0]) |
| reassociate_constant(ir, 1, op_const[1], op_expr[0]); |
| |
| break; |
| |
| case ir_binop_mul: |
| /* Reassociate multiplication of constants so that we can do |
| * constant folding. |
| */ |
| if (op_const[0] && !op_const[1]) |
| reassociate_constant(ir, 0, op_const[0], op_expr[1]); |
| if (op_const[1] && !op_const[0]) |
| reassociate_constant(ir, 1, op_const[1], op_expr[0]); |
| break; |
| |
| case ir_binop_min: |
| case ir_binop_max: |
| if (!glsl_type_is_float(ir->type)) |
| break; |
| |
| /* Replace min(max) operations and its commutative combinations with |
| * a saturate operation |
| */ |
| for (int op = 0; op < 2; op++) { |
| ir_expression *inner_expr = op_expr[op]; |
| ir_constant *outer_const = op_const[1 - op]; |
| ir_expression_operation op_cond = (ir->operation == ir_binop_max) ? |
| ir_binop_min : ir_binop_max; |
| |
| if (!inner_expr || !outer_const || (inner_expr->operation != op_cond)) |
| continue; |
| |
| /* One of these has to be a constant */ |
| if (!inner_expr->operands[0]->as_constant() && |
| !inner_expr->operands[1]->as_constant()) |
| break; |
| |
| /* Found a min(max) combination. Now try to see if its operands |
| * meet our conditions that we can do just a single saturate operation |
| */ |
| for (int minmax_op = 0; minmax_op < 2; minmax_op++) { |
| ir_rvalue *x = inner_expr->operands[minmax_op]; |
| ir_rvalue *y = inner_expr->operands[1 - minmax_op]; |
| |
| ir_constant *inner_const = y->as_constant(); |
| if (!inner_const) |
| continue; |
| |
| /* min(max(x, 0.0), 1.0) is sat(x) */ |
| if (ir->operation == ir_binop_min && |
| inner_const->is_zero() && |
| outer_const->is_one()) |
| return saturate(x); |
| |
| /* max(min(x, 1.0), 0.0) is sat(x) */ |
| if (ir->operation == ir_binop_max && |
| inner_const->is_one() && |
| outer_const->is_zero()) |
| return saturate(x); |
| |
| /* min(max(x, 0.0), b) where b < 1.0 is sat(min(x, b)) */ |
| if (ir->operation == ir_binop_min && |
| inner_const->is_zero() && |
| is_less_than_one(outer_const)) |
| return saturate(expr(ir_binop_min, x, outer_const)); |
| |
| /* max(min(x, b), 0.0) where b < 1.0 is sat(min(x, b)) */ |
| if (ir->operation == ir_binop_max && |
| is_less_than_one(inner_const) && |
| outer_const->is_zero()) |
| return saturate(expr(ir_binop_min, x, inner_const)); |
| |
| /* max(min(x, 1.0), b) where b > 0.0 is sat(max(x, b)) */ |
| if (ir->operation == ir_binop_max && |
| inner_const->is_one() && |
| is_greater_than_zero(outer_const)) |
| return saturate(expr(ir_binop_max, x, outer_const)); |
| |
| /* min(max(x, b), 1.0) where b > 0.0 is sat(max(x, b)) */ |
| if (ir->operation == ir_binop_min && |
| is_greater_than_zero(inner_const) && |
| outer_const->is_one()) |
| return saturate(expr(ir_binop_max, x, inner_const)); |
| } |
| } |
| |
| break; |
| |
| /* Remove interpolateAt* instructions for demoted inputs. They are |
| * assigned a constant expression to facilitate this. |
| */ |
| case ir_unop_interpolate_at_centroid: |
| case ir_binop_interpolate_at_offset: |
| case ir_binop_interpolate_at_sample: |
| if (op_const[0]) |
| return ir->operands[0]; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return ir; |
| } |
| |
| void |
| ir_algebraic_visitor::handle_rvalue(ir_rvalue **rvalue) |
| { |
| if (!*rvalue) |
| return; |
| |
| ir_expression *expr = (*rvalue)->as_expression(); |
| if (!expr || expr->operation == ir_quadop_vector) |
| return; |
| |
| ir_rvalue *new_rvalue = handle_expression(expr); |
| if (new_rvalue == *rvalue) |
| return; |
| |
| /* If the expr used to be some vec OP scalar returning a vector, and the |
| * optimization gave us back a scalar, we still need to turn it into a |
| * vector. |
| */ |
| *rvalue = swizzle_if_required(expr, new_rvalue); |
| |
| this->progress = true; |
| } |
| |
| bool |
| do_algebraic(exec_list *instructions, bool native_integers, |
| const struct gl_shader_compiler_options *options) |
| { |
| ir_algebraic_visitor v(native_integers, options); |
| |
| visit_list_elements(&v, instructions); |
| |
| return v.progress; |
| } |