glsl_types.cpp \
glsl_symbol_table.cpp \
hir_field_selection.cpp \
- ir_algebraic.cpp \
ir_basic_block.cpp \
ir_clone.cpp \
ir_constant_expression.cpp \
- ir_constant_folding.cpp \
- ir_constant_propagation.cpp \
- ir_constant_variable.cpp \
- ir_copy_propagation.cpp \
ir.cpp \
- ir_dead_code.cpp \
- ir_dead_code_local.cpp \
- ir_dead_functions.cpp \
- ir_div_to_mul_rcp.cpp \
- ir_explog_to_explog2.cpp \
ir_expression_flattening.cpp \
ir_function_can_inline.cpp \
ir_function.cpp \
- ir_function_inlining.cpp \
ir_hierarchical_visitor.cpp \
ir_hv_accept.cpp \
- ir_if_simplification.cpp \
- ir_if_to_cond_assign.cpp \
ir_import_prototypes.cpp \
- ir_lower_jumps.cpp \
- ir_mat_op_to_vec.cpp \
- ir_mod_to_fract.cpp \
- ir_noop_swizzle.cpp \
ir_print_visitor.cpp \
ir_reader.cpp \
ir_rvalue_visitor.cpp \
ir_set_program_inouts.cpp \
- ir_structure_splitting.cpp \
- ir_sub_to_add_neg.cpp \
- ir_swizzle_swizzle.cpp \
- ir_tree_grafting.cpp \
ir_validate.cpp \
ir_variable.cpp \
ir_variable_refcount.cpp \
- ir_vec_index_to_cond_assign.cpp \
- ir_vec_index_to_swizzle.cpp \
linker.cpp \
link_functions.cpp \
loop_analysis.cpp \
loop_controls.cpp \
loop_unroll.cpp \
+ lower_div_to_mul_rcp.cpp \
+ lower_explog_to_explog2.cpp \
+ lower_if_to_cond_assign.cpp \
+ lower_jumps.cpp \
+ lower_mat_op_to_vec.cpp \
+ lower_mod_to_fract.cpp \
lower_noise.cpp \
+ lower_sub_to_add_neg.cpp \
lower_texture_projection.cpp \
lower_variable_index_to_cond_assign.cpp \
+ lower_vec_index_to_cond_assign.cpp \
+ lower_vec_index_to_swizzle.cpp \
+ opt_algebraic.cpp \
+ opt_constant_folding.cpp \
+ opt_constant_propagation.cpp \
+ opt_constant_variable.cpp \
+ opt_copy_propagation.cpp \
+ opt_dead_code.cpp \
+ opt_dead_code_local.cpp \
+ opt_dead_functions.cpp \
+ opt_function_inlining.cpp \
+ opt_if_simplification.cpp \
+ opt_noop_swizzle.cpp \
opt_redundant_jumps.cpp \
+ opt_structure_splitting.cpp \
+ opt_swizzle_swizzle.cpp \
+ opt_tree_grafting.cpp \
s_expression.cpp
LIBS = \
'glsl_types.cpp',
'glsl_symbol_table.cpp',
'hir_field_selection.cpp',
- 'ir_algebraic.cpp',
'ir_basic_block.cpp',
'ir_clone.cpp',
- 'ir_constant_expression.cpp',
- 'ir_constant_folding.cpp',
- 'ir_constant_propagation.cpp',
- 'ir_constant_variable.cpp',
- 'ir_copy_propagation.cpp',
'ir.cpp',
- 'ir_dead_code.cpp',
- 'ir_dead_code_local.cpp',
- 'ir_dead_functions.cpp',
- 'ir_div_to_mul_rcp.cpp',
- 'ir_explog_to_explog2.cpp',
'ir_expression_flattening.cpp',
'ir_function_can_inline.cpp',
'ir_function.cpp',
- 'ir_function_inlining.cpp',
'ir_hierarchical_visitor.cpp',
'ir_hv_accept.cpp',
- 'ir_if_simplification.cpp',
- 'ir_if_to_cond_assign.cpp',
'ir_import_prototypes.cpp',
- 'ir_lower_jumps.cpp',
- 'ir_mat_op_to_vec.cpp',
- 'ir_mod_to_fract.cpp',
- 'ir_noop_swizzle.cpp',
'ir_print_visitor.cpp',
'ir_reader.cpp',
'ir_rvalue_visitor.cpp',
'ir_set_program_inouts.cpp',
- 'ir_structure_splitting.cpp',
- 'ir_sub_to_add_neg.cpp',
- 'ir_swizzle_swizzle.cpp',
- 'ir_tree_grafting.cpp',
'ir_validate.cpp',
'ir_variable.cpp',
'ir_variable_refcount.cpp',
- 'ir_vec_index_to_cond_assign.cpp',
- 'ir_vec_index_to_swizzle.cpp',
'linker.cpp',
'link_functions.cpp',
'loop_analysis.cpp',
'loop_controls.cpp',
'loop_unroll.cpp',
+ 'lower_div_to_mul_rcp.cpp',
+ 'lower_explog_to_explog2.cpp',
+ 'lower_if_to_cond_assign.cpp',
+ 'lower_jumps.cpp',
+ 'lower_mat_op_to_vec.cpp',
+ 'lower_mod_to_fract.cpp',
'lower_noise.cpp',
+ 'lower_sub_to_add_neg.cpp',
'lower_variable_index_to_cond_assign.cpp',
+ 'lower_vec_index_to_cond_assign.cpp',
+ 'lower_vec_index_to_swizzle.cpp',
+ 'opt_algebraic.cpp',
+ 'opt_constant_folding.cpp',
+ 'opt_noop_swizzle.cpp',
'opt_redundant_jumps.cpp',
+ 'opt_constant_expression.cpp',
+ 'opt_constant_propagation.cpp',
+ 'opt_constant_variable.cpp',
+ 'opt_copy_propagation.cpp',
+ 'opt_dead_code.cpp',
+ 'opt_dead_code_local.cpp',
+ 'opt_dead_functions.cpp',
+ 'opt_function_inlining.cpp',
+ 'opt_if_simplification.cpp',
+ 'opt_structure_splitting.cpp',
+ 'opt_swizzle_swizzle.cpp',
+ 'opt_tree_grafting.cpp',
's_expression.cpp',
]
+++ /dev/null
-/*
- * 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 ir_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 "glsl_types.h"
-
-/**
- * Visitor class for replacing expressions with ir_constant values.
- */
-
-class ir_algebraic_visitor : public ir_rvalue_visitor {
-public:
- ir_algebraic_visitor()
- {
- this->progress = false;
- this->mem_ctx = NULL;
- }
-
- virtual ~ir_algebraic_visitor()
- {
- }
-
- 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);
-
- void *mem_ctx;
-
- bool progress;
-};
-
-static bool
-is_vec_zero(ir_constant *ir)
-{
- int c;
-
- if (!ir)
- return false;
- if (!ir->type->is_scalar() &&
- !ir->type->is_vector())
- return false;
-
- for (c = 0; c < ir->type->vector_elements; c++) {
- switch (ir->type->base_type) {
- case GLSL_TYPE_FLOAT:
- if (ir->value.f[c] != 0.0)
- return false;
- break;
- case GLSL_TYPE_INT:
- if (ir->value.i[c] != 0)
- return false;
- break;
- case GLSL_TYPE_UINT:
- if (ir->value.u[c] != 0)
- return false;
- break;
- case GLSL_TYPE_BOOL:
- if (ir->value.b[c] != false)
- return false;
- break;
- default:
- assert(!"bad base type");
- return false;
- }
- }
-
- return true;
-}
-
-static bool
-is_vec_one(ir_constant *ir)
-{
- int c;
-
- if (!ir)
- return false;
- if (!ir->type->is_scalar() &&
- !ir->type->is_vector())
- return false;
-
- for (c = 0; c < ir->type->vector_elements; c++) {
- switch (ir->type->base_type) {
- case GLSL_TYPE_FLOAT:
- if (ir->value.f[c] != 1.0)
- return false;
- break;
- case GLSL_TYPE_INT:
- if (ir->value.i[c] != 1)
- return false;
- break;
- case GLSL_TYPE_UINT:
- if (ir->value.u[c] != 1)
- return false;
- break;
- case GLSL_TYPE_BOOL:
- if (ir->value.b[c] != true)
- return false;
- break;
- default:
- assert(!"bad base type");
- return false;
- }
- }
-
- return true;
-}
-
-static void
-update_type(ir_expression *ir)
-{
- if (ir->operands[0]->type->is_vector())
- 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 send 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 (ir1->operands[0]->type->is_matrix() ||
- ir1->operands[0]->type->is_matrix() ||
- ir2->operands[1]->type->is_matrix() ||
- ir2->operands[1]->type->is_matrix())
- return false;
-
- ir_constant *ir2_const[2];
- ir2_const[0] = ir2->operands[0]->constant_expression_value();
- ir2_const[1] = ir2->operands[1]->constant_expression_value();
-
- 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 (expr->type->is_vector() && operand->type->is_scalar()) {
- 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[2] = {NULL, NULL};
- ir_expression *op_expr[2] = {NULL, NULL};
- ir_expression *temp;
- unsigned int i;
-
- for (i = 0; i < ir->get_num_operands(); i++) {
- if (ir->operands[i]->type->is_matrix())
- return ir;
-
- op_const[i] = ir->operands[i]->constant_expression_value();
- op_expr[i] = ir->operands[i]->as_expression();
- }
-
- if (this->mem_ctx == NULL)
- this->mem_ctx = talloc_parent(ir);
-
- switch (ir->operation) {
- case ir_unop_logic_not: {
- enum ir_expression_operation new_op = ir_unop_logic_not;
-
- if (op_expr[0] == NULL)
- break;
-
- switch (op_expr[0]->operation) {
- case ir_binop_less: new_op = ir_binop_gequal; break;
- case ir_binop_greater: new_op = ir_binop_lequal; break;
- case ir_binop_lequal: new_op = ir_binop_greater; break;
- case ir_binop_gequal: new_op = ir_binop_less; break;
- case ir_binop_equal: new_op = ir_binop_nequal; break;
- case ir_binop_nequal: new_op = ir_binop_equal; break;
- case ir_binop_all_equal: new_op = ir_binop_any_nequal; break;
- case ir_binop_any_nequal: new_op = ir_binop_all_equal; break;
-
- default:
- /* The default case handler is here to silence a warning from GCC.
- */
- break;
- }
-
- if (new_op != ir_unop_logic_not) {
- this->progress = true;
- return new(mem_ctx) ir_expression(new_op,
- ir->type,
- op_expr[0]->operands[0],
- op_expr[0]->operands[1]);
- }
-
- break;
- }
-
- case ir_binop_add:
- if (is_vec_zero(op_const[0])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[1]);
- }
- if (is_vec_zero(op_const[1])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[0]);
- }
-
- /* 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],
- ir->operands[1]->as_expression());
- if (op_const[1] && !op_const[0])
- reassociate_constant(ir, 1, op_const[1],
- ir->operands[0]->as_expression());
- break;
-
- case ir_binop_sub:
- if (is_vec_zero(op_const[0])) {
- this->progress = true;
- temp = new(mem_ctx) ir_expression(ir_unop_neg,
- ir->operands[1]->type,
- ir->operands[1],
- NULL);
- return swizzle_if_required(ir, temp);
- }
- if (is_vec_zero(op_const[1])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[0]);
- }
- break;
-
- case ir_binop_mul:
- if (is_vec_one(op_const[0])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[1]);
- }
- if (is_vec_one(op_const[1])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[0]);
- }
-
- if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
- this->progress = true;
- return ir_constant::zero(ir, ir->type);
- }
-
- /* 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],
- ir->operands[1]->as_expression());
- if (op_const[1] && !op_const[0])
- reassociate_constant(ir, 1, op_const[1],
- ir->operands[0]->as_expression());
-
- break;
-
- case ir_binop_div:
- if (is_vec_one(op_const[0]) && ir->type->base_type == GLSL_TYPE_FLOAT) {
- this->progress = true;
- temp = new(mem_ctx) ir_expression(ir_unop_rcp,
- ir->operands[1]->type,
- ir->operands[1],
- NULL);
- return swizzle_if_required(ir, temp);
- }
- if (is_vec_one(op_const[1])) {
- this->progress = true;
- return swizzle_if_required(ir, ir->operands[0]);
- }
- break;
-
- case ir_binop_logic_and:
- /* FINISHME: Also simplify (a && a) to (a). */
- if (is_vec_one(op_const[0])) {
- this->progress = true;
- return ir->operands[1];
- } else if (is_vec_one(op_const[1])) {
- this->progress = true;
- return ir->operands[0];
- } else if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
- this->progress = true;
- return ir_constant::zero(mem_ctx, ir->type);
- }
- break;
-
- case ir_binop_logic_xor:
- /* FINISHME: Also simplify (a ^^ a) to (false). */
- if (is_vec_zero(op_const[0])) {
- this->progress = true;
- return ir->operands[1];
- } else if (is_vec_zero(op_const[1])) {
- this->progress = true;
- return ir->operands[0];
- } else if (is_vec_one(op_const[0])) {
- this->progress = true;
- return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
- ir->operands[1], NULL);
- } else if (is_vec_one(op_const[1])) {
- this->progress = true;
- return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
- ir->operands[0], NULL);
- }
- break;
-
- case ir_binop_logic_or:
- /* FINISHME: Also simplify (a || a) to (a). */
- if (is_vec_zero(op_const[0])) {
- this->progress = true;
- return ir->operands[1];
- } else if (is_vec_zero(op_const[1])) {
- this->progress = true;
- return ir->operands[0];
- } else if (is_vec_one(op_const[0]) || is_vec_one(op_const[1])) {
- ir_constant_data data;
-
- for (unsigned i = 0; i < 16; i++)
- data.b[i] = true;
-
- this->progress = true;
- return new(mem_ctx) ir_constant(ir->type, &data);
- }
- break;
-
- case ir_unop_rcp:
- if (op_expr[0] && op_expr[0]->operation == ir_unop_rcp) {
- this->progress = true;
- return op_expr[0]->operands[0];
- }
-
- /* FINISHME: We should do rcp(rsq(x)) -> sqrt(x) for some
- * backends, except that some backends will have done sqrt ->
- * rcp(rsq(x)) and we don't want to undo it for them.
- */
-
- /* As far as we know, all backends are OK with rsq. */
- if (op_expr[0] && op_expr[0]->operation == ir_unop_sqrt) {
- this->progress = true;
- temp = new(mem_ctx) ir_expression(ir_unop_rsq,
- op_expr[0]->operands[0]->type,
- op_expr[0]->operands[0],
- NULL);
- return swizzle_if_required(ir, temp);
- }
-
- 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)
- return;
-
- *rvalue = handle_expression(expr);
-}
-
-bool
-do_algebraic(exec_list *instructions)
-{
- ir_algebraic_visitor v;
-
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_constant_folding.cpp
- * Replace constant-valued expressions with references to constant values.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_rvalue_visitor.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-/**
- * Visitor class for replacing expressions with ir_constant values.
- */
-
-class ir_constant_folding_visitor : public ir_rvalue_visitor {
-public:
- ir_constant_folding_visitor()
- {
- this->progress = false;
- }
-
- virtual ~ir_constant_folding_visitor()
- {
- /* empty */
- }
-
- virtual ir_visitor_status visit_enter(ir_assignment *ir);
- virtual ir_visitor_status visit_enter(ir_call *ir);
-
- virtual void handle_rvalue(ir_rvalue **rvalue);
-
- bool progress;
-};
-
-void
-ir_constant_folding_visitor::handle_rvalue(ir_rvalue **rvalue)
-{
- if (*rvalue == NULL || (*rvalue)->ir_type == ir_type_constant)
- return;
-
- /* Note that we do rvalue visitoring on leaving. So if an
- * expression has a non-constant operand, no need to go looking
- * down it to find if it's constant. This cuts the time of this
- * pass down drastically.
- */
- ir_expression *expr = (*rvalue)->as_expression();
- if (expr) {
- for (unsigned int i = 0; i < expr->get_num_operands(); i++) {
- if (!expr->operands[i]->as_constant())
- return;
- }
- }
-
- ir_constant *constant = (*rvalue)->constant_expression_value();
- if (constant) {
- *rvalue = constant;
- this->progress = true;
- } else {
- (*rvalue)->accept(this);
- }
-}
-
-ir_visitor_status
-ir_constant_folding_visitor::visit_enter(ir_assignment *ir)
-{
- ir->rhs->accept(this);
- handle_rvalue(&ir->rhs);
-
- if (ir->condition) {
- ir->condition->accept(this);
- handle_rvalue(&ir->condition);
-
- ir_constant *const_val = ir->condition->as_constant();
- /* If the condition is constant, either remove the condition or
- * remove the never-executed assignment.
- */
- if (const_val) {
- if (const_val->value.b[0])
- ir->condition = NULL;
- else
- ir->remove();
- this->progress = true;
- }
- }
-
- /* Don't descend into the LHS because we want it to stay as a
- * variable dereference. FINISHME: We probably should to get array
- * indices though.
- */
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_constant_folding_visitor::visit_enter(ir_call *ir)
-{
- exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_rvalue *param_rval = (ir_rvalue *)iter.get();
- ir_variable *sig_param = (ir_variable *)sig_iter.get();
-
- if (sig_param->mode == ir_var_in) {
- ir_rvalue *new_param = param_rval;
-
- handle_rvalue(&new_param);
- if (new_param != param_rval) {
- param_rval->replace_with(new_param);
- }
- }
- sig_iter.next();
- }
-
- return visit_continue_with_parent;
-}
-
-bool
-do_constant_folding(exec_list *instructions)
-{
- ir_constant_folding_visitor constant_folding;
-
- visit_list_elements(&constant_folding, instructions);
-
- return constant_folding.progress;
-}
+++ /dev/null
-/*
- * Copyright © 2010 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * constant of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, constant, 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 constantright 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 CONSTANTRIGHT 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 ir_constant_propagation.cpp
- *
- * Tracks assignments of constants to channels of variables, and
- * usage of those constant channels with direct usage of the constants.
- *
- * This can lead to constant folding and algebraic optimizations in
- * those later expressions, while causing no increase in instruction
- * count (due to constants being generally free to load from a
- * constant push buffer or as instruction immediate values) and
- * possibly reducing register pressure.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_rvalue_visitor.h"
-#include "ir_basic_block.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-class acp_entry : public exec_node
-{
-public:
- acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant)
- {
- assert(var);
- assert(constant);
- this->var = var;
- this->write_mask = write_mask;
- this->constant = constant;
- }
-
- ir_variable *var;
- ir_constant *constant;
- unsigned write_mask;
-};
-
-
-class kill_entry : public exec_node
-{
-public:
- kill_entry(ir_variable *var, unsigned write_mask)
- {
- assert(var);
- this->var = var;
- this->write_mask = write_mask;
- }
-
- ir_variable *var;
- unsigned write_mask;
-};
-
-class ir_constant_propagation_visitor : public ir_rvalue_visitor {
-public:
- ir_constant_propagation_visitor()
- {
- progress = false;
- mem_ctx = talloc_new(0);
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- }
- ~ir_constant_propagation_visitor()
- {
- talloc_free(mem_ctx);
- }
-
- virtual ir_visitor_status visit_enter(class ir_loop *);
- virtual ir_visitor_status visit_enter(class ir_function_signature *);
- virtual ir_visitor_status visit_enter(class ir_function *);
- virtual ir_visitor_status visit_leave(class ir_assignment *);
- virtual ir_visitor_status visit_enter(class ir_call *);
- virtual ir_visitor_status visit_enter(class ir_if *);
-
- void add_constant(ir_assignment *ir);
- void kill(ir_variable *ir, unsigned write_mask);
- void handle_if_block(exec_list *instructions);
- void handle_rvalue(ir_rvalue **rvalue);
-
- /** List of acp_entry: The available constants to propagate */
- exec_list *acp;
-
- /**
- * List of kill_entry: The masks of variables whose values were
- * killed in this block.
- */
- exec_list *kills;
-
- bool progress;
-
- bool killed_all;
-
- void *mem_ctx;
-};
-
-
-void
-ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue)
-{
- if (this->in_assignee || !*rvalue)
- return;
-
- const glsl_type *type = (*rvalue)->type;
- if (!type->is_scalar() && !type->is_vector())
- return;
-
- ir_swizzle *swiz = NULL;
- ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
- if (!deref) {
- swiz = (*rvalue)->as_swizzle();
- if (!swiz)
- return;
-
- deref = swiz->val->as_dereference_variable();
- if (!deref)
- return;
- }
-
- ir_constant_data data;
- memset(&data, 0, sizeof(data));
-
- for (unsigned int i = 0; i < type->components(); i++) {
- int channel;
- acp_entry *found = NULL;
-
- if (swiz) {
- switch (i) {
- case 0: channel = swiz->mask.x; break;
- case 1: channel = swiz->mask.y; break;
- case 2: channel = swiz->mask.z; break;
- case 3: channel = swiz->mask.w; break;
- default: assert(!"shouldn't be reached"); channel = 0; break;
- }
- } else {
- channel = i;
- }
-
- foreach_iter(exec_list_iterator, iter, *this->acp) {
- acp_entry *entry = (acp_entry *)iter.get();
- if (entry->var == deref->var && entry->write_mask & (1 << channel)) {
- found = entry;
- break;
- }
- }
-
- if (!found)
- return;
-
- int rhs_channel = 0;
- for (int j = 0; j < 4; j++) {
- if (j == channel)
- break;
- if (found->write_mask & (1 << j))
- rhs_channel++;
- }
-
- switch (type->base_type) {
- case GLSL_TYPE_FLOAT:
- data.f[i] = found->constant->value.f[rhs_channel];
- break;
- case GLSL_TYPE_INT:
- data.i[i] = found->constant->value.i[rhs_channel];
- break;
- case GLSL_TYPE_UINT:
- data.u[i] = found->constant->value.u[rhs_channel];
- break;
- case GLSL_TYPE_BOOL:
- data.b[i] = found->constant->value.b[rhs_channel];
- break;
- default:
- assert(!"not reached");
- break;
- }
- }
-
- *rvalue = new(talloc_parent(deref)) ir_constant(type, &data);
- this->progress = true;
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)
-{
- /* Treat entry into a function signature as a completely separate
- * block. Any instructions at global scope will be shuffled into
- * main() at link time, so they're irrelevant to us.
- */
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- visit_list_elements(this, &ir->body);
-
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = orig_killed_all;
-
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_leave(ir_assignment *ir)
-{
- if (this->in_assignee)
- return visit_continue;
-
- kill(ir->lhs->variable_referenced(), ir->write_mask);
-
- add_constant(ir);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_enter(ir_function *ir)
-{
- (void) ir;
- return visit_continue;
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_enter(ir_call *ir)
-{
- /* Do constant propagation on call parameters, but skip any out params */
- exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
- foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
- ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
- ir_rvalue *param = (ir_rvalue *)iter.get();
- if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
- ir_rvalue *new_param = param;
- handle_rvalue(&new_param);
- if (new_param != param)
- param->replace_with(new_param);
- else
- param->accept(this);
- }
- sig_param_iter.next();
- }
-
- /* Since we're unlinked, we don't (necssarily) know the side effects of
- * this call. So kill all copies.
- */
- acp->make_empty();
- this->killed_all = true;
-
- return visit_continue_with_parent;
-}
-
-void
-ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)
-{
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- /* Populate the initial acp with a constant of the original */
- foreach_iter(exec_list_iterator, iter, *orig_acp) {
- acp_entry *a = (acp_entry *)iter.get();
- this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask,
- a->constant));
- }
-
- visit_list_elements(this, instructions);
-
- if (this->killed_all) {
- orig_acp->make_empty();
- }
-
- exec_list *new_kills = this->kills;
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = this->killed_all || orig_killed_all;
-
- foreach_iter(exec_list_iterator, iter, *new_kills) {
- kill_entry *k = (kill_entry *)iter.get();
- kill(k->var, k->write_mask);
- }
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_enter(ir_if *ir)
-{
- ir->condition->accept(this);
- handle_rvalue(&ir->condition);
-
- handle_if_block(&ir->then_instructions);
- handle_if_block(&ir->else_instructions);
-
- /* handle_if_block() already descended into the children. */
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_constant_propagation_visitor::visit_enter(ir_loop *ir)
-{
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- /* FINISHME: For now, the initial acp for loops is totally empty.
- * We could go through once, then go through again with the acp
- * cloned minus the killed entries after the first run through.
- */
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- visit_list_elements(this, &ir->body_instructions);
-
- if (this->killed_all) {
- orig_acp->make_empty();
- }
-
- exec_list *new_kills = this->kills;
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = this->killed_all || orig_killed_all;
-
- foreach_iter(exec_list_iterator, iter, *new_kills) {
- kill_entry *k = (kill_entry *)iter.get();
- kill(k->var, k->write_mask);
- }
-
- /* already descended into the children. */
- return visit_continue_with_parent;
-}
-
-void
-ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask)
-{
- assert(var != NULL);
-
- /* We don't track non-vectors. */
- if (!var->type->is_vector() && !var->type->is_scalar())
- return;
-
- /* Remove any entries currently in the ACP for this kill. */
- foreach_iter(exec_list_iterator, iter, *this->acp) {
- acp_entry *entry = (acp_entry *)iter.get();
-
- if (entry->var == var) {
- entry->write_mask &= ~write_mask;
- if (entry->write_mask == 0)
- entry->remove();
- }
- }
-
- /* Add this writemask of the variable to the list of killed
- * variables in this block.
- */
- foreach_iter(exec_list_iterator, iter, *this->kills) {
- kill_entry *entry = (kill_entry *)iter.get();
-
- if (entry->var == var) {
- entry->write_mask |= write_mask;
- return;
- }
- }
- /* Not already in the list. Make new entry. */
- this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask));
-}
-
-/**
- * Adds an entry to the available constant list if it's a plain assignment
- * of a variable to a variable.
- */
-void
-ir_constant_propagation_visitor::add_constant(ir_assignment *ir)
-{
- acp_entry *entry;
-
- if (ir->condition) {
- ir_constant *condition = ir->condition->as_constant();
- if (!condition || !condition->value.b[0])
- return;
- }
-
- if (!ir->write_mask)
- return;
-
- ir_dereference_variable *deref = ir->lhs->as_dereference_variable();
- ir_constant *constant = ir->rhs->as_constant();
-
- if (!deref || !constant)
- return;
-
- /* Only do constant propagation on vectors. Constant matrices,
- * arrays, or structures would require more work elsewhere.
- */
- if (!deref->var->type->is_vector() && !deref->var->type->is_scalar())
- return;
-
- entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant);
- this->acp->push_tail(entry);
-}
-
-/**
- * Does a constant propagation pass on the code present in the instruction stream.
- */
-bool
-do_constant_propagation(exec_list *instructions)
-{
- ir_constant_propagation_visitor v;
-
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_constant_variable.cpp
- *
- * Marks variables assigned a single constant value over the course
- * of the program as constant.
- *
- * The goal here is to trigger further constant folding and then dead
- * code elimination. This is common with vector/matrix constructors
- * and calls to builtin functions.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-struct assignment_entry {
- exec_node link;
- int assignment_count;
- ir_variable *var;
- ir_constant *constval;
- bool our_scope;
-};
-
-class ir_constant_variable_visitor : public ir_hierarchical_visitor {
-public:
- virtual ir_visitor_status visit_enter(ir_dereference_variable *);
- virtual ir_visitor_status visit(ir_variable *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_call *);
-
- exec_list list;
-};
-
-static struct assignment_entry *
-get_assignment_entry(ir_variable *var, exec_list *list)
-{
- struct assignment_entry *entry;
-
- foreach_list_typed(struct assignment_entry, entry, link, list) {
- if (entry->var == var)
- return entry;
- }
-
- entry = (struct assignment_entry *)calloc(1, sizeof(*entry));
- entry->var = var;
- list->push_head(&entry->link);
- return entry;
-}
-
-ir_visitor_status
-ir_constant_variable_visitor::visit(ir_variable *ir)
-{
- struct assignment_entry *entry = get_assignment_entry(ir, &this->list);
- entry->our_scope = true;
- return visit_continue;
-}
-
-/* Skip derefs of variables so that we can detect declarations. */
-ir_visitor_status
-ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
-{
- (void)ir;
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
-{
- ir_constant *constval;
- struct assignment_entry *entry;
-
- entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list);
- assert(entry);
- entry->assignment_count++;
-
- /* If it's already constant, don't do the work. */
- if (entry->var->constant_value)
- return visit_continue;
-
- /* OK, now find if we actually have all the right conditions for
- * this to be a constant value assigned to the var.
- */
- if (ir->condition) {
- constval = ir->condition->constant_expression_value();
- if (!constval || !constval->value.b[0])
- return visit_continue;
- }
-
- ir_variable *var = ir->whole_variable_written();
- if (!var)
- return visit_continue;
-
- constval = ir->rhs->constant_expression_value();
- if (!constval)
- return visit_continue;
-
- /* Mark this entry as having a constant assignment (if the
- * assignment count doesn't go >1). do_constant_variable will fix
- * up the variable with the constant value later.
- */
- entry->constval = constval;
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_constant_variable_visitor::visit_enter(ir_call *ir)
-{
- exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_rvalue *param_rval = (ir_rvalue *)iter.get();
- ir_variable *param = (ir_variable *)sig_iter.get();
-
- if (param->mode == ir_var_out ||
- param->mode == ir_var_inout) {
- ir_variable *var = param_rval->variable_referenced();
- struct assignment_entry *entry;
-
- assert(var);
- entry = get_assignment_entry(var, &this->list);
- entry->assignment_count++;
- }
- sig_iter.next();
- }
- return visit_continue;
-}
-
-/**
- * Does a copy propagation pass on the code present in the instruction stream.
- */
-bool
-do_constant_variable(exec_list *instructions)
-{
- bool progress = false;
- ir_constant_variable_visitor v;
-
- v.run(instructions);
-
- while (!v.list.is_empty()) {
-
- struct assignment_entry *entry;
- entry = exec_node_data(struct assignment_entry, v.list.head, link);
-
- if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
- entry->var->constant_value = entry->constval;
- progress = true;
- }
- entry->link.remove();
- free(entry);
- }
-
- return progress;
-}
-
-bool
-do_constant_variable_unlinked(exec_list *instructions)
-{
- bool progress = false;
-
- foreach_iter(exec_list_iterator, iter, *instructions) {
- ir_instruction *ir = (ir_instruction *)iter.get();
- ir_function *f = ir->as_function();
- if (f) {
- foreach_iter(exec_list_iterator, sigiter, *f) {
- ir_function_signature *sig =
- (ir_function_signature *) sigiter.get();
- if (do_constant_variable(&sig->body))
- progress = true;
- }
- }
- }
-
- return progress;
-}
+++ /dev/null
-/*
- * 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 ir_copy_propagation.cpp
- *
- * Moves usage of recently-copied variables to the previous copy of
- * the variable.
- *
- * This should reduce the number of MOV instructions in the generated
- * programs unless copy propagation is also done on the LIR, and may
- * help anyway by triggering other optimizations that live in the HIR.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_basic_block.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-class acp_entry : public exec_node
-{
-public:
- acp_entry(ir_variable *lhs, ir_variable *rhs)
- {
- assert(lhs);
- assert(rhs);
- this->lhs = lhs;
- this->rhs = rhs;
- }
-
- ir_variable *lhs;
- ir_variable *rhs;
-};
-
-
-class kill_entry : public exec_node
-{
-public:
- kill_entry(ir_variable *var)
- {
- assert(var);
- this->var = var;
- }
-
- ir_variable *var;
-};
-
-class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
-public:
- ir_copy_propagation_visitor()
- {
- progress = false;
- mem_ctx = talloc_new(0);
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- }
- ~ir_copy_propagation_visitor()
- {
- talloc_free(mem_ctx);
- }
-
- virtual ir_visitor_status visit(class ir_dereference_variable *);
- virtual ir_visitor_status visit_enter(class ir_loop *);
- virtual ir_visitor_status visit_enter(class ir_function_signature *);
- virtual ir_visitor_status visit_enter(class ir_function *);
- virtual ir_visitor_status visit_leave(class ir_assignment *);
- virtual ir_visitor_status visit_enter(class ir_call *);
- virtual ir_visitor_status visit_enter(class ir_if *);
-
- void add_copy(ir_assignment *ir);
- void kill(ir_variable *ir);
- void handle_if_block(exec_list *instructions);
-
- /** List of acp_entry: The available copies to propagate */
- exec_list *acp;
- /**
- * List of kill_entry: The variables whose values were killed in this
- * block.
- */
- exec_list *kills;
-
- bool progress;
-
- bool killed_all;
-
- void *mem_ctx;
-};
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
-{
- /* Treat entry into a function signature as a completely separate
- * block. Any instructions at global scope will be shuffled into
- * main() at link time, so they're irrelevant to us.
- */
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- visit_list_elements(this, &ir->body);
-
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = orig_killed_all;
-
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_leave(ir_assignment *ir)
-{
- kill(ir->lhs->variable_referenced());
-
- add_copy(ir);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_enter(ir_function *ir)
-{
- (void) ir;
- return visit_continue;
-}
-
-/**
- * Replaces dereferences of ACP RHS variables with ACP LHS variables.
- *
- * This is where the actual copy propagation occurs. Note that the
- * rewriting of ir_dereference means that the ir_dereference instance
- * must not be shared by multiple IR operations!
- */
-ir_visitor_status
-ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
-{
- if (this->in_assignee)
- return visit_continue;
-
- ir_variable *var = ir->var;
-
- foreach_iter(exec_list_iterator, iter, *this->acp) {
- acp_entry *entry = (acp_entry *)iter.get();
-
- if (var == entry->lhs) {
- ir->var = entry->rhs;
- this->progress = true;
- break;
- }
- }
-
- return visit_continue;
-}
-
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_enter(ir_call *ir)
-{
- /* Do copy propagation on call parameters, but skip any out params */
- exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
- foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
- ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
- ir_instruction *ir = (ir_instruction *)iter.get();
- if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
- ir->accept(this);
- }
- sig_param_iter.next();
- }
-
- /* Since we're unlinked, we don't (necssarily) know the side effects of
- * this call. So kill all copies.
- */
- acp->make_empty();
- this->killed_all = true;
-
- return visit_continue_with_parent;
-}
-
-void
-ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
-{
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- /* Populate the initial acp with a copy of the original */
- foreach_iter(exec_list_iterator, iter, *orig_acp) {
- acp_entry *a = (acp_entry *)iter.get();
- this->acp->push_tail(new(this->mem_ctx) acp_entry(a->lhs, a->rhs));
- }
-
- visit_list_elements(this, instructions);
-
- if (this->killed_all) {
- orig_acp->make_empty();
- }
-
- exec_list *new_kills = this->kills;
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = this->killed_all || orig_killed_all;
-
- foreach_iter(exec_list_iterator, iter, *new_kills) {
- kill_entry *k = (kill_entry *)iter.get();
- kill(k->var);
- }
-}
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_enter(ir_if *ir)
-{
- ir->condition->accept(this);
-
- handle_if_block(&ir->then_instructions);
- handle_if_block(&ir->else_instructions);
-
- /* handle_if_block() already descended into the children. */
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
-{
- exec_list *orig_acp = this->acp;
- exec_list *orig_kills = this->kills;
- bool orig_killed_all = this->killed_all;
-
- /* FINISHME: For now, the initial acp for loops is totally empty.
- * We could go through once, then go through again with the acp
- * cloned minus the killed entries after the first run through.
- */
- this->acp = new(mem_ctx) exec_list;
- this->kills = new(mem_ctx) exec_list;
- this->killed_all = false;
-
- visit_list_elements(this, &ir->body_instructions);
-
- if (this->killed_all) {
- orig_acp->make_empty();
- }
-
- exec_list *new_kills = this->kills;
- this->kills = orig_kills;
- this->acp = orig_acp;
- this->killed_all = this->killed_all || orig_killed_all;
-
- foreach_iter(exec_list_iterator, iter, *new_kills) {
- kill_entry *k = (kill_entry *)iter.get();
- kill(k->var);
- }
-
- /* already descended into the children. */
- return visit_continue_with_parent;
-}
-
-void
-ir_copy_propagation_visitor::kill(ir_variable *var)
-{
- assert(var != NULL);
-
- /* Remove any entries currently in the ACP for this kill. */
- foreach_iter(exec_list_iterator, iter, *acp) {
- acp_entry *entry = (acp_entry *)iter.get();
-
- if (entry->lhs == var || entry->rhs == var) {
- entry->remove();
- }
- }
-
- /* Add the LHS variable to the list of killed variables in this block.
- */
- this->kills->push_tail(new(this->mem_ctx) kill_entry(var));
-}
-
-/**
- * Adds an entry to the available copy list if it's a plain assignment
- * of a variable to a variable.
- */
-void
-ir_copy_propagation_visitor::add_copy(ir_assignment *ir)
-{
- acp_entry *entry;
-
- if (ir->condition) {
- ir_constant *condition = ir->condition->as_constant();
- if (!condition || !condition->value.b[0])
- return;
- }
-
- ir_variable *lhs_var = ir->whole_variable_written();
- ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
-
- if ((lhs_var != NULL) && (rhs_var != NULL)) {
- if (lhs_var == rhs_var) {
- /* This is a dumb assignment, but we've conveniently noticed
- * it here. Removing it now would mess up the loop iteration
- * calling us. Just flag it to not execute, and someone else
- * will clean up the mess.
- */
- ir->condition = new(talloc_parent(ir)) ir_constant(false);
- this->progress = true;
- } else {
- entry = new(this->mem_ctx) acp_entry(lhs_var, rhs_var);
- this->acp->push_tail(entry);
- }
- }
-}
-
-/**
- * Does a copy propagation pass on the code present in the instruction stream.
- */
-bool
-do_copy_propagation(exec_list *instructions)
-{
- ir_copy_propagation_visitor v;
-
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_dead_code.cpp
- *
- * Eliminates dead assignments and variable declarations from the code.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_variable_refcount.h"
-#include "glsl_types.h"
-
-static bool debug = false;
-
-/**
- * Do a dead code pass over instructions and everything that instructions
- * references.
- *
- * Note that this will remove assignments to globals, so it is not suitable
- * for usage on an unlinked instruction stream.
- */
-bool
-do_dead_code(exec_list *instructions)
-{
- ir_variable_refcount_visitor v;
- bool progress = false;
-
- v.run(instructions);
-
- foreach_iter(exec_list_iterator, iter, v.variable_list) {
- variable_entry *entry = (variable_entry *)iter.get();
-
- /* Since each assignment is a reference, the refereneced count must be
- * greater than or equal to the assignment count. If they are equal,
- * then all of the references are assignments, and the variable is
- * dead.
- *
- * Note that if the variable is neither assigned nor referenced, both
- * counts will be zero and will be caught by the equality test.
- */
- assert(entry->referenced_count >= entry->assigned_count);
-
- if (debug) {
- printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n",
- entry->var->name, (void *) entry->var,
- entry->referenced_count, entry->assigned_count,
- entry->declaration ? "" : "not ");
- }
-
- if ((entry->referenced_count > entry->assigned_count)
- || !entry->declaration)
- continue;
-
- if (entry->assign) {
- /* Remove a single dead assignment to the variable we found.
- * Don't do so if it's a shader output, though.
- */
- if (entry->var->mode != ir_var_out &&
- entry->var->mode != ir_var_inout &&
- !ir_has_call(entry->assign)) {
- entry->assign->remove();
- progress = true;
-
- if (debug) {
- printf("Removed assignment to %s@%p\n",
- entry->var->name, (void *) entry->var);
- }
- }
- } else {
- /* If there are no assignments or references to the variable left,
- * then we can remove its declaration.
- */
-
- /* uniform initializers are precious, and could get used by another
- * stage.
- */
- if (entry->var->mode == ir_var_uniform &&
- entry->var->constant_value)
- continue;
-
- entry->var->remove();
- progress = true;
-
- if (debug) {
- printf("Removed declaration of %s@%p\n",
- entry->var->name, (void *) entry->var);
- }
- }
- }
-
- return progress;
-}
-
-/**
- * Does a dead code pass on the functions present in the instruction stream.
- *
- * This is suitable for use while the program is not linked, as it will
- * ignore variable declarations (and the assignments to them) for variables
- * with global scope.
- */
-bool
-do_dead_code_unlinked(exec_list *instructions)
-{
- bool progress = false;
-
- foreach_iter(exec_list_iterator, iter, *instructions) {
- ir_instruction *ir = (ir_instruction *)iter.get();
- ir_function *f = ir->as_function();
- if (f) {
- foreach_iter(exec_list_iterator, sigiter, *f) {
- ir_function_signature *sig =
- (ir_function_signature *) sigiter.get();
- if (do_dead_code(&sig->body))
- progress = true;
- }
- }
- }
-
- return progress;
-}
+++ /dev/null
-/*
- * 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 ir_dead_code_local.cpp
- *
- * Eliminates local dead assignments from the code.
- *
- * This operates on basic blocks, tracking assignments and finding if
- * they're used before the variable is completely reassigned.
- *
- * Compare this to ir_dead_code.cpp, which operates globally looking
- * for assignments to variables that are never read.
- */
-
-#include "ir.h"
-#include "ir_basic_block.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-static bool debug = false;
-
-class assignment_entry : public exec_node
-{
-public:
- assignment_entry(ir_variable *lhs, ir_instruction *ir)
- {
- assert(lhs);
- assert(ir);
- this->lhs = lhs;
- this->ir = ir;
- }
-
- ir_variable *lhs;
- ir_instruction *ir;
-};
-
-class kill_for_derefs_visitor : public ir_hierarchical_visitor {
-public:
- kill_for_derefs_visitor(exec_list *assignments)
- {
- this->assignments = assignments;
- }
-
- virtual ir_visitor_status visit(ir_dereference_variable *ir)
- {
- ir_variable *const var = ir->variable_referenced();
-
- foreach_iter(exec_list_iterator, iter, *this->assignments) {
- assignment_entry *entry = (assignment_entry *)iter.get();
-
- if (entry->lhs == var) {
- if (debug)
- printf("kill %s\n", entry->lhs->name);
- entry->remove();
- }
- }
-
- return visit_continue;
- }
-
-private:
- exec_list *assignments;
-};
-
-class array_index_visit : public ir_hierarchical_visitor {
-public:
- array_index_visit(ir_hierarchical_visitor *v)
- {
- this->visitor = v;
- }
-
- virtual ir_visitor_status visit_enter(class ir_dereference_array *ir)
- {
- ir->array_index->accept(visitor);
- return visit_continue;
- }
-
- static void run(ir_instruction *ir, ir_hierarchical_visitor *v)
- {
- array_index_visit top_visit(v);
- ir->accept(& top_visit);
- }
-
- ir_hierarchical_visitor *visitor;
-};
-
-
-/**
- * Adds an entry to the available copy list if it's a plain assignment
- * of a variable to a variable.
- */
-static bool
-process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments)
-{
- ir_variable *var = NULL;
- bool progress = false;
- kill_for_derefs_visitor v(assignments);
-
- /* Kill assignment entries for things used to produce this assignment. */
- ir->rhs->accept(&v);
- if (ir->condition) {
- ir->condition->accept(&v);
- }
-
- /* Kill assignment enties used as array indices.
- */
- array_index_visit::run(ir->lhs, &v);
- var = ir->lhs->variable_referenced();
- assert(var);
-
- bool always_assign = true;
- if (ir->condition) {
- ir_constant *condition = ir->condition->as_constant();
- if (!condition || !condition->value.b[0])
- always_assign = false;
- }
-
- /* Now, check if we did a whole-variable assignment. */
- if (always_assign && (ir->whole_variable_written() != NULL)) {
- /* We did a whole-variable assignment. So, any instruction in
- * the assignment list with the same LHS is dead.
- */
- if (debug)
- printf("looking for %s to remove\n", var->name);
- foreach_iter(exec_list_iterator, iter, *assignments) {
- assignment_entry *entry = (assignment_entry *)iter.get();
-
- if (entry->lhs == var) {
- if (debug)
- printf("removing %s\n", var->name);
- entry->ir->remove();
- entry->remove();
- progress = true;
- }
- }
- }
-
- /* Add this instruction to the assignment list available to be removed.
- * But not if the assignment has other side effects.
- */
- if (ir_has_call(ir))
- return progress;
-
- assignment_entry *entry = new(ctx) assignment_entry(var, ir);
- assignments->push_tail(entry);
-
- if (debug) {
- printf("add %s\n", var->name);
-
- printf("current entries\n");
- foreach_iter(exec_list_iterator, iter, *assignments) {
- assignment_entry *entry = (assignment_entry *)iter.get();
-
- printf(" %s\n", entry->lhs->name);
- }
- }
-
- return progress;
-}
-
-static void
-dead_code_local_basic_block(ir_instruction *first,
- ir_instruction *last,
- void *data)
-{
- ir_instruction *ir, *ir_next;
- /* List of avaialble_copy */
- exec_list assignments;
- bool *out_progress = (bool *)data;
- bool progress = false;
-
- void *ctx = talloc_new(NULL);
- /* Safe looping, since process_assignment */
- for (ir = first, ir_next = (ir_instruction *)first->next;;
- ir = ir_next, ir_next = (ir_instruction *)ir->next) {
- ir_assignment *ir_assign = ir->as_assignment();
-
- if (debug) {
- ir->print();
- printf("\n");
- }
-
- if (ir_assign) {
- progress = process_assignment(ctx, ir_assign, &assignments) || progress;
- } else {
- kill_for_derefs_visitor kill(&assignments);
- ir->accept(&kill);
- }
-
- if (ir == last)
- break;
- }
- *out_progress = progress;
- talloc_free(ctx);
-}
-
-/**
- * Does a copy propagation pass on the code present in the instruction stream.
- */
-bool
-do_dead_code_local(exec_list *instructions)
-{
- bool progress = false;
-
- call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress);
-
- return progress;
-}
+++ /dev/null
- /*
- * 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 ir_dead_functions.cpp
- *
- * Eliminates unused functions from the linked program.
- */
-
- #include "ir.h"
- #include "ir_visitor.h"
- #include "ir_expression_flattening.h"
- #include "glsl_types.h"
-
- class signature_entry : public exec_node
- {
- public:
- signature_entry(ir_function_signature *sig)
- {
- this->signature = sig;
- this->used = false;
- }
-
- ir_function_signature *signature;
- bool used;
- };
-
- class ir_dead_functions_visitor : public ir_hierarchical_visitor {
- public:
- ir_dead_functions_visitor()
- {
- this->mem_ctx = talloc_new(NULL);
- }
-
- ~ir_dead_functions_visitor()
- {
- talloc_free(this->mem_ctx);
- }
-
- virtual ir_visitor_status visit_enter(ir_function_signature *);
- virtual ir_visitor_status visit_enter(ir_call *);
-
- signature_entry *get_signature_entry(ir_function_signature *var);
-
- bool (*predicate)(ir_instruction *ir);
-
- /* List of signature_entry */
- exec_list signature_list;
- void *mem_ctx;
- };
-
-
- signature_entry *
- ir_dead_functions_visitor::get_signature_entry(ir_function_signature *sig)
- {
- foreach_iter(exec_list_iterator, iter, this->signature_list) {
- signature_entry *entry = (signature_entry *)iter.get();
- if (entry->signature == sig)
- return entry;
- }
-
- signature_entry *entry = new(mem_ctx) signature_entry(sig);
- this->signature_list.push_tail(entry);
- return entry;
- }
-
-
- ir_visitor_status
- ir_dead_functions_visitor::visit_enter(ir_function_signature *ir)
- {
- signature_entry *entry = this->get_signature_entry(ir);
-
- if (strcmp(ir->function_name(), "main") == 0) {
- entry->used = true;
- }
-
- return visit_continue;
- }
-
-
- ir_visitor_status
- ir_dead_functions_visitor::visit_enter(ir_call *ir)
- {
- signature_entry *entry = this->get_signature_entry(ir->get_callee());
-
- entry->used = true;
-
- return visit_continue;
-}
-
-bool
-do_dead_functions(exec_list *instructions)
-{
- ir_dead_functions_visitor v;
- bool progress = false;
-
- visit_list_elements(&v, instructions);
-
- /* Now that we've figured out which function signatures are used, remove
- * the unused ones, and remove function definitions that have no more
- * signatures.
- */
- foreach_iter(exec_list_iterator, iter, v.signature_list) {
- signature_entry *entry = (signature_entry *)iter.get();
-
- if (!entry->used) {
- entry->signature->remove();
- delete entry->signature;
- progress = true;
- }
- delete(entry);
- }
-
- /* We don't just do this above when we nuked a signature because of
- * const pointers.
- */
- foreach_iter(exec_list_iterator, iter, *instructions) {
- ir_instruction *ir = (ir_instruction *)iter.get();
- ir_function *func = ir->as_function();
-
- if (func && func->signatures.is_empty()) {
- /* At this point (post-linking), the symbol table is no
- * longer in use, so not removing the function from the
- * symbol table should be OK.
- */
- func->remove();
- delete func;
- progress = true;
- }
- }
-
- return progress;
-}
+++ /dev/null
-/*
- * 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 ir_div_to_mul_rcp.cpp
- *
- * Breaks an ir_unop_div expression down to op0 * (rcp(op1)).
- *
- * Many GPUs don't have a divide instruction (945 and 965 included),
- * but they do have an RCP instruction to compute an approximate
- * reciprocal. By breaking the operation down, constant reciprocals
- * can get constant folded.
- */
-
-#include "ir.h"
-#include "glsl_types.h"
-
-class ir_div_to_mul_rcp_visitor : public ir_hierarchical_visitor {
-public:
- ir_div_to_mul_rcp_visitor()
- {
- this->made_progress = false;
- }
-
- ir_visitor_status visit_leave(ir_expression *);
-
- bool made_progress;
-};
-
-bool
-do_div_to_mul_rcp(exec_list *instructions)
-{
- ir_div_to_mul_rcp_visitor v;
-
- visit_list_elements(&v, instructions);
- return v.made_progress;
-}
-
-ir_visitor_status
-ir_div_to_mul_rcp_visitor::visit_leave(ir_expression *ir)
-{
- if (ir->operation != ir_binop_div)
- return visit_continue;
-
- if (ir->operands[1]->type->base_type != GLSL_TYPE_INT &&
- ir->operands[1]->type->base_type != GLSL_TYPE_UINT) {
- /* New expression for the 1.0 / op1 */
- ir_rvalue *expr;
- expr = new(ir) ir_expression(ir_unop_rcp,
- ir->operands[1]->type,
- ir->operands[1],
- NULL);
-
- /* op0 / op1 -> op0 * (1.0 / op1) */
- ir->operation = ir_binop_mul;
- ir->operands[1] = expr;
- } else {
- /* Be careful with integer division -- we need to do it as a
- * float and re-truncate, since rcp(n > 1) of an integer would
- * just be 0.
- */
- ir_rvalue *op0, *op1;
- const struct glsl_type *vec_type;
-
- vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
- ir->operands[1]->type->vector_elements,
- ir->operands[1]->type->matrix_columns);
-
- if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
- op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
- else
- op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
-
- op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
-
- vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
- ir->operands[0]->type->vector_elements,
- ir->operands[0]->type->matrix_columns);
-
- if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
- op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
- else
- op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
-
- op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
-
- ir->operation = ir_unop_f2i;
- ir->operands[0] = op0;
- ir->operands[1] = NULL;
- }
-
- this->made_progress = true;
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_explog_to_explog2.cpp
- *
- * Many GPUs don't have a base e log or exponent instruction, but they
- * do have base 2 versions, so this pass converts exp and log to exp2
- * and log2 operations.
- */
-
-#include "main/core.h" /* for log2f on MSVC */
-#include "ir.h"
-#include "glsl_types.h"
-
-class ir_explog_to_explog2_visitor : public ir_hierarchical_visitor {
-public:
- ir_explog_to_explog2_visitor()
- {
- this->progress = false;
- }
-
- ir_visitor_status visit_leave(ir_expression *);
-
- bool progress;
-};
-
-bool
-do_explog_to_explog2(exec_list *instructions)
-{
- ir_explog_to_explog2_visitor v;
-
- visit_list_elements(&v, instructions);
- return v.progress;
-}
-
-ir_visitor_status
-ir_explog_to_explog2_visitor::visit_leave(ir_expression *ir)
-{
- if (ir->operation == ir_unop_exp) {
- void *mem_ctx = talloc_parent(ir);
- ir_constant *log2_e = new(mem_ctx) ir_constant(log2f(M_E));
-
- ir->operation = ir_unop_exp2;
- ir->operands[0] = new(mem_ctx) ir_expression(ir_binop_mul,
- ir->operands[0]->type,
- ir->operands[0],
- log2_e);
- this->progress = true;
- }
-
- if (ir->operation == ir_unop_log) {
- void *mem_ctx = talloc_parent(ir);
-
- ir->operation = ir_binop_mul;
- ir->operands[0] = new(mem_ctx) ir_expression(ir_unop_log2,
- ir->operands[0]->type,
- ir->operands[0],
- NULL);
- ir->operands[1] = new(mem_ctx) ir_constant(1.0f / log2f(M_E));
- this->progress = true;
- }
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_function_inlining.cpp
- *
- * Replaces calls to functions with the body of the function.
- */
-
-#include <inttypes.h>
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_function_inlining.h"
-#include "ir_expression_flattening.h"
-#include "glsl_types.h"
-#include "program/hash_table.h"
-
-static void
-do_sampler_replacement(exec_list *instructions,
- ir_variable *sampler,
- ir_dereference *deref);
-
-class ir_function_inlining_visitor : public ir_hierarchical_visitor {
-public:
- ir_function_inlining_visitor()
- {
- progress = false;
- }
-
- virtual ~ir_function_inlining_visitor()
- {
- /* empty */
- }
-
- virtual ir_visitor_status visit_enter(ir_expression *);
- virtual ir_visitor_status visit_enter(ir_call *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_return *);
- virtual ir_visitor_status visit_enter(ir_texture *);
- virtual ir_visitor_status visit_enter(ir_swizzle *);
-
- bool progress;
-};
-
-
-bool
-automatic_inlining_predicate(ir_instruction *ir)
-{
- ir_call *call = ir->as_call();
-
- if (call && can_inline(call))
- return true;
-
- return false;
-}
-
-bool
-do_function_inlining(exec_list *instructions)
-{
- ir_function_inlining_visitor v;
-
- do_expression_flattening(instructions, automatic_inlining_predicate);
-
- v.run(instructions);
-
- return v.progress;
-}
-
-static void
-replace_return_with_assignment(ir_instruction *ir, void *data)
-{
- void *ctx = talloc_parent(ir);
- ir_variable *retval = (ir_variable *)data;
- ir_return *ret = ir->as_return();
-
- if (ret) {
- if (ret->value) {
- ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
- ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
- } else {
- /* un-valued return has to be the last return, or we shouldn't
- * have reached here. (see can_inline()).
- */
- assert(ret->next->is_tail_sentinel());
- ret->remove();
- }
- }
-}
-
-ir_rvalue *
-ir_call::generate_inline(ir_instruction *next_ir)
-{
- void *ctx = talloc_parent(this);
- ir_variable **parameters;
- int num_parameters;
- int i;
- ir_variable *retval = NULL;
- struct hash_table *ht;
-
- ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
-
- num_parameters = 0;
- foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
- num_parameters++;
-
- parameters = new ir_variable *[num_parameters];
-
- /* Generate storage for the return value. */
- if (this->callee->return_type) {
- retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
- ir_var_auto);
- next_ir->insert_before(retval);
- }
-
- /* Generate the declarations for the parameters to our inlined code,
- * and set up the mapping of real function body variables to ours.
- */
- i = 0;
- exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
- exec_list_iterator param_iter = this->actual_parameters.iterator();
- for (i = 0; i < num_parameters; i++) {
- ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
- ir_rvalue *param = (ir_rvalue *) param_iter.get();
-
- /* Generate a new variable for the parameter. */
- if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
- /* For samplers, we want the inlined sampler references
- * referencing the passed in sampler variable, since that
- * will have the location information, which an assignment of
- * a sampler wouldn't. Fix it up below.
- */
- parameters[i] = NULL;
- } else {
- parameters[i] = sig_param->clone(ctx, ht);
- parameters[i]->mode = ir_var_auto;
-
- /* Remove the read-only decoration becuase we're going to write
- * directly to this variable. If the cloned variable is left
- * read-only and the inlined function is inside a loop, the loop
- * analysis code will get confused.
- */
- parameters[i]->read_only = false;
- next_ir->insert_before(parameters[i]);
- }
-
- /* Move the actual param into our param variable if it's an 'in' type. */
- if (parameters[i] && (sig_param->mode == ir_var_in ||
- sig_param->mode == ir_var_inout)) {
- ir_assignment *assign;
-
- assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
- param, NULL);
- next_ir->insert_before(assign);
- }
-
- sig_param_iter.next();
- param_iter.next();
- }
-
- exec_list new_instructions;
-
- /* Generate the inlined body of the function to a new list */
- foreach_iter(exec_list_iterator, iter, callee->body) {
- ir_instruction *ir = (ir_instruction *)iter.get();
- ir_instruction *new_ir = ir->clone(ctx, ht);
-
- new_instructions.push_tail(new_ir);
- visit_tree(new_ir, replace_return_with_assignment, retval);
- }
-
- /* If any samplers were passed in, replace any deref of the sampler
- * with a deref of the sampler argument.
- */
- param_iter = this->actual_parameters.iterator();
- sig_param_iter = this->callee->parameters.iterator();
- for (i = 0; i < num_parameters; i++) {
- ir_instruction *const param = (ir_instruction *) param_iter.get();
- ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
-
- if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
- ir_dereference *deref = param->as_dereference();
-
- assert(deref);
- do_sampler_replacement(&new_instructions, sig_param, deref);
- }
- param_iter.next();
- sig_param_iter.next();
- }
-
- /* Now push those new instructions in. */
- foreach_iter(exec_list_iterator, iter, new_instructions) {
- ir_instruction *ir = (ir_instruction *)iter.get();
- next_ir->insert_before(ir);
- }
-
- /* Copy back the value of any 'out' parameters from the function body
- * variables to our own.
- */
- i = 0;
- param_iter = this->actual_parameters.iterator();
- sig_param_iter = this->callee->parameters.iterator();
- for (i = 0; i < num_parameters; i++) {
- ir_instruction *const param = (ir_instruction *) param_iter.get();
- const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
-
- /* Move our param variable into the actual param if it's an 'out' type. */
- if (parameters[i] && (sig_param->mode == ir_var_out ||
- sig_param->mode == ir_var_inout)) {
- ir_assignment *assign;
-
- assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
- new(ctx) ir_dereference_variable(parameters[i]),
- NULL);
- next_ir->insert_before(assign);
- }
-
- param_iter.next();
- sig_param_iter.next();
- }
-
- delete [] parameters;
-
- hash_table_dtor(ht);
-
- if (retval)
- return new(ctx) ir_dereference_variable(retval);
- else
- return NULL;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_expression *ir)
-{
- (void) ir;
- return visit_continue_with_parent;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_return *ir)
-{
- (void) ir;
- return visit_continue_with_parent;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_texture *ir)
-{
- (void) ir;
- return visit_continue_with_parent;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
-{
- (void) ir;
- return visit_continue_with_parent;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_call *ir)
-{
- if (can_inline(ir)) {
- /* If the call was part of some tree, then it should have been
- * flattened out or we shouldn't have seen it because of a
- * visit_continue_with_parent in this visitor.
- */
- assert(ir == base_ir);
-
- (void) ir->generate_inline(ir);
- ir->remove();
- this->progress = true;
- }
-
- return visit_continue;
-}
-
-
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
-{
- ir_call *call = ir->rhs->as_call();
- if (!call || !can_inline(call))
- return visit_continue;
-
- /* generates the parameter setup, function body, and returns the return
- * value of the function
- */
- ir_rvalue *rhs = call->generate_inline(ir);
- assert(rhs);
-
- ir->rhs = rhs;
- this->progress = true;
-
- return visit_continue;
-}
-
-/**
- * Replaces references to the "sampler" variable with a clone of "deref."
- *
- * From the spec, samplers can appear in the tree as function
- * (non-out) parameters and as the result of array indexing and
- * structure field selection. In our builtin implementation, they
- * also appear in the sampler field of an ir_tex instruction.
- */
-
-class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
-public:
- ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
- {
- this->sampler = sampler;
- this->deref = deref;
- }
-
- virtual ~ir_sampler_replacement_visitor()
- {
- }
-
- virtual ir_visitor_status visit_leave(ir_call *);
- virtual ir_visitor_status visit_leave(ir_dereference_array *);
- virtual ir_visitor_status visit_leave(ir_dereference_record *);
- virtual ir_visitor_status visit_leave(ir_texture *);
-
- void replace_deref(ir_dereference **deref);
- void replace_rvalue(ir_rvalue **rvalue);
-
- ir_variable *sampler;
- ir_dereference *deref;
-};
-
-void
-ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
-{
- ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
- if (deref_var && deref_var->var == this->sampler) {
- *deref = this->deref->clone(talloc_parent(*deref), NULL);
- }
-}
-
-void
-ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
-{
- if (!*rvalue)
- return;
-
- ir_dereference *deref = (*rvalue)->as_dereference();
-
- if (!deref)
- return;
-
- replace_deref(&deref);
- *rvalue = deref;
-}
-
-ir_visitor_status
-ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
-{
- replace_deref(&ir->sampler);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
-{
- replace_rvalue(&ir->array);
- return visit_continue;
-}
-
-ir_visitor_status
-ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
-{
- replace_rvalue(&ir->record);
- return visit_continue;
-}
-
-ir_visitor_status
-ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
-{
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_rvalue *param = (ir_rvalue *)iter.get();
- ir_rvalue *new_param = param;
- replace_rvalue(&new_param);
-
- if (new_param != param) {
- param->replace_with(new_param);
- }
- }
- return visit_continue;
-}
-
-static void
-do_sampler_replacement(exec_list *instructions,
- ir_variable *sampler,
- ir_dereference *deref)
-{
- ir_sampler_replacement_visitor v(sampler, deref);
-
- visit_list_elements(&v, instructions);
-}
+++ /dev/null
-/*
- * 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 ir_if_simplification.cpp
- *
- * Moves constant branches of if statements out to the surrounding
- * instruction stream.
- */
-
-#include "ir.h"
-
-class ir_if_simplification_visitor : public ir_hierarchical_visitor {
-public:
- ir_if_simplification_visitor()
- {
- this->made_progress = false;
- }
-
- ir_visitor_status visit_leave(ir_if *);
-
- bool made_progress;
-};
-
-bool
-do_if_simplification(exec_list *instructions)
-{
- ir_if_simplification_visitor v;
-
- v.run(instructions);
- return v.made_progress;
-}
-
-
-ir_visitor_status
-ir_if_simplification_visitor::visit_leave(ir_if *ir)
-{
- /* FINISHME: Ideally there would be a way to note that the condition results
- * FINISHME: in a constant before processing both of the other subtrees.
- * FINISHME: This can probably be done with some flags, but it would take
- * FINISHME: some work to get right.
- */
- ir_constant *condition_constant = ir->condition->constant_expression_value();
- if (condition_constant) {
- /* Move the contents of the one branch of the conditional
- * that matters out.
- */
- if (condition_constant->value.b[0]) {
- foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) {
- ir_instruction *then_ir = (ir_instruction *)then_iter.get();
- ir->insert_before(then_ir);
- }
- } else {
- foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) {
- ir_instruction *else_ir = (ir_instruction *)else_iter.get();
- ir->insert_before(else_ir);
- }
- }
- ir->remove();
- this->made_progress = true;
- }
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_if_to_cond_assign.cpp
- *
- * This attempts to flatten all if statements to conditional
- * assignments for GPUs that don't do control flow.
- *
- * It can't handle other control flow being inside of its block, such
- * as calls or loops. Hopefully loop unrolling and inlining will take
- * care of those.
- */
-
-#include "glsl_types.h"
-#include "ir.h"
-
-class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor {
-public:
- ir_if_to_cond_assign_visitor()
- {
- this->progress = false;
- }
-
- ir_visitor_status visit_leave(ir_if *);
-
- bool progress;
-};
-
-bool
-do_if_to_cond_assign(exec_list *instructions)
-{
- ir_if_to_cond_assign_visitor v;
-
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
-
-void
-check_control_flow(ir_instruction *ir, void *data)
-{
- bool *found_control_flow = (bool *)data;
- switch (ir->ir_type) {
- case ir_type_call:
- case ir_type_discard:
- case ir_type_loop:
- case ir_type_loop_jump:
- case ir_type_return:
- *found_control_flow = true;
- break;
- default:
- break;
- }
-}
-
-void
-move_block_to_cond_assign(void *mem_ctx,
- ir_if *if_ir, ir_variable *cond_var, bool then)
-{
- exec_list *instructions;
-
- if (then) {
- instructions = &if_ir->then_instructions;
- } else {
- instructions = &if_ir->else_instructions;
- }
-
- foreach_iter(exec_list_iterator, iter, *instructions) {
- ir_instruction *ir = (ir_instruction *)iter.get();
-
- if (ir->ir_type == ir_type_assignment) {
- ir_assignment *assign = (ir_assignment *)ir;
- ir_rvalue *cond_expr;
- ir_dereference *deref = new(mem_ctx) ir_dereference_variable(cond_var);
-
- if (then) {
- cond_expr = deref;
- } else {
- cond_expr = new(mem_ctx) ir_expression(ir_unop_logic_not,
- glsl_type::bool_type,
- deref,
- NULL);
- }
-
- if (!assign->condition) {
- assign->condition = cond_expr;
- } else {
- assign->condition = new(mem_ctx) ir_expression(ir_binop_logic_and,
- glsl_type::bool_type,
- cond_expr,
- assign->condition);
- }
- }
-
- /* Now, move from the if block to the block surrounding it. */
- ir->remove();
- if_ir->insert_before(ir);
- }
-}
-
-ir_visitor_status
-ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir)
-{
- bool found_control_flow = false;
- ir_variable *cond_var;
- ir_assignment *assign;
- ir_dereference_variable *deref;
-
- /* Check that both blocks don't contain anything we can't support. */
- foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) {
- ir_instruction *then_ir = (ir_instruction *)then_iter.get();
- visit_tree(then_ir, check_control_flow, &found_control_flow);
- }
- foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) {
- ir_instruction *else_ir = (ir_instruction *)else_iter.get();
- visit_tree(else_ir, check_control_flow, &found_control_flow);
- }
- if (found_control_flow)
- return visit_continue;
-
- void *mem_ctx = talloc_parent(ir);
-
- /* Store the condition to a variable so the assignment conditions are
- * simpler.
- */
- cond_var = new(mem_ctx) ir_variable(glsl_type::bool_type,
- "if_to_cond_assign_condition",
- ir_var_temporary);
- ir->insert_before(cond_var);
-
- deref = new(mem_ctx) ir_dereference_variable(cond_var);
- assign = new(mem_ctx) ir_assignment(deref,
- ir->condition, NULL);
- ir->insert_before(assign);
-
- /* Now, move all of the instructions out of the if blocks, putting
- * conditions on assignments.
- */
- move_block_to_cond_assign(mem_ctx, ir, cond_var, true);
- move_block_to_cond_assign(mem_ctx, ir, cond_var, false);
-
- ir->remove();
-
- this->progress = true;
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * Copyright © 2010 Luca Barbieri
- *
- * 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 ir_lower_jumps.cpp
- */
-
-#include "glsl_types.h"
-#include <string.h>
-#include "ir.h"
-
-enum jump_strength
-{
- strength_none,
- strength_always_clears_execute_flag,
- strength_continue,
- strength_break,
- strength_return,
- strength_discard
-};
-
-struct block_record
-{
- /* minimum jump strength (of lowered IR, not pre-lowering IR)
- *
- * If the block ends with a jump, must be the strength of the jump.
- * Otherwise, the jump would be dead and have been deleted before)
- *
- * If the block doesn't end with a jump, it can be different than strength_none if all paths before it lead to some jump
- * (e.g. an if with a return in one branch, and a break in the other, while not lowering them)
- * Note that identical jumps are usually unified though.
- */
- jump_strength min_strength;
-
- /* can anything clear the execute flag? */
- bool may_clear_execute_flag;
-
- block_record()
- {
- this->min_strength = strength_none;
- this->may_clear_execute_flag = false;
- }
-};
-
-struct loop_record
-{
- ir_function_signature* signature;
- ir_loop* loop;
-
- /* used to avoid lowering the break used to represent lowered breaks */
- unsigned nesting_depth;
- bool in_if_at_the_end_of_the_loop;
-
- bool may_set_return_flag;
-
- ir_variable* break_flag;
- ir_variable* execute_flag; /* cleared to emulate continue */
-
- loop_record(ir_function_signature* p_signature = 0, ir_loop* p_loop = 0)
- {
- this->signature = p_signature;
- this->loop = p_loop;
- this->nesting_depth = 0;
- this->in_if_at_the_end_of_the_loop = false;
- this->may_set_return_flag = false;
- this->break_flag = 0;
- this->execute_flag = 0;
- }
-
- ir_variable* get_execute_flag()
- {
- /* also supported for the "function loop" */
- if(!this->execute_flag) {
- exec_list& list = this->loop ? this->loop->body_instructions : signature->body;
- this->execute_flag = new(this->signature) ir_variable(glsl_type::bool_type, "execute_flag", ir_var_temporary);
- list.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(execute_flag), new(this->signature) ir_constant(true), 0));
- list.push_head(this->execute_flag);
- }
- return this->execute_flag;
- }
-
- ir_variable* get_break_flag()
- {
- assert(this->loop);
- if(!this->break_flag) {
- this->break_flag = new(this->signature) ir_variable(glsl_type::bool_type, "break_flag", ir_var_temporary);
- this->loop->insert_before(this->break_flag);
- this->loop->insert_before(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(break_flag), new(this->signature) ir_constant(false), 0));
- }
- return this->break_flag;
- }
-};
-
-struct function_record
-{
- ir_function_signature* signature;
- ir_variable* return_flag; /* used to break out of all loops and then jump to the return instruction */
- ir_variable* return_value;
- bool is_main;
- unsigned nesting_depth;
-
- function_record(ir_function_signature* p_signature = 0)
- {
- this->signature = p_signature;
- this->return_flag = 0;
- this->return_value = 0;
- this->nesting_depth = 0;
- this->is_main = this->signature && (strcmp(this->signature->function_name(), "main") == 0);
- }
-
- ir_variable* get_return_flag()
- {
- if(!this->return_flag) {
- this->return_flag = new(this->signature) ir_variable(glsl_type::bool_type, "return_flag", ir_var_temporary);
- this->signature->body.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(return_flag), new(this->signature) ir_constant(false), 0));
- this->signature->body.push_head(this->return_flag);
- }
- return this->return_flag;
- }
-
- ir_variable* get_return_value()
- {
- if(!this->return_value) {
- assert(!this->signature->return_type->is_void());
- return_value = new(this->signature) ir_variable(this->signature->return_type, "return_value", ir_var_temporary);
- this->signature->body.push_head(this->return_value);
- }
- return this->return_value;
- }
-};
-
-struct ir_lower_jumps_visitor : public ir_control_flow_visitor {
- bool progress;
-
- struct function_record function;
- struct loop_record loop;
- struct block_record block;
-
- bool pull_out_jumps;
- bool lower_continue;
- bool lower_break;
- bool lower_sub_return;
- bool lower_main_return;
-
- ir_lower_jumps_visitor()
- {
- this->progress = false;
- }
-
- void truncate_after_instruction(exec_node *ir)
- {
- if (!ir)
- return;
-
- while (!ir->get_next()->is_tail_sentinel()) {
- ((ir_instruction *)ir->get_next())->remove();
- this->progress = true;
- }
- }
-
- void move_outer_block_inside(ir_instruction *ir, exec_list *inner_block)
- {
- while (!ir->get_next()->is_tail_sentinel()) {
- ir_instruction *move_ir = (ir_instruction *)ir->get_next();
-
- move_ir->remove();
- inner_block->push_tail(move_ir);
- }
- }
-
- virtual void visit(class ir_loop_jump * ir)
- {
- truncate_after_instruction(ir);
- this->block.min_strength = ir->is_break() ? strength_break : strength_continue;
- }
-
- virtual void visit(class ir_return * ir)
- {
- truncate_after_instruction(ir);
- this->block.min_strength = strength_return;
- }
-
- virtual void visit(class ir_discard * ir)
- {
- truncate_after_instruction(ir);
- this->block.min_strength = strength_discard;
- }
-
- enum jump_strength get_jump_strength(ir_instruction* ir)
- {
- if(!ir)
- return strength_none;
- else if(ir->ir_type == ir_type_loop_jump) {
- if(((ir_loop_jump*)ir)->is_break())
- return strength_break;
- else
- return strength_continue;
- } else if(ir->ir_type == ir_type_return)
- return strength_return;
- else if(ir->ir_type == ir_type_discard)
- return strength_discard;
- else
- return strength_none;
- }
-
- bool should_lower_jump(ir_jump* ir)
- {
- unsigned strength = get_jump_strength(ir);
- bool lower;
- switch(strength)
- {
- case strength_none:
- lower = false; /* don't change this, code relies on it */
- break;
- case strength_continue:
- lower = lower_continue;
- break;
- case strength_break:
- assert(this->loop.loop);
- /* never lower "canonical break" */
- if(ir->get_next()->is_tail_sentinel() && (this->loop.nesting_depth == 0
- || (this->loop.nesting_depth == 1 && this->loop.in_if_at_the_end_of_the_loop)))
- lower = false;
- else
- lower = lower_break;
- break;
- case strength_return:
- /* never lower return at the end of a this->function */
- if(this->function.nesting_depth == 0 && ir->get_next()->is_tail_sentinel())
- lower = false;
- else if (this->function.is_main)
- lower = lower_main_return;
- else
- lower = lower_sub_return;
- break;
- case strength_discard:
- lower = false; /* probably nothing needs this lowered */
- break;
- }
- return lower;
- }
-
- block_record visit_block(exec_list* list)
- {
- block_record saved_block = this->block;
- this->block = block_record();
- visit_exec_list(list, this);
- block_record ret = this->block;
- this->block = saved_block;
- return ret;
- }
-
- virtual void visit(ir_if *ir)
- {
- if(this->loop.nesting_depth == 0 && ir->get_next()->is_tail_sentinel())
- this->loop.in_if_at_the_end_of_the_loop = true;
-
- ++this->function.nesting_depth;
- ++this->loop.nesting_depth;
-
- block_record block_records[2];
- ir_jump* jumps[2];
-
- block_records[0] = visit_block(&ir->then_instructions);
- block_records[1] = visit_block(&ir->else_instructions);
-
-retry: /* we get here if we put code after the if inside a branch */
- for(unsigned i = 0; i < 2; ++i) {
- exec_list& list = i ? ir->else_instructions : ir->then_instructions;
- jumps[i] = 0;
- if(!list.is_empty() && get_jump_strength((ir_instruction*)list.get_tail()))
- jumps[i] = (ir_jump*)list.get_tail();
- }
-
- for(;;) {
- jump_strength jump_strengths[2];
-
- for(unsigned i = 0; i < 2; ++i) {
- if(jumps[i]) {
- jump_strengths[i] = block_records[i].min_strength;
- assert(jump_strengths[i] == get_jump_strength(jumps[i]));
- } else
- jump_strengths[i] = strength_none;
- }
-
- /* move both jumps out if possible */
- if(pull_out_jumps && jump_strengths[0] == jump_strengths[1]) {
- bool unify = true;
- if(jump_strengths[0] == strength_continue)
- ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_continue));
- else if(jump_strengths[0] == strength_break)
- ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_break));
- /* FINISHME: unify returns with identical expressions */
- else if(jump_strengths[0] == strength_return && this->function.signature->return_type->is_void())
- ir->insert_after(new(ir) ir_return(NULL));
- /* FINISHME: unify discards */
- else
- unify = false;
-
- if(unify) {
- jumps[0]->remove();
- jumps[1]->remove();
- this->progress = true;
-
- jumps[0] = 0;
- jumps[1] = 0;
- block_records[0].min_strength = strength_none;
- block_records[1].min_strength = strength_none;
- break;
- }
- }
-
- /* lower a jump: if both need to lowered, start with the strongest one, so that
- * we might later unify the lowered version with the other one
- */
- bool should_lower[2];
- for(unsigned i = 0; i < 2; ++i)
- should_lower[i] = should_lower_jump(jumps[i]);
-
- int lower;
- if(should_lower[1] && should_lower[0])
- lower = jump_strengths[1] > jump_strengths[0];
- else if(should_lower[0])
- lower = 0;
- else if(should_lower[1])
- lower = 1;
- else
- break;
-
- if(jump_strengths[lower] == strength_return) {
- ir_variable* return_flag = this->function.get_return_flag();
- if(!this->function.signature->return_type->is_void()) {
- ir_variable* return_value = this->function.get_return_value();
- jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_value), ((ir_return*)jumps[lower])->value, NULL));
- }
- jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_flag), new (ir) ir_constant(true), NULL));
- this->loop.may_set_return_flag = true;
- if(this->loop.loop) {
- ir_loop_jump* lowered = 0;
- lowered = new(ir) ir_loop_jump(ir_loop_jump::jump_break);
- block_records[lower].min_strength = strength_break;
- jumps[lower]->replace_with(lowered);
- jumps[lower] = lowered;
- } else
- goto lower_continue;
- this->progress = true;
- } else if(jump_strengths[lower] == strength_break) {
- /* We can't lower to an actual continue because that would execute the increment.
- *
- * In the lowered code, we instead put the break check between the this->loop body and the increment,
- * which is impossible with a real continue as defined by the GLSL IR currently.
- *
- * Smarter options (such as undoing the increment) are possible but it's not worth implementing them,
- * because if break is lowered, continue is almost surely lowered too.
- */
- jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(this->loop.get_break_flag()), new (ir) ir_constant(true), 0));
- goto lower_continue;
- } else if(jump_strengths[lower] == strength_continue) {
-lower_continue:
- ir_variable* execute_flag = this->loop.get_execute_flag();
- jumps[lower]->replace_with(new(ir) ir_assignment(new (ir) ir_dereference_variable(execute_flag), new (ir) ir_constant(false), 0));
- jumps[lower] = 0;
- block_records[lower].min_strength = strength_always_clears_execute_flag;
- block_records[lower].may_clear_execute_flag = true;
- this->progress = true;
- break;
- }
- }
-
- /* move out a jump out if possible */
- if(pull_out_jumps) {
- int move_out = -1;
- if(jumps[0] && block_records[1].min_strength >= strength_continue)
- move_out = 0;
- else if(jumps[1] && block_records[0].min_strength >= strength_continue)
- move_out = 1;
-
- if(move_out >= 0)
- {
- jumps[move_out]->remove();
- ir->insert_after(jumps[move_out]);
- jumps[move_out] = 0;
- block_records[move_out].min_strength = strength_none;
- this->progress = true;
- }
- }
-
- if(block_records[0].min_strength < block_records[1].min_strength)
- this->block.min_strength = block_records[0].min_strength;
- else
- this->block.min_strength = block_records[1].min_strength;
- this->block.may_clear_execute_flag = this->block.may_clear_execute_flag || block_records[0].may_clear_execute_flag || block_records[1].may_clear_execute_flag;
-
- if(this->block.min_strength)
- truncate_after_instruction(ir);
- else if(this->block.may_clear_execute_flag)
- {
- int move_into = -1;
- if(block_records[0].min_strength && !block_records[1].may_clear_execute_flag)
- move_into = 1;
- else if(block_records[1].min_strength && !block_records[0].may_clear_execute_flag)
- move_into = 0;
-
- if(move_into >= 0) {
- assert(!block_records[move_into].min_strength && !block_records[move_into].may_clear_execute_flag); /* otherwise, we just truncated */
-
- exec_list* list = move_into ? &ir->else_instructions : &ir->then_instructions;
- exec_node* next = ir->get_next();
- if(!next->is_tail_sentinel()) {
- move_outer_block_inside(ir, list);
-
- exec_list list;
- list.head = next;
- block_records[move_into] = visit_block(&list);
-
- this->progress = true;
- goto retry;
- }
- } else {
- ir_instruction* ir_after;
- for(ir_after = (ir_instruction*)ir->get_next(); !ir_after->is_tail_sentinel();)
- {
- ir_if* ir_if = ir_after->as_if();
- if(ir_if && ir_if->else_instructions.is_empty()) {
- ir_dereference_variable* ir_if_cond_deref = ir_if->condition->as_dereference_variable();
- if(ir_if_cond_deref && ir_if_cond_deref->var == this->loop.execute_flag) {
- ir_instruction* ir_next = (ir_instruction*)ir_after->get_next();
- ir_after->insert_before(&ir_if->then_instructions);
- ir_after->remove();
- ir_after = ir_next;
- continue;
- }
- }
- ir_after = (ir_instruction*)ir_after->get_next();
-
- /* only set this if we find any unprotected instruction */
- this->progress = true;
- }
-
- if(!ir->get_next()->is_tail_sentinel()) {
- assert(this->loop.execute_flag);
- ir_if* if_execute = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.execute_flag));
- move_outer_block_inside(ir, &if_execute->then_instructions);
- ir->insert_after(if_execute);
- }
- }
- }
- --this->loop.nesting_depth;
- --this->function.nesting_depth;
- }
-
- virtual void visit(ir_loop *ir)
- {
- ++this->function.nesting_depth;
- loop_record saved_loop = this->loop;
- this->loop = loop_record(this->function.signature, ir);
-
- block_record body = visit_block(&ir->body_instructions);
-
- if(body.min_strength >= strength_break) {
- /* FINISHME: turn the this->loop into an if, or replace it with its body */
- }
-
- if(this->loop.break_flag) {
- ir_if* break_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.break_flag));
- break_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break));
- ir->body_instructions.push_tail(break_if);
- }
-
- if(this->loop.may_set_return_flag) {
- assert(this->function.return_flag);
- ir_if* return_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->function.return_flag));
- return_if->then_instructions.push_tail(new(ir) ir_loop_jump(saved_loop.loop ? ir_loop_jump::jump_break : ir_loop_jump::jump_continue));
- ir->insert_after(return_if);
- }
-
- this->loop = saved_loop;
- --this->function.nesting_depth;
- }
-
- virtual void visit(ir_function_signature *ir)
- {
- /* these are not strictly necessary */
- assert(!this->function.signature);
- assert(!this->loop.loop);
-
- function_record saved_function = this->function;
- loop_record saved_loop = this->loop;
- this->function = function_record(ir);
- this->loop = loop_record(ir);
-
- assert(!this->loop.loop);
- visit_block(&ir->body);
-
- if(this->function.return_value)
- ir->body.push_tail(new(ir) ir_return(new (ir) ir_dereference_variable(this->function.return_value)));
-
- this->loop = saved_loop;
- this->function = saved_function;
- }
-
- virtual void visit(class ir_function * ir)
- {
- visit_block(&ir->signatures);
- }
-};
-
-bool
-do_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_return, bool lower_main_return, bool lower_continue, bool lower_break)
-{
- ir_lower_jumps_visitor v;
- v.pull_out_jumps = pull_out_jumps;
- v.lower_continue = lower_continue;
- v.lower_break = lower_break;
- v.lower_sub_return = lower_sub_return;
- v.lower_main_return = lower_main_return;
-
- do {
- v.progress = false;
- visit_exec_list(instructions, &v);
- } while (v.progress);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_mat_op_to_vec.cpp
- *
- * Breaks matrix operation expressions down to a series of vector operations.
- *
- * Generally this is how we have to codegen matrix operations for a
- * GPU, so this gives us the chance to constant fold operations on a
- * column or row.
- */
-
-#include "ir.h"
-#include "ir_expression_flattening.h"
-#include "glsl_types.h"
-
-class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
-public:
- ir_mat_op_to_vec_visitor()
- {
- this->made_progress = false;
- this->mem_ctx = NULL;
- }
-
- ir_visitor_status visit_leave(ir_assignment *);
-
- ir_dereference *get_column(ir_variable *var, int col);
- ir_rvalue *get_element(ir_variable *var, int col, int row);
-
- void do_mul_mat_mat(ir_variable *result_var,
- ir_variable *a_var, ir_variable *b_var);
- void do_mul_mat_vec(ir_variable *result_var,
- ir_variable *a_var, ir_variable *b_var);
- void do_mul_vec_mat(ir_variable *result_var,
- ir_variable *a_var, ir_variable *b_var);
- void do_mul_mat_scalar(ir_variable *result_var,
- ir_variable *a_var, ir_variable *b_var);
- void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var,
- ir_variable *b_var, bool test_equal);
-
- void *mem_ctx;
- bool made_progress;
-};
-
-static bool
-mat_op_to_vec_predicate(ir_instruction *ir)
-{
- ir_expression *expr = ir->as_expression();
- unsigned int i;
-
- if (!expr)
- return false;
-
- for (i = 0; i < expr->get_num_operands(); i++) {
- if (expr->operands[i]->type->is_matrix())
- return true;
- }
-
- return false;
-}
-
-bool
-do_mat_op_to_vec(exec_list *instructions)
-{
- ir_mat_op_to_vec_visitor v;
-
- /* Pull out any matrix expression to a separate assignment to a
- * temp. This will make our handling of the breakdown to
- * operations on the matrix's vector components much easier.
- */
- do_expression_flattening(instructions, mat_op_to_vec_predicate);
-
- visit_list_elements(&v, instructions);
-
- return v.made_progress;
-}
-
-ir_rvalue *
-ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
-{
- ir_dereference *deref;
-
- deref = new(mem_ctx) ir_dereference_variable(var);
-
- if (var->type->is_matrix()) {
- deref = new(mem_ctx) ir_dereference_array(var,
- new(mem_ctx) ir_constant(col));
- } else {
- assert(col == 0);
- }
-
- return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1);
-}
-
-ir_dereference *
-ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
-{
- ir_dereference *deref;
-
- if (!var->type->is_matrix()) {
- deref = new(mem_ctx) ir_dereference_variable(var);
- } else {
- deref = new(mem_ctx) ir_dereference_variable(var);
- deref = new(mem_ctx) ir_dereference_array(deref,
- new(mem_ctx) ir_constant(row));
- }
-
- return deref;
-}
-
-void
-ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
- ir_variable *a_var,
- ir_variable *b_var)
-{
- int b_col, i;
- ir_assignment *assign;
- ir_expression *expr;
-
- for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
- ir_rvalue *a = get_column(a_var, 0);
- ir_rvalue *b = get_element(b_var, b_col, 0);
-
- /* first column */
- expr = new(mem_ctx) ir_expression(ir_binop_mul,
- a->type,
- a,
- b);
-
- /* following columns */
- for (i = 1; i < a_var->type->matrix_columns; i++) {
- ir_expression *mul_expr;
-
- a = get_column(a_var, i);
- b = get_element(b_var, b_col, i);
-
- mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
- a->type,
- a,
- b);
- expr = new(mem_ctx) ir_expression(ir_binop_add,
- a->type,
- expr,
- mul_expr);
- }
-
- ir_rvalue *result = get_column(result_var, b_col);
- assign = new(mem_ctx) ir_assignment(result,
- expr,
- NULL);
- base_ir->insert_before(assign);
- }
-}
-
-void
-ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
- ir_variable *a_var,
- ir_variable *b_var)
-{
- int i;
- ir_rvalue *a = get_column(a_var, 0);
- ir_rvalue *b = get_element(b_var, 0, 0);
- ir_assignment *assign;
- ir_expression *expr;
-
- /* first column */
- expr = new(mem_ctx) ir_expression(ir_binop_mul,
- result_var->type,
- a,
- b);
-
- /* following columns */
- for (i = 1; i < a_var->type->matrix_columns; i++) {
- ir_expression *mul_expr;
-
- a = get_column(a_var, i);
- b = get_element(b_var, 0, i);
-
- mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
- result_var->type,
- a,
- b);
- expr = new(mem_ctx) ir_expression(ir_binop_add,
- result_var->type,
- expr,
- mul_expr);
- }
-
- ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var);
- assign = new(mem_ctx) ir_assignment(result,
- expr,
- NULL);
- base_ir->insert_before(assign);
-}
-
-void
-ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
- ir_variable *a_var,
- ir_variable *b_var)
-{
- int i;
-
- for (i = 0; i < b_var->type->matrix_columns; i++) {
- ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var);
- ir_rvalue *b = get_column(b_var, i);
- ir_rvalue *result;
- ir_expression *column_expr;
- ir_assignment *column_assign;
-
- result = new(mem_ctx) ir_dereference_variable(result_var);
- result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1);
-
- column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
- result->type,
- a,
- b);
-
- column_assign = new(mem_ctx) ir_assignment(result,
- column_expr,
- NULL);
- base_ir->insert_before(column_assign);
- }
-}
-
-void
-ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
- ir_variable *a_var,
- ir_variable *b_var)
-{
- int i;
-
- for (i = 0; i < a_var->type->matrix_columns; i++) {
- ir_rvalue *a = get_column(a_var, i);
- ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var);
- ir_rvalue *result = get_column(result_var, i);
- ir_expression *column_expr;
- ir_assignment *column_assign;
-
- column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
- result->type,
- a,
- b);
-
- column_assign = new(mem_ctx) ir_assignment(result,
- column_expr,
- NULL);
- base_ir->insert_before(column_assign);
- }
-}
-
-void
-ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var,
- ir_variable *a_var,
- ir_variable *b_var,
- bool test_equal)
-{
- /* This essentially implements the following GLSL:
- *
- * bool equal(mat4 a, mat4 b)
- * {
- * return !any(bvec4(a[0] != b[0],
- * a[1] != b[1],
- * a[2] != b[2],
- * a[3] != b[3]);
- * }
- *
- * bool nequal(mat4 a, mat4 b)
- * {
- * return any(bvec4(a[0] != b[0],
- * a[1] != b[1],
- * a[2] != b[2],
- * a[3] != b[3]);
- * }
- */
- const unsigned columns = a_var->type->matrix_columns;
- const glsl_type *const bvec_type =
- glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
-
- ir_variable *const tmp_bvec =
- new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
- ir_var_temporary);
- this->base_ir->insert_before(tmp_bvec);
-
- for (unsigned i = 0; i < columns; i++) {
- ir_dereference *const op0 = get_column(a_var, i);
- ir_dereference *const op1 = get_column(b_var, i);
-
- ir_expression *const cmp =
- new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
- glsl_type::bool_type, op0, op1);
-
- ir_dereference *const lhs =
- new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
-
- ir_assignment *const assign =
- new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
-
- this->base_ir->insert_before(assign);
- }
-
- ir_rvalue *const val =
- new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
-
- ir_expression *any =
- new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type,
- val, NULL);
-
- if (test_equal)
- any = new(this->mem_ctx) ir_expression(ir_unop_logic_not,
- glsl_type::bool_type,
- any, NULL);
-
- ir_rvalue *const result =
- new(this->mem_ctx) ir_dereference_variable(result_var);
-
- ir_assignment *const assign =
- new(mem_ctx) ir_assignment(result, any, NULL);
- base_ir->insert_before(assign);
-}
-
-static bool
-has_matrix_operand(const ir_expression *expr, unsigned &columns)
-{
- for (unsigned i = 0; i < expr->get_num_operands(); i++) {
- if (expr->operands[i]->type->is_matrix()) {
- columns = expr->operands[i]->type->matrix_columns;
- return true;
- }
- }
-
- return false;
-}
-
-
-ir_visitor_status
-ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
-{
- ir_expression *orig_expr = orig_assign->rhs->as_expression();
- unsigned int i, matrix_columns = 1;
- ir_variable *op_var[2];
-
- if (!orig_expr)
- return visit_continue;
-
- if (!has_matrix_operand(orig_expr, matrix_columns))
- return visit_continue;
-
- mem_ctx = talloc_parent(orig_assign);
-
- ir_dereference_variable *lhs_deref =
- orig_assign->lhs->as_dereference_variable();
- assert(lhs_deref);
-
- ir_variable *result_var = lhs_deref->var;
-
- /* Store the expression operands in temps so we can use them
- * multiple times.
- */
- for (i = 0; i < orig_expr->get_num_operands(); i++) {
- ir_assignment *assign;
-
- op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
- "mat_op_to_vec",
- ir_var_temporary);
- base_ir->insert_before(op_var[i]);
-
- lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
- assign = new(mem_ctx) ir_assignment(lhs_deref,
- orig_expr->operands[i],
- NULL);
- base_ir->insert_before(assign);
- }
-
- /* OK, time to break down this matrix operation. */
- switch (orig_expr->operation) {
- case ir_unop_neg: {
- const unsigned mask = (1U << result_var->type->vector_elements) - 1;
-
- /* Apply the operation to each column.*/
- for (i = 0; i < matrix_columns; i++) {
- ir_rvalue *op0 = get_column(op_var[0], i);
- ir_dereference *result = get_column(result_var, i);
- ir_expression *column_expr;
- ir_assignment *column_assign;
-
- column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
- result->type,
- op0,
- NULL);
-
- column_assign = new(mem_ctx) ir_assignment(result,
- column_expr,
- NULL,
- mask);
- assert(column_assign->write_mask != 0);
- base_ir->insert_before(column_assign);
- }
- break;
- }
- case ir_binop_add:
- case ir_binop_sub:
- case ir_binop_div:
- case ir_binop_mod: {
- const unsigned mask = (1U << result_var->type->vector_elements) - 1;
-
- /* For most operations, the matrix version is just going
- * column-wise through and applying the operation to each column
- * if available.
- */
- for (i = 0; i < matrix_columns; i++) {
- ir_rvalue *op0 = get_column(op_var[0], i);
- ir_rvalue *op1 = get_column(op_var[1], i);
- ir_dereference *result = get_column(result_var, i);
- ir_expression *column_expr;
- ir_assignment *column_assign;
-
- column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
- result->type,
- op0,
- op1);
-
- column_assign = new(mem_ctx) ir_assignment(result,
- column_expr,
- NULL,
- mask);
- assert(column_assign->write_mask != 0);
- base_ir->insert_before(column_assign);
- }
- break;
- }
- case ir_binop_mul:
- if (op_var[0]->type->is_matrix()) {
- if (op_var[1]->type->is_matrix()) {
- do_mul_mat_mat(result_var, op_var[0], op_var[1]);
- } else if (op_var[1]->type->is_vector()) {
- do_mul_mat_vec(result_var, op_var[0], op_var[1]);
- } else {
- assert(op_var[1]->type->is_scalar());
- do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
- }
- } else {
- assert(op_var[1]->type->is_matrix());
- if (op_var[0]->type->is_vector()) {
- do_mul_vec_mat(result_var, op_var[0], op_var[1]);
- } else {
- assert(op_var[0]->type->is_scalar());
- do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
- }
- }
- break;
-
- case ir_binop_all_equal:
- case ir_binop_any_nequal:
- do_equal_mat_mat(result_var, op_var[1], op_var[0],
- (orig_expr->operation == ir_binop_all_equal));
- break;
-
- default:
- printf("FINISHME: Handle matrix operation for %s\n",
- orig_expr->operator_string());
- abort();
- }
- orig_assign->remove();
- this->made_progress = true;
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_mod_to_fract.cpp
- *
- * Breaks an ir_unop_mod expression down to (op1 * fract(op0 / op1))
- *
- * Many GPUs don't have a MOD instruction (945 and 965 included), and
- * if we have to break it down like this anyway, it gives an
- * opportunity to do things like constant fold the (1.0 / op1) easily.
- */
-
-#include "ir.h"
-
-class ir_mod_to_fract_visitor : public ir_hierarchical_visitor {
-public:
- ir_mod_to_fract_visitor()
- {
- this->made_progress = false;
- }
-
- ir_visitor_status visit_leave(ir_expression *);
-
- bool made_progress;
-};
-
-bool
-do_mod_to_fract(exec_list *instructions)
-{
- ir_mod_to_fract_visitor v;
-
- visit_list_elements(&v, instructions);
- return v.made_progress;
-}
-
-ir_visitor_status
-ir_mod_to_fract_visitor::visit_leave(ir_expression *ir)
-{
- if (ir->operation != ir_binop_mod)
- return visit_continue;
-
- ir_variable *temp = new(ir) ir_variable(ir->operands[1]->type, "mod_b",
- ir_var_temporary);
- this->base_ir->insert_before(temp);
-
- ir_assignment *assign;
- ir_rvalue *expr;
-
- assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(temp),
- ir->operands[1], NULL);
- this->base_ir->insert_before(assign);
-
- expr = new(ir) ir_expression(ir_binop_div,
- ir->operands[0]->type,
- ir->operands[0],
- new(ir) ir_dereference_variable(temp));
-
- expr = new(ir) ir_expression(ir_unop_fract,
- ir->operands[0]->type,
- expr,
- NULL);
-
- ir->operation = ir_binop_mul;
- ir->operands[0] = new(ir) ir_dereference_variable(temp);
- ir->operands[1] = expr;
- this->made_progress = true;
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_noop_swizzle.cpp
- *
- * If a swizzle doesn't change the order or count of components, then
- * remove the swizzle so that other optimization passes see the value
- * behind it.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_rvalue_visitor.h"
-#include "ir_print_visitor.h"
-#include "glsl_types.h"
-
-class ir_noop_swizzle_visitor : public ir_rvalue_visitor {
-public:
- ir_noop_swizzle_visitor()
- {
- this->progress = false;
- }
-
- void handle_rvalue(ir_rvalue **rvalue);
- bool progress;
-};
-
-void
-ir_noop_swizzle_visitor::handle_rvalue(ir_rvalue **rvalue)
-{
- if (!*rvalue)
- return;
-
- ir_swizzle *swiz = (*rvalue)->as_swizzle();
- if (!swiz || swiz->type != swiz->val->type)
- return;
-
- int elems = swiz->val->type->vector_elements;
- if (swiz->mask.x != 0)
- return;
- if (elems >= 2 && swiz->mask.y != 1)
- return;
- if (elems >= 3 && swiz->mask.z != 2)
- return;
- if (elems >= 4 && swiz->mask.w != 3)
- return;
-
- this->progress = true;
- *rvalue = swiz->val;
-}
-
-bool
-do_noop_swizzle(exec_list *instructions)
-{
- ir_noop_swizzle_visitor v;
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_structure_splitting.cpp
- *
- * If a structure is only ever referenced by its components, then
- * split those components out to individual variables so they can be
- * handled normally by other optimization passes.
- *
- * This skips structures like uniforms, which need to be accessible as
- * structures for their access by the GL.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_print_visitor.h"
-#include "ir_rvalue_visitor.h"
-#include "glsl_types.h"
-
-static bool debug = false;
-
-// XXX using variable_entry2 here to avoid collision (MSVC multiply-defined
-// function) with the variable_entry class seen in ir_variable_refcount.h
-// Perhaps we can use the one in ir_variable_refcount.h and make this class
-// here go away?
-class variable_entry2 : public exec_node
-{
-public:
- variable_entry2(ir_variable *var)
- {
- this->var = var;
- this->whole_structure_access = 0;
- this->declaration = false;
- this->components = NULL;
- this->mem_ctx = NULL;
- }
-
- ir_variable *var; /* The key: the variable's pointer. */
-
- /** Number of times the variable is referenced, including assignments. */
- unsigned whole_structure_access;
-
- bool declaration; /* If the variable had a decl in the instruction stream */
-
- ir_variable **components;
-
- /** talloc_parent(this->var) -- the shader's talloc context. */
- void *mem_ctx;
-};
-
-
-class ir_structure_reference_visitor : public ir_hierarchical_visitor {
-public:
- ir_structure_reference_visitor(void)
- {
- this->mem_ctx = talloc_new(NULL);
- this->variable_list.make_empty();
- }
-
- ~ir_structure_reference_visitor(void)
- {
- talloc_free(mem_ctx);
- }
-
- virtual ir_visitor_status visit(ir_variable *);
- virtual ir_visitor_status visit(ir_dereference_variable *);
- virtual ir_visitor_status visit_enter(ir_dereference_record *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_function_signature *);
-
- variable_entry2 *get_variable_entry2(ir_variable *var);
-
- /* List of variable_entry */
- exec_list variable_list;
-
- void *mem_ctx;
-};
-
-variable_entry2 *
-ir_structure_reference_visitor::get_variable_entry2(ir_variable *var)
-{
- assert(var);
-
- if (!var->type->is_record() || var->mode == ir_var_uniform)
- return NULL;
-
- foreach_iter(exec_list_iterator, iter, this->variable_list) {
- variable_entry2 *entry = (variable_entry2 *)iter.get();
- if (entry->var == var)
- return entry;
- }
-
- variable_entry2 *entry = new(mem_ctx) variable_entry2(var);
- this->variable_list.push_tail(entry);
- return entry;
-}
-
-
-ir_visitor_status
-ir_structure_reference_visitor::visit(ir_variable *ir)
-{
- variable_entry2 *entry = this->get_variable_entry2(ir);
-
- if (entry)
- entry->declaration = true;
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_structure_reference_visitor::visit(ir_dereference_variable *ir)
-{
- ir_variable *const var = ir->variable_referenced();
- variable_entry2 *entry = this->get_variable_entry2(var);
-
- if (entry)
- entry->whole_structure_access++;
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir)
-{
- (void) ir;
- /* Don't descend into the ir_dereference_variable below. */
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_structure_reference_visitor::visit_enter(ir_assignment *ir)
-{
- if (ir->lhs->as_dereference_variable() &&
- ir->rhs->as_dereference_variable() &&
- !ir->condition) {
- /* We'll split copies of a structure to copies of components, so don't
- * descend to the ir_dereference_variables.
- */
- return visit_continue_with_parent;
- }
- return visit_continue;
-}
-
-ir_visitor_status
-ir_structure_reference_visitor::visit_enter(ir_function_signature *ir)
-{
- /* We don't want to descend into the function parameters and
- * dead-code eliminate them, so just accept the body here.
- */
- visit_list_elements(this, &ir->body);
- return visit_continue_with_parent;
-}
-
-class ir_structure_splitting_visitor : public ir_rvalue_visitor {
-public:
- ir_structure_splitting_visitor(exec_list *vars)
- {
- this->variable_list = vars;
- }
-
- virtual ~ir_structure_splitting_visitor()
- {
- }
-
- virtual ir_visitor_status visit_leave(ir_assignment *);
-
- void split_deref(ir_dereference **deref);
- void handle_rvalue(ir_rvalue **rvalue);
- variable_entry2 *get_splitting_entry(ir_variable *var);
-
- exec_list *variable_list;
- void *mem_ctx;
-};
-
-variable_entry2 *
-ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var)
-{
- assert(var);
-
- if (!var->type->is_record())
- return NULL;
-
- foreach_iter(exec_list_iterator, iter, *this->variable_list) {
- variable_entry2 *entry = (variable_entry2 *)iter.get();
- if (entry->var == var) {
- return entry;
- }
- }
-
- return NULL;
-}
-
-void
-ir_structure_splitting_visitor::split_deref(ir_dereference **deref)
-{
- if ((*deref)->ir_type != ir_type_dereference_record)
- return;
-
- ir_dereference_record *deref_record = (ir_dereference_record *)*deref;
- ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable();
- if (!deref_var)
- return;
-
- variable_entry2 *entry = get_splitting_entry(deref_var->var);
- if (!entry)
- return;
-
- unsigned int i;
- for (i = 0; i < entry->var->type->length; i++) {
- if (strcmp(deref_record->field,
- entry->var->type->fields.structure[i].name) == 0)
- break;
- }
- assert(i != entry->var->type->length);
-
- *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]);
-}
-
-void
-ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue)
-{
- if (!*rvalue)
- return;
-
- ir_dereference *deref = (*rvalue)->as_dereference();
-
- if (!deref)
- return;
-
- split_deref(&deref);
- *rvalue = deref;
-}
-
-ir_visitor_status
-ir_structure_splitting_visitor::visit_leave(ir_assignment *ir)
-{
- ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable();
- ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable();
- variable_entry2 *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL;
- variable_entry2 *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL;
- const glsl_type *type = ir->rhs->type;
-
- if ((lhs_entry || rhs_entry) && !ir->condition) {
- for (unsigned int i = 0; i < type->length; i++) {
- ir_dereference *new_lhs, *new_rhs;
- void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx;
-
- if (lhs_entry) {
- new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]);
- } else {
- new_lhs = new(mem_ctx)
- ir_dereference_record(ir->lhs->clone(mem_ctx, NULL),
- type->fields.structure[i].name);
- }
-
- if (rhs_entry) {
- new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]);
- } else {
- new_rhs = new(mem_ctx)
- ir_dereference_record(ir->rhs->clone(mem_ctx, NULL),
- type->fields.structure[i].name);
- }
-
- ir->insert_before(new(mem_ctx) ir_assignment(new_lhs,
- new_rhs,
- NULL));
- }
- ir->remove();
- } else {
- handle_rvalue(&ir->rhs);
- split_deref(&ir->lhs);
- }
-
- handle_rvalue(&ir->condition);
-
- return visit_continue;
-}
-
-bool
-do_structure_splitting(exec_list *instructions)
-{
- ir_structure_reference_visitor refs;
-
- visit_list_elements(&refs, instructions);
-
- /* Trim out variables we can't split. */
- foreach_iter(exec_list_iterator, iter, refs.variable_list) {
- variable_entry2 *entry = (variable_entry2 *)iter.get();
-
- if (debug) {
- printf("structure %s@%p: decl %d, whole_access %d\n",
- entry->var->name, (void *) entry->var, entry->declaration,
- entry->whole_structure_access);
- }
-
- if (!entry->declaration || entry->whole_structure_access) {
- entry->remove();
- }
- }
-
- if (refs.variable_list.is_empty())
- return false;
-
- void *mem_ctx = talloc_new(NULL);
-
- /* Replace the decls of the structures to be split with their split
- * components.
- */
- foreach_iter(exec_list_iterator, iter, refs.variable_list) {
- variable_entry2 *entry = (variable_entry2 *)iter.get();
- const struct glsl_type *type = entry->var->type;
-
- entry->mem_ctx = talloc_parent(entry->var);
-
- entry->components = talloc_array(mem_ctx,
- ir_variable *,
- type->length);
-
- for (unsigned int i = 0; i < entry->var->type->length; i++) {
- const char *name = talloc_asprintf(mem_ctx, "%s_%s",
- entry->var->name,
- type->fields.structure[i].name);
-
- entry->components[i] =
- new(entry->mem_ctx) ir_variable(type->fields.structure[i].type,
- name,
- ir_var_temporary);
- entry->var->insert_before(entry->components[i]);
- }
-
- entry->var->remove();
- }
-
- ir_structure_splitting_visitor split(&refs.variable_list);
- visit_list_elements(&split, instructions);
-
- talloc_free(mem_ctx);
-
- return true;
-}
+++ /dev/null
-/*
- * 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 ir_sub_to_add_neg.cpp
- *
- * Breaks an ir_binop_sub expression down to add(op0, neg(op1))
- *
- * This simplifies expression reassociation, and for many backends
- * there is no subtract operation separate from adding the negation.
- * For backends with native subtract operations, they will probably
- * want to recognize add(op0, neg(op1)) or the other way around to
- * produce a subtract anyway.
- */
-
-#include "ir.h"
-
-class ir_sub_to_add_neg_visitor : public ir_hierarchical_visitor {
-public:
- ir_sub_to_add_neg_visitor()
- {
- this->progress = false;
- }
-
- ir_visitor_status visit_leave(ir_expression *);
-
- bool progress;
-};
-
-bool
-do_sub_to_add_neg(exec_list *instructions)
-{
- ir_sub_to_add_neg_visitor v;
-
- visit_list_elements(&v, instructions);
- return v.progress;
-}
-
-ir_visitor_status
-ir_sub_to_add_neg_visitor::visit_leave(ir_expression *ir)
-{
- if (ir->operation != ir_binop_sub)
- return visit_continue;
-
- void *mem_ctx = talloc_parent(ir);
-
- ir->operation = ir_binop_add;
- ir->operands[1] = new(mem_ctx) ir_expression(ir_unop_neg,
- ir->operands[1]->type,
- ir->operands[1],
- NULL);
-
- this->progress = true;
-
- return visit_continue;
-}
+++ /dev/null
-/*
- * 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 ir_swizzle_swizzle.cpp
- *
- * Eliminates the second swizzle in a swizzle chain.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor {
-public:
- ir_swizzle_swizzle_visitor()
- {
- progress = false;
- }
-
- virtual ir_visitor_status visit_enter(ir_swizzle *);
-
- bool progress;
-};
-
-ir_visitor_status
-ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir)
-{
- int mask2[4];
-
- ir_swizzle *swiz2 = ir->val->as_swizzle();
- if (!swiz2)
- return visit_continue;
-
- memset(&mask2, 0, sizeof(mask2));
- if (swiz2->mask.num_components >= 1)
- mask2[0] = swiz2->mask.x;
- if (swiz2->mask.num_components >= 2)
- mask2[1] = swiz2->mask.y;
- if (swiz2->mask.num_components >= 3)
- mask2[2] = swiz2->mask.z;
- if (swiz2->mask.num_components >= 4)
- mask2[3] = swiz2->mask.w;
-
- if (ir->mask.num_components >= 1)
- ir->mask.x = mask2[ir->mask.x];
- if (ir->mask.num_components >= 2)
- ir->mask.y = mask2[ir->mask.y];
- if (ir->mask.num_components >= 3)
- ir->mask.z = mask2[ir->mask.z];
- if (ir->mask.num_components >= 4)
- ir->mask.w = mask2[ir->mask.w];
-
- ir->val = swiz2->val;
-
- this->progress = true;
-
- return visit_continue;
-}
-
-/**
- * Does a copy propagation pass on the code present in the instruction stream.
- */
-bool
-do_swizzle_swizzle(exec_list *instructions)
-{
- ir_swizzle_swizzle_visitor v;
-
- v.run(instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_tree_grafting.cpp
- *
- * Takes assignments to variables that are dereferenced only once and
- * pastes the RHS expression into where the variable is dereferenced.
- *
- * In the process of various operations like function inlining and
- * tertiary op handling, we'll end up with our expression trees having
- * been chopped up into a series of assignments of short expressions
- * to temps. Other passes like ir_algebraic.cpp would prefer to see
- * the deepest expression trees they can to try to optimize them.
- *
- * This is a lot like copy propagaton. In comparison, copy
- * propagation only acts on plain copies, not arbitrary expressions on
- * the RHS. Generally, we wouldn't want to go pasting some
- * complicated expression everywhere it got used, though, so we don't
- * handle expressions in that pass.
- *
- * The hard part is making sure we don't move an expression across
- * some other assignments that would change the value of the
- * expression. So we split this into two passes: First, find the
- * variables in our scope which are written to once and read once, and
- * then go through basic blocks seeing if we find an opportunity to
- * move those expressions safely.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_variable_refcount.h"
-#include "ir_basic_block.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-static bool debug = false;
-
-class ir_tree_grafting_visitor : public ir_hierarchical_visitor {
-public:
- ir_tree_grafting_visitor(ir_assignment *graft_assign,
- ir_variable *graft_var)
- {
- this->progress = false;
- this->graft_assign = graft_assign;
- this->graft_var = graft_var;
- }
-
- virtual ir_visitor_status visit_leave(class ir_assignment *);
- virtual ir_visitor_status visit_enter(class ir_call *);
- virtual ir_visitor_status visit_enter(class ir_expression *);
- virtual ir_visitor_status visit_enter(class ir_function *);
- virtual ir_visitor_status visit_enter(class ir_function_signature *);
- virtual ir_visitor_status visit_enter(class ir_if *);
- virtual ir_visitor_status visit_enter(class ir_loop *);
- virtual ir_visitor_status visit_enter(class ir_swizzle *);
- virtual ir_visitor_status visit_enter(class ir_texture *);
-
- bool do_graft(ir_rvalue **rvalue);
-
- bool progress;
- ir_variable *graft_var;
- ir_assignment *graft_assign;
-};
-
-struct find_deref_info {
- ir_variable *var;
- bool found;
-};
-
-void
-dereferences_variable_callback(ir_instruction *ir, void *data)
-{
- struct find_deref_info *info = (struct find_deref_info *)data;
- ir_dereference_variable *deref = ir->as_dereference_variable();
-
- if (deref && deref->var == info->var)
- info->found = true;
-}
-
-static bool
-dereferences_variable(ir_instruction *ir, ir_variable *var)
-{
- struct find_deref_info info;
-
- info.var = var;
- info.found = false;
-
- visit_tree(ir, dereferences_variable_callback, &info);
-
- return info.found;
-}
-
-bool
-ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue)
-{
- if (!*rvalue)
- return false;
-
- ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
-
- if (!deref || deref->var != this->graft_var)
- return false;
-
- if (debug) {
- printf("GRAFTING:\n");
- this->graft_assign->print();
- printf("\n");
- printf("TO:\n");
- (*rvalue)->print();
- printf("\n");
- }
-
- this->graft_assign->remove();
- *rvalue = this->graft_assign->rhs;
-
- this->progress = true;
- return true;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_loop *ir)
-{
- (void)ir;
- /* Do not traverse into the body of the loop since that is a
- * different basic block.
- */
- return visit_stop;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_leave(ir_assignment *ir)
-{
- if (do_graft(&ir->rhs) ||
- do_graft(&ir->condition))
- return visit_stop;
-
- /* If this assignment updates a variable used in the assignment
- * we're trying to graft, then we're done.
- */
- if (dereferences_variable(this->graft_assign->rhs,
- ir->lhs->variable_referenced())) {
- if (debug) {
- printf("graft killed by: ");
- ir->print();
- printf("\n");
- }
- return visit_stop;
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_function *ir)
-{
- (void) ir;
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir)
-{
- (void)ir;
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_call *ir)
-{
- exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
- /* Reminder: iterating ir_call iterates its parameters. */
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_variable *sig_param = (ir_variable *)sig_iter.get();
- ir_rvalue *ir = (ir_rvalue *)iter.get();
- ir_rvalue *new_ir = ir;
-
- if (sig_param->mode != ir_var_in)
- continue;
-
- if (do_graft(&new_ir)) {
- ir->replace_with(new_ir);
- return visit_stop;
- }
- sig_iter.next();
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_expression *ir)
-{
- for (unsigned int i = 0; i < ir->get_num_operands(); i++) {
- if (do_graft(&ir->operands[i]))
- return visit_stop;
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_if *ir)
-{
- if (do_graft(&ir->condition))
- return visit_stop;
-
- /* Do not traverse into the body of the if-statement since that is a
- * different basic block.
- */
- return visit_continue_with_parent;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir)
-{
- if (do_graft(&ir->val))
- return visit_stop;
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_tree_grafting_visitor::visit_enter(ir_texture *ir)
-{
- if (do_graft(&ir->coordinate) ||
- do_graft(&ir->projector) ||
- do_graft(&ir->shadow_comparitor))
- return visit_stop;
-
- switch (ir->op) {
- case ir_tex:
- break;
- case ir_txb:
- if (do_graft(&ir->lod_info.bias))
- return visit_stop;
- break;
- case ir_txf:
- case ir_txl:
- if (do_graft(&ir->lod_info.lod))
- return visit_stop;
- break;
- case ir_txd:
- if (do_graft(&ir->lod_info.grad.dPdx) ||
- do_graft(&ir->lod_info.grad.dPdy))
- return visit_stop;
- break;
- }
-
- return visit_continue;
-}
-
-struct tree_grafting_info {
- ir_variable_refcount_visitor *refs;
- bool progress;
-};
-
-static bool
-try_tree_grafting(ir_assignment *start,
- ir_variable *lhs_var,
- ir_instruction *bb_last)
-{
- ir_tree_grafting_visitor v(start, lhs_var);
-
- if (debug) {
- printf("trying to graft: ");
- lhs_var->print();
- printf("\n");
- }
-
- for (ir_instruction *ir = (ir_instruction *)start->next;
- ir != bb_last->next;
- ir = (ir_instruction *)ir->next) {
-
- if (debug) {
- printf("- ");
- ir->print();
- printf("\n");
- }
-
- ir_visitor_status s = ir->accept(&v);
- if (s == visit_stop)
- return v.progress;
- }
-
- return false;
-}
-
-static void
-tree_grafting_basic_block(ir_instruction *bb_first,
- ir_instruction *bb_last,
- void *data)
-{
- struct tree_grafting_info *info = (struct tree_grafting_info *)data;
- ir_instruction *ir, *next;
-
- for (ir = bb_first, next = (ir_instruction *)ir->next;
- ir != bb_last->next;
- ir = next, next = (ir_instruction *)ir->next) {
- ir_assignment *assign = ir->as_assignment();
-
- if (!assign)
- continue;
-
- ir_variable *lhs_var = assign->whole_variable_written();
- if (!lhs_var)
- continue;
-
- if (lhs_var->mode == ir_var_out ||
- lhs_var->mode == ir_var_inout)
- continue;
-
- variable_entry *entry = info->refs->get_variable_entry(lhs_var);
-
- if (!entry->declaration ||
- entry->assigned_count != 1 ||
- entry->referenced_count != 2)
- continue;
-
- assert(assign == entry->assign);
-
- /* Found a possibly graftable assignment. Now, walk through the
- * rest of the BB seeing if the deref is here, and if nothing interfered with
- * pasting its expression's values in between.
- */
- info->progress |= try_tree_grafting(assign, lhs_var, bb_last);
- }
-}
-
-/**
- * Does a copy propagation pass on the code present in the instruction stream.
- */
-bool
-do_tree_grafting(exec_list *instructions)
-{
- ir_variable_refcount_visitor refs;
- struct tree_grafting_info info;
-
- info.progress = false;
- info.refs = &refs;
-
- visit_list_elements(info.refs, instructions);
-
- call_for_basic_blocks(instructions, tree_grafting_basic_block, &info);
-
- return info.progress;
-}
+++ /dev/null
-/*
- * 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 ir_vec_index_to_cond_assign.cpp
- *
- * Turns indexing into vector types to a series of conditional moves
- * of each channel's swizzle into a temporary.
- *
- * Most GPUs don't have a native way to do this operation, and this
- * works around that. For drivers using both this pass and
- * ir_vec_index_to_swizzle, there's a risk that this pass will happen
- * before sufficient constant folding to find that the array index is
- * constant. However, we hope that other optimization passes,
- * particularly constant folding of assignment conditions and copy
- * propagation, will result in the same code in the end.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-/**
- * Visitor class for replacing expressions with ir_constant values.
- */
-
-class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor {
-public:
- ir_vec_index_to_cond_assign_visitor()
- {
- progress = false;
- }
-
- ir_rvalue *convert_vec_index_to_cond_assign(ir_rvalue *val);
-
- virtual ir_visitor_status visit_enter(ir_expression *);
- virtual ir_visitor_status visit_enter(ir_swizzle *);
- virtual ir_visitor_status visit_leave(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_return *);
- virtual ir_visitor_status visit_enter(ir_call *);
- virtual ir_visitor_status visit_enter(ir_if *);
-
- bool progress;
-};
-
-ir_rvalue *
-ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue *ir)
-{
- ir_dereference_array *orig_deref = ir->as_dereference_array();
- ir_assignment *assign;
- ir_variable *index, *var;
- ir_dereference *deref;
- ir_expression *condition;
- ir_swizzle *swizzle;
- int i;
-
- if (!orig_deref)
- return ir;
-
- if (orig_deref->array->type->is_matrix() ||
- orig_deref->array->type->is_array())
- return ir;
-
- void *mem_ctx = talloc_parent(ir);
-
- assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
-
- /* Store the index to a temporary to avoid reusing its tree. */
- index = new(base_ir) ir_variable(glsl_type::int_type,
- "vec_index_tmp_i",
- ir_var_temporary);
- base_ir->insert_before(index);
- deref = new(base_ir) ir_dereference_variable(index);
- assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL);
- base_ir->insert_before(assign);
-
- /* Temporary where we store whichever value we swizzle out. */
- var = new(base_ir) ir_variable(ir->type, "vec_index_tmp_v",
- ir_var_temporary);
- base_ir->insert_before(var);
-
- /* Generate a conditional move of each vector element to the temp. */
- for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
- deref = new(base_ir) ir_dereference_variable(index);
- condition = new(base_ir) ir_expression(ir_binop_equal,
- glsl_type::bool_type,
- deref,
- new(base_ir) ir_constant(i));
-
- /* Just clone the rest of the deref chain when trying to get at the
- * underlying variable.
- */
- swizzle = new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
- i, 0, 0, 0, 1);
-
- deref = new(base_ir) ir_dereference_variable(var);
- assign = new(base_ir) ir_assignment(deref, swizzle, condition);
- base_ir->insert_before(assign);
- }
-
- this->progress = true;
- return new(base_ir) ir_dereference_variable(var);
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir)
-{
- unsigned int i;
-
- for (i = 0; i < ir->get_num_operands(); i++) {
- ir->operands[i] = convert_vec_index_to_cond_assign(ir->operands[i]);
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir)
-{
- /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which
- * the result of indexing a vector is. But maybe at some point we'll end up
- * using swizzling of scalars for vector construction.
- */
- ir->val = convert_vec_index_to_cond_assign(ir->val);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir)
-{
- ir_variable *index, *var;
- ir_dereference_variable *deref;
- ir_assignment *assign;
- int i;
-
- ir->rhs = convert_vec_index_to_cond_assign(ir->rhs);
- if (ir->condition)
- ir->condition = convert_vec_index_to_cond_assign(ir->condition);
-
- /* Last, handle the LHS */
- ir_dereference_array *orig_deref = ir->lhs->as_dereference_array();
-
- if (!orig_deref ||
- orig_deref->array->type->is_matrix() ||
- orig_deref->array->type->is_array())
- return visit_continue;
-
- void *mem_ctx = talloc_parent(ir);
-
- assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
-
- /* Store the index to a temporary to avoid reusing its tree. */
- index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i",
- ir_var_temporary);
- ir->insert_before(index);
- deref = new(ir) ir_dereference_variable(index);
- assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL);
- ir->insert_before(assign);
-
- /* Store the RHS to a temporary to avoid reusing its tree. */
- var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v",
- ir_var_temporary);
- ir->insert_before(var);
- deref = new(ir) ir_dereference_variable(var);
- assign = new(ir) ir_assignment(deref, ir->rhs, NULL);
- ir->insert_before(assign);
-
- /* Generate a conditional move of each vector element to the temp. */
- for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
- ir_rvalue *condition, *swizzle;
-
- deref = new(ir) ir_dereference_variable(index);
- condition = new(ir) ir_expression(ir_binop_equal,
- glsl_type::bool_type,
- deref,
- new(ir) ir_constant(i));
-
- /* Just clone the rest of the deref chain when trying to get at the
- * underlying variable.
- */
- swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
- i, 0, 0, 0, 1);
-
- deref = new(ir) ir_dereference_variable(var);
- assign = new(ir) ir_assignment(swizzle, deref, condition);
- ir->insert_before(assign);
- }
- ir->remove();
-
- this->progress = true;
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir)
-{
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_rvalue *param = (ir_rvalue *)iter.get();
- ir_rvalue *new_param = convert_vec_index_to_cond_assign(param);
-
- if (new_param != param) {
- param->replace_with(new_param);
- }
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir)
-{
- if (ir->value) {
- ir->value = convert_vec_index_to_cond_assign(ir->value);
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir)
-{
- ir->condition = convert_vec_index_to_cond_assign(ir->condition);
-
- return visit_continue;
-}
-
-bool
-do_vec_index_to_cond_assign(exec_list *instructions)
-{
- ir_vec_index_to_cond_assign_visitor v;
-
- visit_list_elements(&v, instructions);
-
- return v.progress;
-}
+++ /dev/null
-/*
- * 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 ir_vec_index_to_swizzle.cpp
- *
- * Turns constant indexing into vector types to swizzles. This will
- * let other swizzle-aware optimization passes catch these constructs,
- * and codegen backends not have to worry about this case.
- */
-
-#include "ir.h"
-#include "ir_visitor.h"
-#include "ir_optimization.h"
-#include "glsl_types.h"
-
-/**
- * Visitor class for replacing expressions with ir_constant values.
- */
-
-class ir_vec_index_to_swizzle_visitor : public ir_hierarchical_visitor {
-public:
- ir_vec_index_to_swizzle_visitor()
- {
- progress = false;
- }
-
- ir_rvalue *convert_vec_index_to_swizzle(ir_rvalue *val);
-
- virtual ir_visitor_status visit_enter(ir_expression *);
- virtual ir_visitor_status visit_enter(ir_swizzle *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
- virtual ir_visitor_status visit_enter(ir_return *);
- virtual ir_visitor_status visit_enter(ir_call *);
- virtual ir_visitor_status visit_enter(ir_if *);
-
- bool progress;
-};
-
-ir_rvalue *
-ir_vec_index_to_swizzle_visitor::convert_vec_index_to_swizzle(ir_rvalue *ir)
-{
- ir_dereference_array *deref = ir->as_dereference_array();
- ir_constant *ir_constant;
-
- if (!deref)
- return ir;
-
- if (deref->array->type->is_matrix() || deref->array->type->is_array())
- return ir;
-
- assert(deref->array_index->type->base_type == GLSL_TYPE_INT);
- ir_constant = deref->array_index->constant_expression_value();
- if (!ir_constant)
- return ir;
-
- void *ctx = talloc_parent(ir);
- this->progress = true;
- return new(ctx) ir_swizzle(deref->array,
- ir_constant->value.i[0], 0, 0, 0, 1);
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_expression *ir)
-{
- unsigned int i;
-
- for (i = 0; i < ir->get_num_operands(); i++) {
- ir->operands[i] = convert_vec_index_to_swizzle(ir->operands[i]);
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_swizzle *ir)
-{
- /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which
- * the result of indexing a vector is. But maybe at some point we'll end up
- * using swizzling of scalars for vector construction.
- */
- ir->val = convert_vec_index_to_swizzle(ir->val);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_assignment *ir)
-{
- ir->set_lhs(convert_vec_index_to_swizzle(ir->lhs));
- ir->rhs = convert_vec_index_to_swizzle(ir->rhs);
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_call *ir)
-{
- foreach_iter(exec_list_iterator, iter, *ir) {
- ir_rvalue *param = (ir_rvalue *)iter.get();
- ir_rvalue *new_param = convert_vec_index_to_swizzle(param);
-
- if (new_param != param) {
- param->replace_with(new_param);
- }
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_return *ir)
-{
- if (ir->value) {
- ir->value = convert_vec_index_to_swizzle(ir->value);
- }
-
- return visit_continue;
-}
-
-ir_visitor_status
-ir_vec_index_to_swizzle_visitor::visit_enter(ir_if *ir)
-{
- ir->condition = convert_vec_index_to_swizzle(ir->condition);
-
- return visit_continue;
-}
-
-bool
-do_vec_index_to_swizzle(exec_list *instructions)
-{
- ir_vec_index_to_swizzle_visitor v;
-
- v.run(instructions);
-
- return v.progress;
-}
--- /dev/null
+/*
+ * 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 ir_div_to_mul_rcp.cpp
+ *
+ * Breaks an ir_unop_div expression down to op0 * (rcp(op1)).
+ *
+ * Many GPUs don't have a divide instruction (945 and 965 included),
+ * but they do have an RCP instruction to compute an approximate
+ * reciprocal. By breaking the operation down, constant reciprocals
+ * can get constant folded.
+ */
+
+#include "ir.h"
+#include "glsl_types.h"
+
+class ir_div_to_mul_rcp_visitor : public ir_hierarchical_visitor {
+public:
+ ir_div_to_mul_rcp_visitor()
+ {
+ this->made_progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_expression *);
+
+ bool made_progress;
+};
+
+bool
+do_div_to_mul_rcp(exec_list *instructions)
+{
+ ir_div_to_mul_rcp_visitor v;
+
+ visit_list_elements(&v, instructions);
+ return v.made_progress;
+}
+
+ir_visitor_status
+ir_div_to_mul_rcp_visitor::visit_leave(ir_expression *ir)
+{
+ if (ir->operation != ir_binop_div)
+ return visit_continue;
+
+ if (ir->operands[1]->type->base_type != GLSL_TYPE_INT &&
+ ir->operands[1]->type->base_type != GLSL_TYPE_UINT) {
+ /* New expression for the 1.0 / op1 */
+ ir_rvalue *expr;
+ expr = new(ir) ir_expression(ir_unop_rcp,
+ ir->operands[1]->type,
+ ir->operands[1],
+ NULL);
+
+ /* op0 / op1 -> op0 * (1.0 / op1) */
+ ir->operation = ir_binop_mul;
+ ir->operands[1] = expr;
+ } else {
+ /* Be careful with integer division -- we need to do it as a
+ * float and re-truncate, since rcp(n > 1) of an integer would
+ * just be 0.
+ */
+ ir_rvalue *op0, *op1;
+ const struct glsl_type *vec_type;
+
+ vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
+ ir->operands[1]->type->vector_elements,
+ ir->operands[1]->type->matrix_columns);
+
+ if (ir->operands[1]->type->base_type == GLSL_TYPE_INT)
+ op1 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[1], NULL);
+ else
+ op1 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[1], NULL);
+
+ op1 = new(ir) ir_expression(ir_unop_rcp, op1->type, op1, NULL);
+
+ vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
+ ir->operands[0]->type->vector_elements,
+ ir->operands[0]->type->matrix_columns);
+
+ if (ir->operands[0]->type->base_type == GLSL_TYPE_INT)
+ op0 = new(ir) ir_expression(ir_unop_i2f, vec_type, ir->operands[0], NULL);
+ else
+ op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL);
+
+ op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1);
+
+ ir->operation = ir_unop_f2i;
+ ir->operands[0] = op0;
+ ir->operands[1] = NULL;
+ }
+
+ this->made_progress = true;
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_explog_to_explog2.cpp
+ *
+ * Many GPUs don't have a base e log or exponent instruction, but they
+ * do have base 2 versions, so this pass converts exp and log to exp2
+ * and log2 operations.
+ */
+
+#include "main/core.h" /* for log2f on MSVC */
+#include "ir.h"
+#include "glsl_types.h"
+
+class ir_explog_to_explog2_visitor : public ir_hierarchical_visitor {
+public:
+ ir_explog_to_explog2_visitor()
+ {
+ this->progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_expression *);
+
+ bool progress;
+};
+
+bool
+do_explog_to_explog2(exec_list *instructions)
+{
+ ir_explog_to_explog2_visitor v;
+
+ visit_list_elements(&v, instructions);
+ return v.progress;
+}
+
+ir_visitor_status
+ir_explog_to_explog2_visitor::visit_leave(ir_expression *ir)
+{
+ if (ir->operation == ir_unop_exp) {
+ void *mem_ctx = talloc_parent(ir);
+ ir_constant *log2_e = new(mem_ctx) ir_constant(log2f(M_E));
+
+ ir->operation = ir_unop_exp2;
+ ir->operands[0] = new(mem_ctx) ir_expression(ir_binop_mul,
+ ir->operands[0]->type,
+ ir->operands[0],
+ log2_e);
+ this->progress = true;
+ }
+
+ if (ir->operation == ir_unop_log) {
+ void *mem_ctx = talloc_parent(ir);
+
+ ir->operation = ir_binop_mul;
+ ir->operands[0] = new(mem_ctx) ir_expression(ir_unop_log2,
+ ir->operands[0]->type,
+ ir->operands[0],
+ NULL);
+ ir->operands[1] = new(mem_ctx) ir_constant(1.0f / log2f(M_E));
+ this->progress = true;
+ }
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_if_to_cond_assign.cpp
+ *
+ * This attempts to flatten all if statements to conditional
+ * assignments for GPUs that don't do control flow.
+ *
+ * It can't handle other control flow being inside of its block, such
+ * as calls or loops. Hopefully loop unrolling and inlining will take
+ * care of those.
+ */
+
+#include "glsl_types.h"
+#include "ir.h"
+
+class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor {
+public:
+ ir_if_to_cond_assign_visitor()
+ {
+ this->progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_if *);
+
+ bool progress;
+};
+
+bool
+do_if_to_cond_assign(exec_list *instructions)
+{
+ ir_if_to_cond_assign_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
+
+void
+check_control_flow(ir_instruction *ir, void *data)
+{
+ bool *found_control_flow = (bool *)data;
+ switch (ir->ir_type) {
+ case ir_type_call:
+ case ir_type_discard:
+ case ir_type_loop:
+ case ir_type_loop_jump:
+ case ir_type_return:
+ *found_control_flow = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+move_block_to_cond_assign(void *mem_ctx,
+ ir_if *if_ir, ir_variable *cond_var, bool then)
+{
+ exec_list *instructions;
+
+ if (then) {
+ instructions = &if_ir->then_instructions;
+ } else {
+ instructions = &if_ir->else_instructions;
+ }
+
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+
+ if (ir->ir_type == ir_type_assignment) {
+ ir_assignment *assign = (ir_assignment *)ir;
+ ir_rvalue *cond_expr;
+ ir_dereference *deref = new(mem_ctx) ir_dereference_variable(cond_var);
+
+ if (then) {
+ cond_expr = deref;
+ } else {
+ cond_expr = new(mem_ctx) ir_expression(ir_unop_logic_not,
+ glsl_type::bool_type,
+ deref,
+ NULL);
+ }
+
+ if (!assign->condition) {
+ assign->condition = cond_expr;
+ } else {
+ assign->condition = new(mem_ctx) ir_expression(ir_binop_logic_and,
+ glsl_type::bool_type,
+ cond_expr,
+ assign->condition);
+ }
+ }
+
+ /* Now, move from the if block to the block surrounding it. */
+ ir->remove();
+ if_ir->insert_before(ir);
+ }
+}
+
+ir_visitor_status
+ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir)
+{
+ bool found_control_flow = false;
+ ir_variable *cond_var;
+ ir_assignment *assign;
+ ir_dereference_variable *deref;
+
+ /* Check that both blocks don't contain anything we can't support. */
+ foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) {
+ ir_instruction *then_ir = (ir_instruction *)then_iter.get();
+ visit_tree(then_ir, check_control_flow, &found_control_flow);
+ }
+ foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) {
+ ir_instruction *else_ir = (ir_instruction *)else_iter.get();
+ visit_tree(else_ir, check_control_flow, &found_control_flow);
+ }
+ if (found_control_flow)
+ return visit_continue;
+
+ void *mem_ctx = talloc_parent(ir);
+
+ /* Store the condition to a variable so the assignment conditions are
+ * simpler.
+ */
+ cond_var = new(mem_ctx) ir_variable(glsl_type::bool_type,
+ "if_to_cond_assign_condition",
+ ir_var_temporary);
+ ir->insert_before(cond_var);
+
+ deref = new(mem_ctx) ir_dereference_variable(cond_var);
+ assign = new(mem_ctx) ir_assignment(deref,
+ ir->condition, NULL);
+ ir->insert_before(assign);
+
+ /* Now, move all of the instructions out of the if blocks, putting
+ * conditions on assignments.
+ */
+ move_block_to_cond_assign(mem_ctx, ir, cond_var, true);
+ move_block_to_cond_assign(mem_ctx, ir, cond_var, false);
+
+ ir->remove();
+
+ this->progress = true;
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * Copyright © 2010 Luca Barbieri
+ *
+ * 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 ir_lower_jumps.cpp
+ */
+
+#include "glsl_types.h"
+#include <string.h>
+#include "ir.h"
+
+enum jump_strength
+{
+ strength_none,
+ strength_always_clears_execute_flag,
+ strength_continue,
+ strength_break,
+ strength_return,
+ strength_discard
+};
+
+struct block_record
+{
+ /* minimum jump strength (of lowered IR, not pre-lowering IR)
+ *
+ * If the block ends with a jump, must be the strength of the jump.
+ * Otherwise, the jump would be dead and have been deleted before)
+ *
+ * If the block doesn't end with a jump, it can be different than strength_none if all paths before it lead to some jump
+ * (e.g. an if with a return in one branch, and a break in the other, while not lowering them)
+ * Note that identical jumps are usually unified though.
+ */
+ jump_strength min_strength;
+
+ /* can anything clear the execute flag? */
+ bool may_clear_execute_flag;
+
+ block_record()
+ {
+ this->min_strength = strength_none;
+ this->may_clear_execute_flag = false;
+ }
+};
+
+struct loop_record
+{
+ ir_function_signature* signature;
+ ir_loop* loop;
+
+ /* used to avoid lowering the break used to represent lowered breaks */
+ unsigned nesting_depth;
+ bool in_if_at_the_end_of_the_loop;
+
+ bool may_set_return_flag;
+
+ ir_variable* break_flag;
+ ir_variable* execute_flag; /* cleared to emulate continue */
+
+ loop_record(ir_function_signature* p_signature = 0, ir_loop* p_loop = 0)
+ {
+ this->signature = p_signature;
+ this->loop = p_loop;
+ this->nesting_depth = 0;
+ this->in_if_at_the_end_of_the_loop = false;
+ this->may_set_return_flag = false;
+ this->break_flag = 0;
+ this->execute_flag = 0;
+ }
+
+ ir_variable* get_execute_flag()
+ {
+ /* also supported for the "function loop" */
+ if(!this->execute_flag) {
+ exec_list& list = this->loop ? this->loop->body_instructions : signature->body;
+ this->execute_flag = new(this->signature) ir_variable(glsl_type::bool_type, "execute_flag", ir_var_temporary);
+ list.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(execute_flag), new(this->signature) ir_constant(true), 0));
+ list.push_head(this->execute_flag);
+ }
+ return this->execute_flag;
+ }
+
+ ir_variable* get_break_flag()
+ {
+ assert(this->loop);
+ if(!this->break_flag) {
+ this->break_flag = new(this->signature) ir_variable(glsl_type::bool_type, "break_flag", ir_var_temporary);
+ this->loop->insert_before(this->break_flag);
+ this->loop->insert_before(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(break_flag), new(this->signature) ir_constant(false), 0));
+ }
+ return this->break_flag;
+ }
+};
+
+struct function_record
+{
+ ir_function_signature* signature;
+ ir_variable* return_flag; /* used to break out of all loops and then jump to the return instruction */
+ ir_variable* return_value;
+ bool is_main;
+ unsigned nesting_depth;
+
+ function_record(ir_function_signature* p_signature = 0)
+ {
+ this->signature = p_signature;
+ this->return_flag = 0;
+ this->return_value = 0;
+ this->nesting_depth = 0;
+ this->is_main = this->signature && (strcmp(this->signature->function_name(), "main") == 0);
+ }
+
+ ir_variable* get_return_flag()
+ {
+ if(!this->return_flag) {
+ this->return_flag = new(this->signature) ir_variable(glsl_type::bool_type, "return_flag", ir_var_temporary);
+ this->signature->body.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(return_flag), new(this->signature) ir_constant(false), 0));
+ this->signature->body.push_head(this->return_flag);
+ }
+ return this->return_flag;
+ }
+
+ ir_variable* get_return_value()
+ {
+ if(!this->return_value) {
+ assert(!this->signature->return_type->is_void());
+ return_value = new(this->signature) ir_variable(this->signature->return_type, "return_value", ir_var_temporary);
+ this->signature->body.push_head(this->return_value);
+ }
+ return this->return_value;
+ }
+};
+
+struct ir_lower_jumps_visitor : public ir_control_flow_visitor {
+ bool progress;
+
+ struct function_record function;
+ struct loop_record loop;
+ struct block_record block;
+
+ bool pull_out_jumps;
+ bool lower_continue;
+ bool lower_break;
+ bool lower_sub_return;
+ bool lower_main_return;
+
+ ir_lower_jumps_visitor()
+ {
+ this->progress = false;
+ }
+
+ void truncate_after_instruction(exec_node *ir)
+ {
+ if (!ir)
+ return;
+
+ while (!ir->get_next()->is_tail_sentinel()) {
+ ((ir_instruction *)ir->get_next())->remove();
+ this->progress = true;
+ }
+ }
+
+ void move_outer_block_inside(ir_instruction *ir, exec_list *inner_block)
+ {
+ while (!ir->get_next()->is_tail_sentinel()) {
+ ir_instruction *move_ir = (ir_instruction *)ir->get_next();
+
+ move_ir->remove();
+ inner_block->push_tail(move_ir);
+ }
+ }
+
+ virtual void visit(class ir_loop_jump * ir)
+ {
+ truncate_after_instruction(ir);
+ this->block.min_strength = ir->is_break() ? strength_break : strength_continue;
+ }
+
+ virtual void visit(class ir_return * ir)
+ {
+ truncate_after_instruction(ir);
+ this->block.min_strength = strength_return;
+ }
+
+ virtual void visit(class ir_discard * ir)
+ {
+ truncate_after_instruction(ir);
+ this->block.min_strength = strength_discard;
+ }
+
+ enum jump_strength get_jump_strength(ir_instruction* ir)
+ {
+ if(!ir)
+ return strength_none;
+ else if(ir->ir_type == ir_type_loop_jump) {
+ if(((ir_loop_jump*)ir)->is_break())
+ return strength_break;
+ else
+ return strength_continue;
+ } else if(ir->ir_type == ir_type_return)
+ return strength_return;
+ else if(ir->ir_type == ir_type_discard)
+ return strength_discard;
+ else
+ return strength_none;
+ }
+
+ bool should_lower_jump(ir_jump* ir)
+ {
+ unsigned strength = get_jump_strength(ir);
+ bool lower;
+ switch(strength)
+ {
+ case strength_none:
+ lower = false; /* don't change this, code relies on it */
+ break;
+ case strength_continue:
+ lower = lower_continue;
+ break;
+ case strength_break:
+ assert(this->loop.loop);
+ /* never lower "canonical break" */
+ if(ir->get_next()->is_tail_sentinel() && (this->loop.nesting_depth == 0
+ || (this->loop.nesting_depth == 1 && this->loop.in_if_at_the_end_of_the_loop)))
+ lower = false;
+ else
+ lower = lower_break;
+ break;
+ case strength_return:
+ /* never lower return at the end of a this->function */
+ if(this->function.nesting_depth == 0 && ir->get_next()->is_tail_sentinel())
+ lower = false;
+ else if (this->function.is_main)
+ lower = lower_main_return;
+ else
+ lower = lower_sub_return;
+ break;
+ case strength_discard:
+ lower = false; /* probably nothing needs this lowered */
+ break;
+ }
+ return lower;
+ }
+
+ block_record visit_block(exec_list* list)
+ {
+ block_record saved_block = this->block;
+ this->block = block_record();
+ visit_exec_list(list, this);
+ block_record ret = this->block;
+ this->block = saved_block;
+ return ret;
+ }
+
+ virtual void visit(ir_if *ir)
+ {
+ if(this->loop.nesting_depth == 0 && ir->get_next()->is_tail_sentinel())
+ this->loop.in_if_at_the_end_of_the_loop = true;
+
+ ++this->function.nesting_depth;
+ ++this->loop.nesting_depth;
+
+ block_record block_records[2];
+ ir_jump* jumps[2];
+
+ block_records[0] = visit_block(&ir->then_instructions);
+ block_records[1] = visit_block(&ir->else_instructions);
+
+retry: /* we get here if we put code after the if inside a branch */
+ for(unsigned i = 0; i < 2; ++i) {
+ exec_list& list = i ? ir->else_instructions : ir->then_instructions;
+ jumps[i] = 0;
+ if(!list.is_empty() && get_jump_strength((ir_instruction*)list.get_tail()))
+ jumps[i] = (ir_jump*)list.get_tail();
+ }
+
+ for(;;) {
+ jump_strength jump_strengths[2];
+
+ for(unsigned i = 0; i < 2; ++i) {
+ if(jumps[i]) {
+ jump_strengths[i] = block_records[i].min_strength;
+ assert(jump_strengths[i] == get_jump_strength(jumps[i]));
+ } else
+ jump_strengths[i] = strength_none;
+ }
+
+ /* move both jumps out if possible */
+ if(pull_out_jumps && jump_strengths[0] == jump_strengths[1]) {
+ bool unify = true;
+ if(jump_strengths[0] == strength_continue)
+ ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_continue));
+ else if(jump_strengths[0] == strength_break)
+ ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_break));
+ /* FINISHME: unify returns with identical expressions */
+ else if(jump_strengths[0] == strength_return && this->function.signature->return_type->is_void())
+ ir->insert_after(new(ir) ir_return(NULL));
+ /* FINISHME: unify discards */
+ else
+ unify = false;
+
+ if(unify) {
+ jumps[0]->remove();
+ jumps[1]->remove();
+ this->progress = true;
+
+ jumps[0] = 0;
+ jumps[1] = 0;
+ block_records[0].min_strength = strength_none;
+ block_records[1].min_strength = strength_none;
+ break;
+ }
+ }
+
+ /* lower a jump: if both need to lowered, start with the strongest one, so that
+ * we might later unify the lowered version with the other one
+ */
+ bool should_lower[2];
+ for(unsigned i = 0; i < 2; ++i)
+ should_lower[i] = should_lower_jump(jumps[i]);
+
+ int lower;
+ if(should_lower[1] && should_lower[0])
+ lower = jump_strengths[1] > jump_strengths[0];
+ else if(should_lower[0])
+ lower = 0;
+ else if(should_lower[1])
+ lower = 1;
+ else
+ break;
+
+ if(jump_strengths[lower] == strength_return) {
+ ir_variable* return_flag = this->function.get_return_flag();
+ if(!this->function.signature->return_type->is_void()) {
+ ir_variable* return_value = this->function.get_return_value();
+ jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_value), ((ir_return*)jumps[lower])->value, NULL));
+ }
+ jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_flag), new (ir) ir_constant(true), NULL));
+ this->loop.may_set_return_flag = true;
+ if(this->loop.loop) {
+ ir_loop_jump* lowered = 0;
+ lowered = new(ir) ir_loop_jump(ir_loop_jump::jump_break);
+ block_records[lower].min_strength = strength_break;
+ jumps[lower]->replace_with(lowered);
+ jumps[lower] = lowered;
+ } else
+ goto lower_continue;
+ this->progress = true;
+ } else if(jump_strengths[lower] == strength_break) {
+ /* We can't lower to an actual continue because that would execute the increment.
+ *
+ * In the lowered code, we instead put the break check between the this->loop body and the increment,
+ * which is impossible with a real continue as defined by the GLSL IR currently.
+ *
+ * Smarter options (such as undoing the increment) are possible but it's not worth implementing them,
+ * because if break is lowered, continue is almost surely lowered too.
+ */
+ jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(this->loop.get_break_flag()), new (ir) ir_constant(true), 0));
+ goto lower_continue;
+ } else if(jump_strengths[lower] == strength_continue) {
+lower_continue:
+ ir_variable* execute_flag = this->loop.get_execute_flag();
+ jumps[lower]->replace_with(new(ir) ir_assignment(new (ir) ir_dereference_variable(execute_flag), new (ir) ir_constant(false), 0));
+ jumps[lower] = 0;
+ block_records[lower].min_strength = strength_always_clears_execute_flag;
+ block_records[lower].may_clear_execute_flag = true;
+ this->progress = true;
+ break;
+ }
+ }
+
+ /* move out a jump out if possible */
+ if(pull_out_jumps) {
+ int move_out = -1;
+ if(jumps[0] && block_records[1].min_strength >= strength_continue)
+ move_out = 0;
+ else if(jumps[1] && block_records[0].min_strength >= strength_continue)
+ move_out = 1;
+
+ if(move_out >= 0)
+ {
+ jumps[move_out]->remove();
+ ir->insert_after(jumps[move_out]);
+ jumps[move_out] = 0;
+ block_records[move_out].min_strength = strength_none;
+ this->progress = true;
+ }
+ }
+
+ if(block_records[0].min_strength < block_records[1].min_strength)
+ this->block.min_strength = block_records[0].min_strength;
+ else
+ this->block.min_strength = block_records[1].min_strength;
+ this->block.may_clear_execute_flag = this->block.may_clear_execute_flag || block_records[0].may_clear_execute_flag || block_records[1].may_clear_execute_flag;
+
+ if(this->block.min_strength)
+ truncate_after_instruction(ir);
+ else if(this->block.may_clear_execute_flag)
+ {
+ int move_into = -1;
+ if(block_records[0].min_strength && !block_records[1].may_clear_execute_flag)
+ move_into = 1;
+ else if(block_records[1].min_strength && !block_records[0].may_clear_execute_flag)
+ move_into = 0;
+
+ if(move_into >= 0) {
+ assert(!block_records[move_into].min_strength && !block_records[move_into].may_clear_execute_flag); /* otherwise, we just truncated */
+
+ exec_list* list = move_into ? &ir->else_instructions : &ir->then_instructions;
+ exec_node* next = ir->get_next();
+ if(!next->is_tail_sentinel()) {
+ move_outer_block_inside(ir, list);
+
+ exec_list list;
+ list.head = next;
+ block_records[move_into] = visit_block(&list);
+
+ this->progress = true;
+ goto retry;
+ }
+ } else {
+ ir_instruction* ir_after;
+ for(ir_after = (ir_instruction*)ir->get_next(); !ir_after->is_tail_sentinel();)
+ {
+ ir_if* ir_if = ir_after->as_if();
+ if(ir_if && ir_if->else_instructions.is_empty()) {
+ ir_dereference_variable* ir_if_cond_deref = ir_if->condition->as_dereference_variable();
+ if(ir_if_cond_deref && ir_if_cond_deref->var == this->loop.execute_flag) {
+ ir_instruction* ir_next = (ir_instruction*)ir_after->get_next();
+ ir_after->insert_before(&ir_if->then_instructions);
+ ir_after->remove();
+ ir_after = ir_next;
+ continue;
+ }
+ }
+ ir_after = (ir_instruction*)ir_after->get_next();
+
+ /* only set this if we find any unprotected instruction */
+ this->progress = true;
+ }
+
+ if(!ir->get_next()->is_tail_sentinel()) {
+ assert(this->loop.execute_flag);
+ ir_if* if_execute = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.execute_flag));
+ move_outer_block_inside(ir, &if_execute->then_instructions);
+ ir->insert_after(if_execute);
+ }
+ }
+ }
+ --this->loop.nesting_depth;
+ --this->function.nesting_depth;
+ }
+
+ virtual void visit(ir_loop *ir)
+ {
+ ++this->function.nesting_depth;
+ loop_record saved_loop = this->loop;
+ this->loop = loop_record(this->function.signature, ir);
+
+ block_record body = visit_block(&ir->body_instructions);
+
+ if(body.min_strength >= strength_break) {
+ /* FINISHME: turn the this->loop into an if, or replace it with its body */
+ }
+
+ if(this->loop.break_flag) {
+ ir_if* break_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.break_flag));
+ break_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break));
+ ir->body_instructions.push_tail(break_if);
+ }
+
+ if(this->loop.may_set_return_flag) {
+ assert(this->function.return_flag);
+ ir_if* return_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->function.return_flag));
+ return_if->then_instructions.push_tail(new(ir) ir_loop_jump(saved_loop.loop ? ir_loop_jump::jump_break : ir_loop_jump::jump_continue));
+ ir->insert_after(return_if);
+ }
+
+ this->loop = saved_loop;
+ --this->function.nesting_depth;
+ }
+
+ virtual void visit(ir_function_signature *ir)
+ {
+ /* these are not strictly necessary */
+ assert(!this->function.signature);
+ assert(!this->loop.loop);
+
+ function_record saved_function = this->function;
+ loop_record saved_loop = this->loop;
+ this->function = function_record(ir);
+ this->loop = loop_record(ir);
+
+ assert(!this->loop.loop);
+ visit_block(&ir->body);
+
+ if(this->function.return_value)
+ ir->body.push_tail(new(ir) ir_return(new (ir) ir_dereference_variable(this->function.return_value)));
+
+ this->loop = saved_loop;
+ this->function = saved_function;
+ }
+
+ virtual void visit(class ir_function * ir)
+ {
+ visit_block(&ir->signatures);
+ }
+};
+
+bool
+do_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_return, bool lower_main_return, bool lower_continue, bool lower_break)
+{
+ ir_lower_jumps_visitor v;
+ v.pull_out_jumps = pull_out_jumps;
+ v.lower_continue = lower_continue;
+ v.lower_break = lower_break;
+ v.lower_sub_return = lower_sub_return;
+ v.lower_main_return = lower_main_return;
+
+ do {
+ v.progress = false;
+ visit_exec_list(instructions, &v);
+ } while (v.progress);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_mat_op_to_vec.cpp
+ *
+ * Breaks matrix operation expressions down to a series of vector operations.
+ *
+ * Generally this is how we have to codegen matrix operations for a
+ * GPU, so this gives us the chance to constant fold operations on a
+ * column or row.
+ */
+
+#include "ir.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+
+class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
+public:
+ ir_mat_op_to_vec_visitor()
+ {
+ this->made_progress = false;
+ this->mem_ctx = NULL;
+ }
+
+ ir_visitor_status visit_leave(ir_assignment *);
+
+ ir_dereference *get_column(ir_variable *var, int col);
+ ir_rvalue *get_element(ir_variable *var, int col, int row);
+
+ void do_mul_mat_mat(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_mat_vec(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_vec_mat(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_mat_scalar(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var,
+ ir_variable *b_var, bool test_equal);
+
+ void *mem_ctx;
+ bool made_progress;
+};
+
+static bool
+mat_op_to_vec_predicate(ir_instruction *ir)
+{
+ ir_expression *expr = ir->as_expression();
+ unsigned int i;
+
+ if (!expr)
+ return false;
+
+ for (i = 0; i < expr->get_num_operands(); i++) {
+ if (expr->operands[i]->type->is_matrix())
+ return true;
+ }
+
+ return false;
+}
+
+bool
+do_mat_op_to_vec(exec_list *instructions)
+{
+ ir_mat_op_to_vec_visitor v;
+
+ /* Pull out any matrix expression to a separate assignment to a
+ * temp. This will make our handling of the breakdown to
+ * operations on the matrix's vector components much easier.
+ */
+ do_expression_flattening(instructions, mat_op_to_vec_predicate);
+
+ visit_list_elements(&v, instructions);
+
+ return v.made_progress;
+}
+
+ir_rvalue *
+ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
+{
+ ir_dereference *deref;
+
+ deref = new(mem_ctx) ir_dereference_variable(var);
+
+ if (var->type->is_matrix()) {
+ deref = new(mem_ctx) ir_dereference_array(var,
+ new(mem_ctx) ir_constant(col));
+ } else {
+ assert(col == 0);
+ }
+
+ return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1);
+}
+
+ir_dereference *
+ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
+{
+ ir_dereference *deref;
+
+ if (!var->type->is_matrix()) {
+ deref = new(mem_ctx) ir_dereference_variable(var);
+ } else {
+ deref = new(mem_ctx) ir_dereference_variable(var);
+ deref = new(mem_ctx) ir_dereference_array(deref,
+ new(mem_ctx) ir_constant(row));
+ }
+
+ return deref;
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int b_col, i;
+ ir_assignment *assign;
+ ir_expression *expr;
+
+ for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
+ ir_rvalue *a = get_column(a_var, 0);
+ ir_rvalue *b = get_element(b_var, b_col, 0);
+
+ /* first column */
+ expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ a->type,
+ a,
+ b);
+
+ /* following columns */
+ for (i = 1; i < a_var->type->matrix_columns; i++) {
+ ir_expression *mul_expr;
+
+ a = get_column(a_var, i);
+ b = get_element(b_var, b_col, i);
+
+ mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ a->type,
+ a,
+ b);
+ expr = new(mem_ctx) ir_expression(ir_binop_add,
+ a->type,
+ expr,
+ mul_expr);
+ }
+
+ ir_rvalue *result = get_column(result_var, b_col);
+ assign = new(mem_ctx) ir_assignment(result,
+ expr,
+ NULL);
+ base_ir->insert_before(assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+ ir_rvalue *a = get_column(a_var, 0);
+ ir_rvalue *b = get_element(b_var, 0, 0);
+ ir_assignment *assign;
+ ir_expression *expr;
+
+ /* first column */
+ expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result_var->type,
+ a,
+ b);
+
+ /* following columns */
+ for (i = 1; i < a_var->type->matrix_columns; i++) {
+ ir_expression *mul_expr;
+
+ a = get_column(a_var, i);
+ b = get_element(b_var, 0, i);
+
+ mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result_var->type,
+ a,
+ b);
+ expr = new(mem_ctx) ir_expression(ir_binop_add,
+ result_var->type,
+ expr,
+ mul_expr);
+ }
+
+ ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var);
+ assign = new(mem_ctx) ir_assignment(result,
+ expr,
+ NULL);
+ base_ir->insert_before(assign);
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+
+ for (i = 0; i < b_var->type->matrix_columns; i++) {
+ ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var);
+ ir_rvalue *b = get_column(b_var, i);
+ ir_rvalue *result;
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ result = new(mem_ctx) ir_dereference_variable(result_var);
+ result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1);
+
+ column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
+ result->type,
+ a,
+ b);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL);
+ base_ir->insert_before(column_assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+
+ for (i = 0; i < a_var->type->matrix_columns; i++) {
+ ir_rvalue *a = get_column(a_var, i);
+ ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var);
+ ir_rvalue *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result->type,
+ a,
+ b);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL);
+ base_ir->insert_before(column_assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var,
+ bool test_equal)
+{
+ /* This essentially implements the following GLSL:
+ *
+ * bool equal(mat4 a, mat4 b)
+ * {
+ * return !any(bvec4(a[0] != b[0],
+ * a[1] != b[1],
+ * a[2] != b[2],
+ * a[3] != b[3]);
+ * }
+ *
+ * bool nequal(mat4 a, mat4 b)
+ * {
+ * return any(bvec4(a[0] != b[0],
+ * a[1] != b[1],
+ * a[2] != b[2],
+ * a[3] != b[3]);
+ * }
+ */
+ const unsigned columns = a_var->type->matrix_columns;
+ const glsl_type *const bvec_type =
+ glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
+
+ ir_variable *const tmp_bvec =
+ new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
+ ir_var_temporary);
+ this->base_ir->insert_before(tmp_bvec);
+
+ for (unsigned i = 0; i < columns; i++) {
+ ir_dereference *const op0 = get_column(a_var, i);
+ ir_dereference *const op1 = get_column(b_var, i);
+
+ ir_expression *const cmp =
+ new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
+ glsl_type::bool_type, op0, op1);
+
+ ir_dereference *const lhs =
+ new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+ ir_assignment *const assign =
+ new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
+
+ this->base_ir->insert_before(assign);
+ }
+
+ ir_rvalue *const val =
+ new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+ ir_expression *any =
+ new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type,
+ val, NULL);
+
+ if (test_equal)
+ any = new(this->mem_ctx) ir_expression(ir_unop_logic_not,
+ glsl_type::bool_type,
+ any, NULL);
+
+ ir_rvalue *const result =
+ new(this->mem_ctx) ir_dereference_variable(result_var);
+
+ ir_assignment *const assign =
+ new(mem_ctx) ir_assignment(result, any, NULL);
+ base_ir->insert_before(assign);
+}
+
+static bool
+has_matrix_operand(const ir_expression *expr, unsigned &columns)
+{
+ for (unsigned i = 0; i < expr->get_num_operands(); i++) {
+ if (expr->operands[i]->type->is_matrix()) {
+ columns = expr->operands[i]->type->matrix_columns;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+ir_visitor_status
+ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
+{
+ ir_expression *orig_expr = orig_assign->rhs->as_expression();
+ unsigned int i, matrix_columns = 1;
+ ir_variable *op_var[2];
+
+ if (!orig_expr)
+ return visit_continue;
+
+ if (!has_matrix_operand(orig_expr, matrix_columns))
+ return visit_continue;
+
+ mem_ctx = talloc_parent(orig_assign);
+
+ ir_dereference_variable *lhs_deref =
+ orig_assign->lhs->as_dereference_variable();
+ assert(lhs_deref);
+
+ ir_variable *result_var = lhs_deref->var;
+
+ /* Store the expression operands in temps so we can use them
+ * multiple times.
+ */
+ for (i = 0; i < orig_expr->get_num_operands(); i++) {
+ ir_assignment *assign;
+
+ op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
+ "mat_op_to_vec",
+ ir_var_temporary);
+ base_ir->insert_before(op_var[i]);
+
+ lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
+ assign = new(mem_ctx) ir_assignment(lhs_deref,
+ orig_expr->operands[i],
+ NULL);
+ base_ir->insert_before(assign);
+ }
+
+ /* OK, time to break down this matrix operation. */
+ switch (orig_expr->operation) {
+ case ir_unop_neg: {
+ const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
+ /* Apply the operation to each column.*/
+ for (i = 0; i < matrix_columns; i++) {
+ ir_rvalue *op0 = get_column(op_var[0], i);
+ ir_dereference *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
+ result->type,
+ op0,
+ NULL);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL,
+ mask);
+ assert(column_assign->write_mask != 0);
+ base_ir->insert_before(column_assign);
+ }
+ break;
+ }
+ case ir_binop_add:
+ case ir_binop_sub:
+ case ir_binop_div:
+ case ir_binop_mod: {
+ const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
+ /* For most operations, the matrix version is just going
+ * column-wise through and applying the operation to each column
+ * if available.
+ */
+ for (i = 0; i < matrix_columns; i++) {
+ ir_rvalue *op0 = get_column(op_var[0], i);
+ ir_rvalue *op1 = get_column(op_var[1], i);
+ ir_dereference *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
+ result->type,
+ op0,
+ op1);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL,
+ mask);
+ assert(column_assign->write_mask != 0);
+ base_ir->insert_before(column_assign);
+ }
+ break;
+ }
+ case ir_binop_mul:
+ if (op_var[0]->type->is_matrix()) {
+ if (op_var[1]->type->is_matrix()) {
+ do_mul_mat_mat(result_var, op_var[0], op_var[1]);
+ } else if (op_var[1]->type->is_vector()) {
+ do_mul_mat_vec(result_var, op_var[0], op_var[1]);
+ } else {
+ assert(op_var[1]->type->is_scalar());
+ do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
+ }
+ } else {
+ assert(op_var[1]->type->is_matrix());
+ if (op_var[0]->type->is_vector()) {
+ do_mul_vec_mat(result_var, op_var[0], op_var[1]);
+ } else {
+ assert(op_var[0]->type->is_scalar());
+ do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
+ }
+ }
+ break;
+
+ case ir_binop_all_equal:
+ case ir_binop_any_nequal:
+ do_equal_mat_mat(result_var, op_var[1], op_var[0],
+ (orig_expr->operation == ir_binop_all_equal));
+ break;
+
+ default:
+ printf("FINISHME: Handle matrix operation for %s\n",
+ orig_expr->operator_string());
+ abort();
+ }
+ orig_assign->remove();
+ this->made_progress = true;
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_mod_to_fract.cpp
+ *
+ * Breaks an ir_unop_mod expression down to (op1 * fract(op0 / op1))
+ *
+ * Many GPUs don't have a MOD instruction (945 and 965 included), and
+ * if we have to break it down like this anyway, it gives an
+ * opportunity to do things like constant fold the (1.0 / op1) easily.
+ */
+
+#include "ir.h"
+
+class ir_mod_to_fract_visitor : public ir_hierarchical_visitor {
+public:
+ ir_mod_to_fract_visitor()
+ {
+ this->made_progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_expression *);
+
+ bool made_progress;
+};
+
+bool
+do_mod_to_fract(exec_list *instructions)
+{
+ ir_mod_to_fract_visitor v;
+
+ visit_list_elements(&v, instructions);
+ return v.made_progress;
+}
+
+ir_visitor_status
+ir_mod_to_fract_visitor::visit_leave(ir_expression *ir)
+{
+ if (ir->operation != ir_binop_mod)
+ return visit_continue;
+
+ ir_variable *temp = new(ir) ir_variable(ir->operands[1]->type, "mod_b",
+ ir_var_temporary);
+ this->base_ir->insert_before(temp);
+
+ ir_assignment *assign;
+ ir_rvalue *expr;
+
+ assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(temp),
+ ir->operands[1], NULL);
+ this->base_ir->insert_before(assign);
+
+ expr = new(ir) ir_expression(ir_binop_div,
+ ir->operands[0]->type,
+ ir->operands[0],
+ new(ir) ir_dereference_variable(temp));
+
+ expr = new(ir) ir_expression(ir_unop_fract,
+ ir->operands[0]->type,
+ expr,
+ NULL);
+
+ ir->operation = ir_binop_mul;
+ ir->operands[0] = new(ir) ir_dereference_variable(temp);
+ ir->operands[1] = expr;
+ this->made_progress = true;
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_sub_to_add_neg.cpp
+ *
+ * Breaks an ir_binop_sub expression down to add(op0, neg(op1))
+ *
+ * This simplifies expression reassociation, and for many backends
+ * there is no subtract operation separate from adding the negation.
+ * For backends with native subtract operations, they will probably
+ * want to recognize add(op0, neg(op1)) or the other way around to
+ * produce a subtract anyway.
+ */
+
+#include "ir.h"
+
+class ir_sub_to_add_neg_visitor : public ir_hierarchical_visitor {
+public:
+ ir_sub_to_add_neg_visitor()
+ {
+ this->progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_expression *);
+
+ bool progress;
+};
+
+bool
+do_sub_to_add_neg(exec_list *instructions)
+{
+ ir_sub_to_add_neg_visitor v;
+
+ visit_list_elements(&v, instructions);
+ return v.progress;
+}
+
+ir_visitor_status
+ir_sub_to_add_neg_visitor::visit_leave(ir_expression *ir)
+{
+ if (ir->operation != ir_binop_sub)
+ return visit_continue;
+
+ void *mem_ctx = talloc_parent(ir);
+
+ ir->operation = ir_binop_add;
+ ir->operands[1] = new(mem_ctx) ir_expression(ir_unop_neg,
+ ir->operands[1]->type,
+ ir->operands[1],
+ NULL);
+
+ this->progress = true;
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_vec_index_to_cond_assign.cpp
+ *
+ * Turns indexing into vector types to a series of conditional moves
+ * of each channel's swizzle into a temporary.
+ *
+ * Most GPUs don't have a native way to do this operation, and this
+ * works around that. For drivers using both this pass and
+ * ir_vec_index_to_swizzle, there's a risk that this pass will happen
+ * before sufficient constant folding to find that the array index is
+ * constant. However, we hope that other optimization passes,
+ * particularly constant folding of assignment conditions and copy
+ * propagation, will result in the same code in the end.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+/**
+ * Visitor class for replacing expressions with ir_constant values.
+ */
+
+class ir_vec_index_to_cond_assign_visitor : public ir_hierarchical_visitor {
+public:
+ ir_vec_index_to_cond_assign_visitor()
+ {
+ progress = false;
+ }
+
+ ir_rvalue *convert_vec_index_to_cond_assign(ir_rvalue *val);
+
+ virtual ir_visitor_status visit_enter(ir_expression *);
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+ virtual ir_visitor_status visit_leave(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_return *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+ virtual ir_visitor_status visit_enter(ir_if *);
+
+ bool progress;
+};
+
+ir_rvalue *
+ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue *ir)
+{
+ ir_dereference_array *orig_deref = ir->as_dereference_array();
+ ir_assignment *assign;
+ ir_variable *index, *var;
+ ir_dereference *deref;
+ ir_expression *condition;
+ ir_swizzle *swizzle;
+ int i;
+
+ if (!orig_deref)
+ return ir;
+
+ if (orig_deref->array->type->is_matrix() ||
+ orig_deref->array->type->is_array())
+ return ir;
+
+ void *mem_ctx = talloc_parent(ir);
+
+ assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
+
+ /* Store the index to a temporary to avoid reusing its tree. */
+ index = new(base_ir) ir_variable(glsl_type::int_type,
+ "vec_index_tmp_i",
+ ir_var_temporary);
+ base_ir->insert_before(index);
+ deref = new(base_ir) ir_dereference_variable(index);
+ assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL);
+ base_ir->insert_before(assign);
+
+ /* Temporary where we store whichever value we swizzle out. */
+ var = new(base_ir) ir_variable(ir->type, "vec_index_tmp_v",
+ ir_var_temporary);
+ base_ir->insert_before(var);
+
+ /* Generate a conditional move of each vector element to the temp. */
+ for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
+ deref = new(base_ir) ir_dereference_variable(index);
+ condition = new(base_ir) ir_expression(ir_binop_equal,
+ glsl_type::bool_type,
+ deref,
+ new(base_ir) ir_constant(i));
+
+ /* Just clone the rest of the deref chain when trying to get at the
+ * underlying variable.
+ */
+ swizzle = new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
+ i, 0, 0, 0, 1);
+
+ deref = new(base_ir) ir_dereference_variable(var);
+ assign = new(base_ir) ir_assignment(deref, swizzle, condition);
+ base_ir->insert_before(assign);
+ }
+
+ this->progress = true;
+ return new(base_ir) ir_dereference_variable(var);
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_enter(ir_expression *ir)
+{
+ unsigned int i;
+
+ for (i = 0; i < ir->get_num_operands(); i++) {
+ ir->operands[i] = convert_vec_index_to_cond_assign(ir->operands[i]);
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_enter(ir_swizzle *ir)
+{
+ /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which
+ * the result of indexing a vector is. But maybe at some point we'll end up
+ * using swizzling of scalars for vector construction.
+ */
+ ir->val = convert_vec_index_to_cond_assign(ir->val);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir)
+{
+ ir_variable *index, *var;
+ ir_dereference_variable *deref;
+ ir_assignment *assign;
+ int i;
+
+ ir->rhs = convert_vec_index_to_cond_assign(ir->rhs);
+ if (ir->condition)
+ ir->condition = convert_vec_index_to_cond_assign(ir->condition);
+
+ /* Last, handle the LHS */
+ ir_dereference_array *orig_deref = ir->lhs->as_dereference_array();
+
+ if (!orig_deref ||
+ orig_deref->array->type->is_matrix() ||
+ orig_deref->array->type->is_array())
+ return visit_continue;
+
+ void *mem_ctx = talloc_parent(ir);
+
+ assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT);
+
+ /* Store the index to a temporary to avoid reusing its tree. */
+ index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i",
+ ir_var_temporary);
+ ir->insert_before(index);
+ deref = new(ir) ir_dereference_variable(index);
+ assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL);
+ ir->insert_before(assign);
+
+ /* Store the RHS to a temporary to avoid reusing its tree. */
+ var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v",
+ ir_var_temporary);
+ ir->insert_before(var);
+ deref = new(ir) ir_dereference_variable(var);
+ assign = new(ir) ir_assignment(deref, ir->rhs, NULL);
+ ir->insert_before(assign);
+
+ /* Generate a conditional move of each vector element to the temp. */
+ for (i = 0; i < orig_deref->array->type->vector_elements; i++) {
+ ir_rvalue *condition, *swizzle;
+
+ deref = new(ir) ir_dereference_variable(index);
+ condition = new(ir) ir_expression(ir_binop_equal,
+ glsl_type::bool_type,
+ deref,
+ new(ir) ir_constant(i));
+
+ /* Just clone the rest of the deref chain when trying to get at the
+ * underlying variable.
+ */
+ swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL),
+ i, 0, 0, 0, 1);
+
+ deref = new(ir) ir_dereference_variable(var);
+ assign = new(ir) ir_assignment(swizzle, deref, condition);
+ ir->insert_before(assign);
+ }
+ ir->remove();
+
+ this->progress = true;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_enter(ir_call *ir)
+{
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ ir_rvalue *new_param = convert_vec_index_to_cond_assign(param);
+
+ if (new_param != param) {
+ param->replace_with(new_param);
+ }
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_enter(ir_return *ir)
+{
+ if (ir->value) {
+ ir->value = convert_vec_index_to_cond_assign(ir->value);
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_cond_assign_visitor::visit_enter(ir_if *ir)
+{
+ ir->condition = convert_vec_index_to_cond_assign(ir->condition);
+
+ return visit_continue;
+}
+
+bool
+do_vec_index_to_cond_assign(exec_list *instructions)
+{
+ ir_vec_index_to_cond_assign_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_vec_index_to_swizzle.cpp
+ *
+ * Turns constant indexing into vector types to swizzles. This will
+ * let other swizzle-aware optimization passes catch these constructs,
+ * and codegen backends not have to worry about this case.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+/**
+ * Visitor class for replacing expressions with ir_constant values.
+ */
+
+class ir_vec_index_to_swizzle_visitor : public ir_hierarchical_visitor {
+public:
+ ir_vec_index_to_swizzle_visitor()
+ {
+ progress = false;
+ }
+
+ ir_rvalue *convert_vec_index_to_swizzle(ir_rvalue *val);
+
+ virtual ir_visitor_status visit_enter(ir_expression *);
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_return *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+ virtual ir_visitor_status visit_enter(ir_if *);
+
+ bool progress;
+};
+
+ir_rvalue *
+ir_vec_index_to_swizzle_visitor::convert_vec_index_to_swizzle(ir_rvalue *ir)
+{
+ ir_dereference_array *deref = ir->as_dereference_array();
+ ir_constant *ir_constant;
+
+ if (!deref)
+ return ir;
+
+ if (deref->array->type->is_matrix() || deref->array->type->is_array())
+ return ir;
+
+ assert(deref->array_index->type->base_type == GLSL_TYPE_INT);
+ ir_constant = deref->array_index->constant_expression_value();
+ if (!ir_constant)
+ return ir;
+
+ void *ctx = talloc_parent(ir);
+ this->progress = true;
+ return new(ctx) ir_swizzle(deref->array,
+ ir_constant->value.i[0], 0, 0, 0, 1);
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_expression *ir)
+{
+ unsigned int i;
+
+ for (i = 0; i < ir->get_num_operands(); i++) {
+ ir->operands[i] = convert_vec_index_to_swizzle(ir->operands[i]);
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_swizzle *ir)
+{
+ /* Can't be hit from normal GLSL, since you can't swizzle a scalar (which
+ * the result of indexing a vector is. But maybe at some point we'll end up
+ * using swizzling of scalars for vector construction.
+ */
+ ir->val = convert_vec_index_to_swizzle(ir->val);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_assignment *ir)
+{
+ ir->set_lhs(convert_vec_index_to_swizzle(ir->lhs));
+ ir->rhs = convert_vec_index_to_swizzle(ir->rhs);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_call *ir)
+{
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ ir_rvalue *new_param = convert_vec_index_to_swizzle(param);
+
+ if (new_param != param) {
+ param->replace_with(new_param);
+ }
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_return *ir)
+{
+ if (ir->value) {
+ ir->value = convert_vec_index_to_swizzle(ir->value);
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_vec_index_to_swizzle_visitor::visit_enter(ir_if *ir)
+{
+ ir->condition = convert_vec_index_to_swizzle(ir->condition);
+
+ return visit_continue;
+}
+
+bool
+do_vec_index_to_swizzle(exec_list *instructions)
+{
+ ir_vec_index_to_swizzle_visitor v;
+
+ v.run(instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_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 "glsl_types.h"
+
+/**
+ * Visitor class for replacing expressions with ir_constant values.
+ */
+
+class ir_algebraic_visitor : public ir_rvalue_visitor {
+public:
+ ir_algebraic_visitor()
+ {
+ this->progress = false;
+ this->mem_ctx = NULL;
+ }
+
+ virtual ~ir_algebraic_visitor()
+ {
+ }
+
+ 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);
+
+ void *mem_ctx;
+
+ bool progress;
+};
+
+static bool
+is_vec_zero(ir_constant *ir)
+{
+ int c;
+
+ if (!ir)
+ return false;
+ if (!ir->type->is_scalar() &&
+ !ir->type->is_vector())
+ return false;
+
+ for (c = 0; c < ir->type->vector_elements; c++) {
+ switch (ir->type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ if (ir->value.f[c] != 0.0)
+ return false;
+ break;
+ case GLSL_TYPE_INT:
+ if (ir->value.i[c] != 0)
+ return false;
+ break;
+ case GLSL_TYPE_UINT:
+ if (ir->value.u[c] != 0)
+ return false;
+ break;
+ case GLSL_TYPE_BOOL:
+ if (ir->value.b[c] != false)
+ return false;
+ break;
+ default:
+ assert(!"bad base type");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+is_vec_one(ir_constant *ir)
+{
+ int c;
+
+ if (!ir)
+ return false;
+ if (!ir->type->is_scalar() &&
+ !ir->type->is_vector())
+ return false;
+
+ for (c = 0; c < ir->type->vector_elements; c++) {
+ switch (ir->type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ if (ir->value.f[c] != 1.0)
+ return false;
+ break;
+ case GLSL_TYPE_INT:
+ if (ir->value.i[c] != 1)
+ return false;
+ break;
+ case GLSL_TYPE_UINT:
+ if (ir->value.u[c] != 1)
+ return false;
+ break;
+ case GLSL_TYPE_BOOL:
+ if (ir->value.b[c] != true)
+ return false;
+ break;
+ default:
+ assert(!"bad base type");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+update_type(ir_expression *ir)
+{
+ if (ir->operands[0]->type->is_vector())
+ 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 send 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 (ir1->operands[0]->type->is_matrix() ||
+ ir1->operands[0]->type->is_matrix() ||
+ ir2->operands[1]->type->is_matrix() ||
+ ir2->operands[1]->type->is_matrix())
+ return false;
+
+ ir_constant *ir2_const[2];
+ ir2_const[0] = ir2->operands[0]->constant_expression_value();
+ ir2_const[1] = ir2->operands[1]->constant_expression_value();
+
+ 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 (expr->type->is_vector() && operand->type->is_scalar()) {
+ 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[2] = {NULL, NULL};
+ ir_expression *op_expr[2] = {NULL, NULL};
+ ir_expression *temp;
+ unsigned int i;
+
+ for (i = 0; i < ir->get_num_operands(); i++) {
+ if (ir->operands[i]->type->is_matrix())
+ return ir;
+
+ op_const[i] = ir->operands[i]->constant_expression_value();
+ op_expr[i] = ir->operands[i]->as_expression();
+ }
+
+ if (this->mem_ctx == NULL)
+ this->mem_ctx = talloc_parent(ir);
+
+ switch (ir->operation) {
+ case ir_unop_logic_not: {
+ enum ir_expression_operation new_op = ir_unop_logic_not;
+
+ if (op_expr[0] == NULL)
+ break;
+
+ switch (op_expr[0]->operation) {
+ case ir_binop_less: new_op = ir_binop_gequal; break;
+ case ir_binop_greater: new_op = ir_binop_lequal; break;
+ case ir_binop_lequal: new_op = ir_binop_greater; break;
+ case ir_binop_gequal: new_op = ir_binop_less; break;
+ case ir_binop_equal: new_op = ir_binop_nequal; break;
+ case ir_binop_nequal: new_op = ir_binop_equal; break;
+ case ir_binop_all_equal: new_op = ir_binop_any_nequal; break;
+ case ir_binop_any_nequal: new_op = ir_binop_all_equal; break;
+
+ default:
+ /* The default case handler is here to silence a warning from GCC.
+ */
+ break;
+ }
+
+ if (new_op != ir_unop_logic_not) {
+ this->progress = true;
+ return new(mem_ctx) ir_expression(new_op,
+ ir->type,
+ op_expr[0]->operands[0],
+ op_expr[0]->operands[1]);
+ }
+
+ break;
+ }
+
+ case ir_binop_add:
+ if (is_vec_zero(op_const[0])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[1]);
+ }
+ if (is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[0]);
+ }
+
+ /* 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],
+ ir->operands[1]->as_expression());
+ if (op_const[1] && !op_const[0])
+ reassociate_constant(ir, 1, op_const[1],
+ ir->operands[0]->as_expression());
+ break;
+
+ case ir_binop_sub:
+ if (is_vec_zero(op_const[0])) {
+ this->progress = true;
+ temp = new(mem_ctx) ir_expression(ir_unop_neg,
+ ir->operands[1]->type,
+ ir->operands[1],
+ NULL);
+ return swizzle_if_required(ir, temp);
+ }
+ if (is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[0]);
+ }
+ break;
+
+ case ir_binop_mul:
+ if (is_vec_one(op_const[0])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[1]);
+ }
+ if (is_vec_one(op_const[1])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[0]);
+ }
+
+ if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return ir_constant::zero(ir, ir->type);
+ }
+
+ /* 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],
+ ir->operands[1]->as_expression());
+ if (op_const[1] && !op_const[0])
+ reassociate_constant(ir, 1, op_const[1],
+ ir->operands[0]->as_expression());
+
+ break;
+
+ case ir_binop_div:
+ if (is_vec_one(op_const[0]) && ir->type->base_type == GLSL_TYPE_FLOAT) {
+ this->progress = true;
+ temp = new(mem_ctx) ir_expression(ir_unop_rcp,
+ ir->operands[1]->type,
+ ir->operands[1],
+ NULL);
+ return swizzle_if_required(ir, temp);
+ }
+ if (is_vec_one(op_const[1])) {
+ this->progress = true;
+ return swizzle_if_required(ir, ir->operands[0]);
+ }
+ break;
+
+ case ir_binop_logic_and:
+ /* FINISHME: Also simplify (a && a) to (a). */
+ if (is_vec_one(op_const[0])) {
+ this->progress = true;
+ return ir->operands[1];
+ } else if (is_vec_one(op_const[1])) {
+ this->progress = true;
+ return ir->operands[0];
+ } else if (is_vec_zero(op_const[0]) || is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return ir_constant::zero(mem_ctx, ir->type);
+ }
+ break;
+
+ case ir_binop_logic_xor:
+ /* FINISHME: Also simplify (a ^^ a) to (false). */
+ if (is_vec_zero(op_const[0])) {
+ this->progress = true;
+ return ir->operands[1];
+ } else if (is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return ir->operands[0];
+ } else if (is_vec_one(op_const[0])) {
+ this->progress = true;
+ return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
+ ir->operands[1], NULL);
+ } else if (is_vec_one(op_const[1])) {
+ this->progress = true;
+ return new(mem_ctx) ir_expression(ir_unop_logic_not, ir->type,
+ ir->operands[0], NULL);
+ }
+ break;
+
+ case ir_binop_logic_or:
+ /* FINISHME: Also simplify (a || a) to (a). */
+ if (is_vec_zero(op_const[0])) {
+ this->progress = true;
+ return ir->operands[1];
+ } else if (is_vec_zero(op_const[1])) {
+ this->progress = true;
+ return ir->operands[0];
+ } else if (is_vec_one(op_const[0]) || is_vec_one(op_const[1])) {
+ ir_constant_data data;
+
+ for (unsigned i = 0; i < 16; i++)
+ data.b[i] = true;
+
+ this->progress = true;
+ return new(mem_ctx) ir_constant(ir->type, &data);
+ }
+ break;
+
+ case ir_unop_rcp:
+ if (op_expr[0] && op_expr[0]->operation == ir_unop_rcp) {
+ this->progress = true;
+ return op_expr[0]->operands[0];
+ }
+
+ /* FINISHME: We should do rcp(rsq(x)) -> sqrt(x) for some
+ * backends, except that some backends will have done sqrt ->
+ * rcp(rsq(x)) and we don't want to undo it for them.
+ */
+
+ /* As far as we know, all backends are OK with rsq. */
+ if (op_expr[0] && op_expr[0]->operation == ir_unop_sqrt) {
+ this->progress = true;
+ temp = new(mem_ctx) ir_expression(ir_unop_rsq,
+ op_expr[0]->operands[0]->type,
+ op_expr[0]->operands[0],
+ NULL);
+ return swizzle_if_required(ir, temp);
+ }
+
+ 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)
+ return;
+
+ *rvalue = handle_expression(expr);
+}
+
+bool
+do_algebraic(exec_list *instructions)
+{
+ ir_algebraic_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_constant_folding.cpp
+ * Replace constant-valued expressions with references to constant values.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_rvalue_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+/**
+ * Visitor class for replacing expressions with ir_constant values.
+ */
+
+class ir_constant_folding_visitor : public ir_rvalue_visitor {
+public:
+ ir_constant_folding_visitor()
+ {
+ this->progress = false;
+ }
+
+ virtual ~ir_constant_folding_visitor()
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_assignment *ir);
+ virtual ir_visitor_status visit_enter(ir_call *ir);
+
+ virtual void handle_rvalue(ir_rvalue **rvalue);
+
+ bool progress;
+};
+
+void
+ir_constant_folding_visitor::handle_rvalue(ir_rvalue **rvalue)
+{
+ if (*rvalue == NULL || (*rvalue)->ir_type == ir_type_constant)
+ return;
+
+ /* Note that we do rvalue visitoring on leaving. So if an
+ * expression has a non-constant operand, no need to go looking
+ * down it to find if it's constant. This cuts the time of this
+ * pass down drastically.
+ */
+ ir_expression *expr = (*rvalue)->as_expression();
+ if (expr) {
+ for (unsigned int i = 0; i < expr->get_num_operands(); i++) {
+ if (!expr->operands[i]->as_constant())
+ return;
+ }
+ }
+
+ ir_constant *constant = (*rvalue)->constant_expression_value();
+ if (constant) {
+ *rvalue = constant;
+ this->progress = true;
+ } else {
+ (*rvalue)->accept(this);
+ }
+}
+
+ir_visitor_status
+ir_constant_folding_visitor::visit_enter(ir_assignment *ir)
+{
+ ir->rhs->accept(this);
+ handle_rvalue(&ir->rhs);
+
+ if (ir->condition) {
+ ir->condition->accept(this);
+ handle_rvalue(&ir->condition);
+
+ ir_constant *const_val = ir->condition->as_constant();
+ /* If the condition is constant, either remove the condition or
+ * remove the never-executed assignment.
+ */
+ if (const_val) {
+ if (const_val->value.b[0])
+ ir->condition = NULL;
+ else
+ ir->remove();
+ this->progress = true;
+ }
+ }
+
+ /* Don't descend into the LHS because we want it to stay as a
+ * variable dereference. FINISHME: We probably should to get array
+ * indices though.
+ */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_folding_visitor::visit_enter(ir_call *ir)
+{
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *sig_param = (ir_variable *)sig_iter.get();
+
+ if (sig_param->mode == ir_var_in) {
+ ir_rvalue *new_param = param_rval;
+
+ handle_rvalue(&new_param);
+ if (new_param != param_rval) {
+ param_rval->replace_with(new_param);
+ }
+ }
+ sig_iter.next();
+ }
+
+ return visit_continue_with_parent;
+}
+
+bool
+do_constant_folding(exec_list *instructions)
+{
+ ir_constant_folding_visitor constant_folding;
+
+ visit_list_elements(&constant_folding, instructions);
+
+ return constant_folding.progress;
+}
--- /dev/null
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * constant of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, constant, 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 constantright 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 CONSTANTRIGHT 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 ir_constant_propagation.cpp
+ *
+ * Tracks assignments of constants to channels of variables, and
+ * usage of those constant channels with direct usage of the constants.
+ *
+ * This can lead to constant folding and algebraic optimizations in
+ * those later expressions, while causing no increase in instruction
+ * count (due to constants being generally free to load from a
+ * constant push buffer or as instruction immediate values) and
+ * possibly reducing register pressure.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_rvalue_visitor.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+class acp_entry : public exec_node
+{
+public:
+ acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant)
+ {
+ assert(var);
+ assert(constant);
+ this->var = var;
+ this->write_mask = write_mask;
+ this->constant = constant;
+ }
+
+ ir_variable *var;
+ ir_constant *constant;
+ unsigned write_mask;
+};
+
+
+class kill_entry : public exec_node
+{
+public:
+ kill_entry(ir_variable *var, unsigned write_mask)
+ {
+ assert(var);
+ this->var = var;
+ this->write_mask = write_mask;
+ }
+
+ ir_variable *var;
+ unsigned write_mask;
+};
+
+class ir_constant_propagation_visitor : public ir_rvalue_visitor {
+public:
+ ir_constant_propagation_visitor()
+ {
+ progress = false;
+ mem_ctx = talloc_new(0);
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ }
+ ~ir_constant_propagation_visitor()
+ {
+ talloc_free(mem_ctx);
+ }
+
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_leave(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
+
+ void add_constant(ir_assignment *ir);
+ void kill(ir_variable *ir, unsigned write_mask);
+ void handle_if_block(exec_list *instructions);
+ void handle_rvalue(ir_rvalue **rvalue);
+
+ /** List of acp_entry: The available constants to propagate */
+ exec_list *acp;
+
+ /**
+ * List of kill_entry: The masks of variables whose values were
+ * killed in this block.
+ */
+ exec_list *kills;
+
+ bool progress;
+
+ bool killed_all;
+
+ void *mem_ctx;
+};
+
+
+void
+ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue)
+{
+ if (this->in_assignee || !*rvalue)
+ return;
+
+ const glsl_type *type = (*rvalue)->type;
+ if (!type->is_scalar() && !type->is_vector())
+ return;
+
+ ir_swizzle *swiz = NULL;
+ ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
+ if (!deref) {
+ swiz = (*rvalue)->as_swizzle();
+ if (!swiz)
+ return;
+
+ deref = swiz->val->as_dereference_variable();
+ if (!deref)
+ return;
+ }
+
+ ir_constant_data data;
+ memset(&data, 0, sizeof(data));
+
+ for (unsigned int i = 0; i < type->components(); i++) {
+ int channel;
+ acp_entry *found = NULL;
+
+ if (swiz) {
+ switch (i) {
+ case 0: channel = swiz->mask.x; break;
+ case 1: channel = swiz->mask.y; break;
+ case 2: channel = swiz->mask.z; break;
+ case 3: channel = swiz->mask.w; break;
+ default: assert(!"shouldn't be reached"); channel = 0; break;
+ }
+ } else {
+ channel = i;
+ }
+
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+ if (entry->var == deref->var && entry->write_mask & (1 << channel)) {
+ found = entry;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ int rhs_channel = 0;
+ for (int j = 0; j < 4; j++) {
+ if (j == channel)
+ break;
+ if (found->write_mask & (1 << j))
+ rhs_channel++;
+ }
+
+ switch (type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ data.f[i] = found->constant->value.f[rhs_channel];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[i] = found->constant->value.i[rhs_channel];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[i] = found->constant->value.u[rhs_channel];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[i] = found->constant->value.b[rhs_channel];
+ break;
+ default:
+ assert(!"not reached");
+ break;
+ }
+ }
+
+ *rvalue = new(talloc_parent(deref)) ir_constant(type, &data);
+ this->progress = true;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)
+{
+ /* Treat entry into a function signature as a completely separate
+ * block. Any instructions at global scope will be shuffled into
+ * main() at link time, so they're irrelevant to us.
+ */
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body);
+
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = orig_killed_all;
+
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_leave(ir_assignment *ir)
+{
+ if (this->in_assignee)
+ return visit_continue;
+
+ kill(ir->lhs->variable_referenced(), ir->write_mask);
+
+ add_constant(ir);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_function *ir)
+{
+ (void) ir;
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_call *ir)
+{
+ /* Do constant propagation on call parameters, but skip any out params */
+ exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
+ ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
+ ir_rvalue *new_param = param;
+ handle_rvalue(&new_param);
+ if (new_param != param)
+ param->replace_with(new_param);
+ else
+ param->accept(this);
+ }
+ sig_param_iter.next();
+ }
+
+ /* Since we're unlinked, we don't (necssarily) know the side effects of
+ * this call. So kill all copies.
+ */
+ acp->make_empty();
+ this->killed_all = true;
+
+ return visit_continue_with_parent;
+}
+
+void
+ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ /* Populate the initial acp with a constant of the original */
+ foreach_iter(exec_list_iterator, iter, *orig_acp) {
+ acp_entry *a = (acp_entry *)iter.get();
+ this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask,
+ a->constant));
+ }
+
+ visit_list_elements(this, instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var, k->write_mask);
+ }
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_if *ir)
+{
+ ir->condition->accept(this);
+ handle_rvalue(&ir->condition);
+
+ handle_if_block(&ir->then_instructions);
+ handle_if_block(&ir->else_instructions);
+
+ /* handle_if_block() already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_loop *ir)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ /* FINISHME: For now, the initial acp for loops is totally empty.
+ * We could go through once, then go through again with the acp
+ * cloned minus the killed entries after the first run through.
+ */
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body_instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var, k->write_mask);
+ }
+
+ /* already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+void
+ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask)
+{
+ assert(var != NULL);
+
+ /* We don't track non-vectors. */
+ if (!var->type->is_vector() && !var->type->is_scalar())
+ return;
+
+ /* Remove any entries currently in the ACP for this kill. */
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+
+ if (entry->var == var) {
+ entry->write_mask &= ~write_mask;
+ if (entry->write_mask == 0)
+ entry->remove();
+ }
+ }
+
+ /* Add this writemask of the variable to the list of killed
+ * variables in this block.
+ */
+ foreach_iter(exec_list_iterator, iter, *this->kills) {
+ kill_entry *entry = (kill_entry *)iter.get();
+
+ if (entry->var == var) {
+ entry->write_mask |= write_mask;
+ return;
+ }
+ }
+ /* Not already in the list. Make new entry. */
+ this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask));
+}
+
+/**
+ * Adds an entry to the available constant list if it's a plain assignment
+ * of a variable to a variable.
+ */
+void
+ir_constant_propagation_visitor::add_constant(ir_assignment *ir)
+{
+ acp_entry *entry;
+
+ if (ir->condition) {
+ ir_constant *condition = ir->condition->as_constant();
+ if (!condition || !condition->value.b[0])
+ return;
+ }
+
+ if (!ir->write_mask)
+ return;
+
+ ir_dereference_variable *deref = ir->lhs->as_dereference_variable();
+ ir_constant *constant = ir->rhs->as_constant();
+
+ if (!deref || !constant)
+ return;
+
+ /* Only do constant propagation on vectors. Constant matrices,
+ * arrays, or structures would require more work elsewhere.
+ */
+ if (!deref->var->type->is_vector() && !deref->var->type->is_scalar())
+ return;
+
+ entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant);
+ this->acp->push_tail(entry);
+}
+
+/**
+ * Does a constant propagation pass on the code present in the instruction stream.
+ */
+bool
+do_constant_propagation(exec_list *instructions)
+{
+ ir_constant_propagation_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_constant_variable.cpp
+ *
+ * Marks variables assigned a single constant value over the course
+ * of the program as constant.
+ *
+ * The goal here is to trigger further constant folding and then dead
+ * code elimination. This is common with vector/matrix constructors
+ * and calls to builtin functions.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+struct assignment_entry {
+ exec_node link;
+ int assignment_count;
+ ir_variable *var;
+ ir_constant *constval;
+ bool our_scope;
+};
+
+class ir_constant_variable_visitor : public ir_hierarchical_visitor {
+public:
+ virtual ir_visitor_status visit_enter(ir_dereference_variable *);
+ virtual ir_visitor_status visit(ir_variable *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+
+ exec_list list;
+};
+
+static struct assignment_entry *
+get_assignment_entry(ir_variable *var, exec_list *list)
+{
+ struct assignment_entry *entry;
+
+ foreach_list_typed(struct assignment_entry, entry, link, list) {
+ if (entry->var == var)
+ return entry;
+ }
+
+ entry = (struct assignment_entry *)calloc(1, sizeof(*entry));
+ entry->var = var;
+ list->push_head(&entry->link);
+ return entry;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit(ir_variable *ir)
+{
+ struct assignment_entry *entry = get_assignment_entry(ir, &this->list);
+ entry->our_scope = true;
+ return visit_continue;
+}
+
+/* Skip derefs of variables so that we can detect declarations. */
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
+{
+ (void)ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
+{
+ ir_constant *constval;
+ struct assignment_entry *entry;
+
+ entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list);
+ assert(entry);
+ entry->assignment_count++;
+
+ /* If it's already constant, don't do the work. */
+ if (entry->var->constant_value)
+ return visit_continue;
+
+ /* OK, now find if we actually have all the right conditions for
+ * this to be a constant value assigned to the var.
+ */
+ if (ir->condition) {
+ constval = ir->condition->constant_expression_value();
+ if (!constval || !constval->value.b[0])
+ return visit_continue;
+ }
+
+ ir_variable *var = ir->whole_variable_written();
+ if (!var)
+ return visit_continue;
+
+ constval = ir->rhs->constant_expression_value();
+ if (!constval)
+ return visit_continue;
+
+ /* Mark this entry as having a constant assignment (if the
+ * assignment count doesn't go >1). do_constant_variable will fix
+ * up the variable with the constant value later.
+ */
+ entry->constval = constval;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_call *ir)
+{
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *param = (ir_variable *)sig_iter.get();
+
+ if (param->mode == ir_var_out ||
+ param->mode == ir_var_inout) {
+ ir_variable *var = param_rval->variable_referenced();
+ struct assignment_entry *entry;
+
+ assert(var);
+ entry = get_assignment_entry(var, &this->list);
+ entry->assignment_count++;
+ }
+ sig_iter.next();
+ }
+ return visit_continue;
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_constant_variable(exec_list *instructions)
+{
+ bool progress = false;
+ ir_constant_variable_visitor v;
+
+ v.run(instructions);
+
+ while (!v.list.is_empty()) {
+
+ struct assignment_entry *entry;
+ entry = exec_node_data(struct assignment_entry, v.list.head, link);
+
+ if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
+ entry->var->constant_value = entry->constval;
+ progress = true;
+ }
+ entry->link.remove();
+ free(entry);
+ }
+
+ return progress;
+}
+
+bool
+do_constant_variable_unlinked(exec_list *instructions)
+{
+ bool progress = false;
+
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_function *f = ir->as_function();
+ if (f) {
+ foreach_iter(exec_list_iterator, sigiter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) sigiter.get();
+ if (do_constant_variable(&sig->body))
+ progress = true;
+ }
+ }
+ }
+
+ return progress;
+}
--- /dev/null
+/*
+ * 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 ir_copy_propagation.cpp
+ *
+ * Moves usage of recently-copied variables to the previous copy of
+ * the variable.
+ *
+ * This should reduce the number of MOV instructions in the generated
+ * programs unless copy propagation is also done on the LIR, and may
+ * help anyway by triggering other optimizations that live in the HIR.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+class acp_entry : public exec_node
+{
+public:
+ acp_entry(ir_variable *lhs, ir_variable *rhs)
+ {
+ assert(lhs);
+ assert(rhs);
+ this->lhs = lhs;
+ this->rhs = rhs;
+ }
+
+ ir_variable *lhs;
+ ir_variable *rhs;
+};
+
+
+class kill_entry : public exec_node
+{
+public:
+ kill_entry(ir_variable *var)
+ {
+ assert(var);
+ this->var = var;
+ }
+
+ ir_variable *var;
+};
+
+class ir_copy_propagation_visitor : public ir_hierarchical_visitor {
+public:
+ ir_copy_propagation_visitor()
+ {
+ progress = false;
+ mem_ctx = talloc_new(0);
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ }
+ ~ir_copy_propagation_visitor()
+ {
+ talloc_free(mem_ctx);
+ }
+
+ virtual ir_visitor_status visit(class ir_dereference_variable *);
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_leave(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
+
+ void add_copy(ir_assignment *ir);
+ void kill(ir_variable *ir);
+ void handle_if_block(exec_list *instructions);
+
+ /** List of acp_entry: The available copies to propagate */
+ exec_list *acp;
+ /**
+ * List of kill_entry: The variables whose values were killed in this
+ * block.
+ */
+ exec_list *kills;
+
+ bool progress;
+
+ bool killed_all;
+
+ void *mem_ctx;
+};
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function_signature *ir)
+{
+ /* Treat entry into a function signature as a completely separate
+ * block. Any instructions at global scope will be shuffled into
+ * main() at link time, so they're irrelevant to us.
+ */
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body);
+
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = orig_killed_all;
+
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_leave(ir_assignment *ir)
+{
+ kill(ir->lhs->variable_referenced());
+
+ add_copy(ir);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_function *ir)
+{
+ (void) ir;
+ return visit_continue;
+}
+
+/**
+ * Replaces dereferences of ACP RHS variables with ACP LHS variables.
+ *
+ * This is where the actual copy propagation occurs. Note that the
+ * rewriting of ir_dereference means that the ir_dereference instance
+ * must not be shared by multiple IR operations!
+ */
+ir_visitor_status
+ir_copy_propagation_visitor::visit(ir_dereference_variable *ir)
+{
+ if (this->in_assignee)
+ return visit_continue;
+
+ ir_variable *var = ir->var;
+
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+
+ if (var == entry->lhs) {
+ ir->var = entry->rhs;
+ this->progress = true;
+ break;
+ }
+ }
+
+ return visit_continue;
+}
+
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_call *ir)
+{
+ /* Do copy propagation on call parameters, but skip any out params */
+ exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
+ ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
+ ir->accept(this);
+ }
+ sig_param_iter.next();
+ }
+
+ /* Since we're unlinked, we don't (necssarily) know the side effects of
+ * this call. So kill all copies.
+ */
+ acp->make_empty();
+ this->killed_all = true;
+
+ return visit_continue_with_parent;
+}
+
+void
+ir_copy_propagation_visitor::handle_if_block(exec_list *instructions)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ /* Populate the initial acp with a copy of the original */
+ foreach_iter(exec_list_iterator, iter, *orig_acp) {
+ acp_entry *a = (acp_entry *)iter.get();
+ this->acp->push_tail(new(this->mem_ctx) acp_entry(a->lhs, a->rhs));
+ }
+
+ visit_list_elements(this, instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var);
+ }
+}
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_if *ir)
+{
+ ir->condition->accept(this);
+
+ handle_if_block(&ir->then_instructions);
+ handle_if_block(&ir->else_instructions);
+
+ /* handle_if_block() already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_copy_propagation_visitor::visit_enter(ir_loop *ir)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ /* FINISHME: For now, the initial acp for loops is totally empty.
+ * We could go through once, then go through again with the acp
+ * cloned minus the killed entries after the first run through.
+ */
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body_instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var);
+ }
+
+ /* already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+void
+ir_copy_propagation_visitor::kill(ir_variable *var)
+{
+ assert(var != NULL);
+
+ /* Remove any entries currently in the ACP for this kill. */
+ foreach_iter(exec_list_iterator, iter, *acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+
+ if (entry->lhs == var || entry->rhs == var) {
+ entry->remove();
+ }
+ }
+
+ /* Add the LHS variable to the list of killed variables in this block.
+ */
+ this->kills->push_tail(new(this->mem_ctx) kill_entry(var));
+}
+
+/**
+ * Adds an entry to the available copy list if it's a plain assignment
+ * of a variable to a variable.
+ */
+void
+ir_copy_propagation_visitor::add_copy(ir_assignment *ir)
+{
+ acp_entry *entry;
+
+ if (ir->condition) {
+ ir_constant *condition = ir->condition->as_constant();
+ if (!condition || !condition->value.b[0])
+ return;
+ }
+
+ ir_variable *lhs_var = ir->whole_variable_written();
+ ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
+
+ if ((lhs_var != NULL) && (rhs_var != NULL)) {
+ if (lhs_var == rhs_var) {
+ /* This is a dumb assignment, but we've conveniently noticed
+ * it here. Removing it now would mess up the loop iteration
+ * calling us. Just flag it to not execute, and someone else
+ * will clean up the mess.
+ */
+ ir->condition = new(talloc_parent(ir)) ir_constant(false);
+ this->progress = true;
+ } else {
+ entry = new(this->mem_ctx) acp_entry(lhs_var, rhs_var);
+ this->acp->push_tail(entry);
+ }
+ }
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_copy_propagation(exec_list *instructions)
+{
+ ir_copy_propagation_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_dead_code.cpp
+ *
+ * Eliminates dead assignments and variable declarations from the code.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_variable_refcount.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+/**
+ * Do a dead code pass over instructions and everything that instructions
+ * references.
+ *
+ * Note that this will remove assignments to globals, so it is not suitable
+ * for usage on an unlinked instruction stream.
+ */
+bool
+do_dead_code(exec_list *instructions)
+{
+ ir_variable_refcount_visitor v;
+ bool progress = false;
+
+ v.run(instructions);
+
+ foreach_iter(exec_list_iterator, iter, v.variable_list) {
+ variable_entry *entry = (variable_entry *)iter.get();
+
+ /* Since each assignment is a reference, the refereneced count must be
+ * greater than or equal to the assignment count. If they are equal,
+ * then all of the references are assignments, and the variable is
+ * dead.
+ *
+ * Note that if the variable is neither assigned nor referenced, both
+ * counts will be zero and will be caught by the equality test.
+ */
+ assert(entry->referenced_count >= entry->assigned_count);
+
+ if (debug) {
+ printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n",
+ entry->var->name, (void *) entry->var,
+ entry->referenced_count, entry->assigned_count,
+ entry->declaration ? "" : "not ");
+ }
+
+ if ((entry->referenced_count > entry->assigned_count)
+ || !entry->declaration)
+ continue;
+
+ if (entry->assign) {
+ /* Remove a single dead assignment to the variable we found.
+ * Don't do so if it's a shader output, though.
+ */
+ if (entry->var->mode != ir_var_out &&
+ entry->var->mode != ir_var_inout &&
+ !ir_has_call(entry->assign)) {
+ entry->assign->remove();
+ progress = true;
+
+ if (debug) {
+ printf("Removed assignment to %s@%p\n",
+ entry->var->name, (void *) entry->var);
+ }
+ }
+ } else {
+ /* If there are no assignments or references to the variable left,
+ * then we can remove its declaration.
+ */
+
+ /* uniform initializers are precious, and could get used by another
+ * stage.
+ */
+ if (entry->var->mode == ir_var_uniform &&
+ entry->var->constant_value)
+ continue;
+
+ entry->var->remove();
+ progress = true;
+
+ if (debug) {
+ printf("Removed declaration of %s@%p\n",
+ entry->var->name, (void *) entry->var);
+ }
+ }
+ }
+
+ return progress;
+}
+
+/**
+ * Does a dead code pass on the functions present in the instruction stream.
+ *
+ * This is suitable for use while the program is not linked, as it will
+ * ignore variable declarations (and the assignments to them) for variables
+ * with global scope.
+ */
+bool
+do_dead_code_unlinked(exec_list *instructions)
+{
+ bool progress = false;
+
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_function *f = ir->as_function();
+ if (f) {
+ foreach_iter(exec_list_iterator, sigiter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) sigiter.get();
+ if (do_dead_code(&sig->body))
+ progress = true;
+ }
+ }
+ }
+
+ return progress;
+}
--- /dev/null
+/*
+ * 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 ir_dead_code_local.cpp
+ *
+ * Eliminates local dead assignments from the code.
+ *
+ * This operates on basic blocks, tracking assignments and finding if
+ * they're used before the variable is completely reassigned.
+ *
+ * Compare this to ir_dead_code.cpp, which operates globally looking
+ * for assignments to variables that are never read.
+ */
+
+#include "ir.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+class assignment_entry : public exec_node
+{
+public:
+ assignment_entry(ir_variable *lhs, ir_instruction *ir)
+ {
+ assert(lhs);
+ assert(ir);
+ this->lhs = lhs;
+ this->ir = ir;
+ }
+
+ ir_variable *lhs;
+ ir_instruction *ir;
+};
+
+class kill_for_derefs_visitor : public ir_hierarchical_visitor {
+public:
+ kill_for_derefs_visitor(exec_list *assignments)
+ {
+ this->assignments = assignments;
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ ir_variable *const var = ir->variable_referenced();
+
+ foreach_iter(exec_list_iterator, iter, *this->assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ if (entry->lhs == var) {
+ if (debug)
+ printf("kill %s\n", entry->lhs->name);
+ entry->remove();
+ }
+ }
+
+ return visit_continue;
+ }
+
+private:
+ exec_list *assignments;
+};
+
+class array_index_visit : public ir_hierarchical_visitor {
+public:
+ array_index_visit(ir_hierarchical_visitor *v)
+ {
+ this->visitor = v;
+ }
+
+ virtual ir_visitor_status visit_enter(class ir_dereference_array *ir)
+ {
+ ir->array_index->accept(visitor);
+ return visit_continue;
+ }
+
+ static void run(ir_instruction *ir, ir_hierarchical_visitor *v)
+ {
+ array_index_visit top_visit(v);
+ ir->accept(& top_visit);
+ }
+
+ ir_hierarchical_visitor *visitor;
+};
+
+
+/**
+ * Adds an entry to the available copy list if it's a plain assignment
+ * of a variable to a variable.
+ */
+static bool
+process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments)
+{
+ ir_variable *var = NULL;
+ bool progress = false;
+ kill_for_derefs_visitor v(assignments);
+
+ /* Kill assignment entries for things used to produce this assignment. */
+ ir->rhs->accept(&v);
+ if (ir->condition) {
+ ir->condition->accept(&v);
+ }
+
+ /* Kill assignment enties used as array indices.
+ */
+ array_index_visit::run(ir->lhs, &v);
+ var = ir->lhs->variable_referenced();
+ assert(var);
+
+ bool always_assign = true;
+ if (ir->condition) {
+ ir_constant *condition = ir->condition->as_constant();
+ if (!condition || !condition->value.b[0])
+ always_assign = false;
+ }
+
+ /* Now, check if we did a whole-variable assignment. */
+ if (always_assign && (ir->whole_variable_written() != NULL)) {
+ /* We did a whole-variable assignment. So, any instruction in
+ * the assignment list with the same LHS is dead.
+ */
+ if (debug)
+ printf("looking for %s to remove\n", var->name);
+ foreach_iter(exec_list_iterator, iter, *assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ if (entry->lhs == var) {
+ if (debug)
+ printf("removing %s\n", var->name);
+ entry->ir->remove();
+ entry->remove();
+ progress = true;
+ }
+ }
+ }
+
+ /* Add this instruction to the assignment list available to be removed.
+ * But not if the assignment has other side effects.
+ */
+ if (ir_has_call(ir))
+ return progress;
+
+ assignment_entry *entry = new(ctx) assignment_entry(var, ir);
+ assignments->push_tail(entry);
+
+ if (debug) {
+ printf("add %s\n", var->name);
+
+ printf("current entries\n");
+ foreach_iter(exec_list_iterator, iter, *assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ printf(" %s\n", entry->lhs->name);
+ }
+ }
+
+ return progress;
+}
+
+static void
+dead_code_local_basic_block(ir_instruction *first,
+ ir_instruction *last,
+ void *data)
+{
+ ir_instruction *ir, *ir_next;
+ /* List of avaialble_copy */
+ exec_list assignments;
+ bool *out_progress = (bool *)data;
+ bool progress = false;
+
+ void *ctx = talloc_new(NULL);
+ /* Safe looping, since process_assignment */
+ for (ir = first, ir_next = (ir_instruction *)first->next;;
+ ir = ir_next, ir_next = (ir_instruction *)ir->next) {
+ ir_assignment *ir_assign = ir->as_assignment();
+
+ if (debug) {
+ ir->print();
+ printf("\n");
+ }
+
+ if (ir_assign) {
+ progress = process_assignment(ctx, ir_assign, &assignments) || progress;
+ } else {
+ kill_for_derefs_visitor kill(&assignments);
+ ir->accept(&kill);
+ }
+
+ if (ir == last)
+ break;
+ }
+ *out_progress = progress;
+ talloc_free(ctx);
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_dead_code_local(exec_list *instructions)
+{
+ bool progress = false;
+
+ call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress);
+
+ return progress;
+}
--- /dev/null
+ /*
+ * 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 ir_dead_functions.cpp
+ *
+ * Eliminates unused functions from the linked program.
+ */
+
+ #include "ir.h"
+ #include "ir_visitor.h"
+ #include "ir_expression_flattening.h"
+ #include "glsl_types.h"
+
+ class signature_entry : public exec_node
+ {
+ public:
+ signature_entry(ir_function_signature *sig)
+ {
+ this->signature = sig;
+ this->used = false;
+ }
+
+ ir_function_signature *signature;
+ bool used;
+ };
+
+ class ir_dead_functions_visitor : public ir_hierarchical_visitor {
+ public:
+ ir_dead_functions_visitor()
+ {
+ this->mem_ctx = talloc_new(NULL);
+ }
+
+ ~ir_dead_functions_visitor()
+ {
+ talloc_free(this->mem_ctx);
+ }
+
+ virtual ir_visitor_status visit_enter(ir_function_signature *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+
+ signature_entry *get_signature_entry(ir_function_signature *var);
+
+ bool (*predicate)(ir_instruction *ir);
+
+ /* List of signature_entry */
+ exec_list signature_list;
+ void *mem_ctx;
+ };
+
+
+ signature_entry *
+ ir_dead_functions_visitor::get_signature_entry(ir_function_signature *sig)
+ {
+ foreach_iter(exec_list_iterator, iter, this->signature_list) {
+ signature_entry *entry = (signature_entry *)iter.get();
+ if (entry->signature == sig)
+ return entry;
+ }
+
+ signature_entry *entry = new(mem_ctx) signature_entry(sig);
+ this->signature_list.push_tail(entry);
+ return entry;
+ }
+
+
+ ir_visitor_status
+ ir_dead_functions_visitor::visit_enter(ir_function_signature *ir)
+ {
+ signature_entry *entry = this->get_signature_entry(ir);
+
+ if (strcmp(ir->function_name(), "main") == 0) {
+ entry->used = true;
+ }
+
+ return visit_continue;
+ }
+
+
+ ir_visitor_status
+ ir_dead_functions_visitor::visit_enter(ir_call *ir)
+ {
+ signature_entry *entry = this->get_signature_entry(ir->get_callee());
+
+ entry->used = true;
+
+ return visit_continue;
+}
+
+bool
+do_dead_functions(exec_list *instructions)
+{
+ ir_dead_functions_visitor v;
+ bool progress = false;
+
+ visit_list_elements(&v, instructions);
+
+ /* Now that we've figured out which function signatures are used, remove
+ * the unused ones, and remove function definitions that have no more
+ * signatures.
+ */
+ foreach_iter(exec_list_iterator, iter, v.signature_list) {
+ signature_entry *entry = (signature_entry *)iter.get();
+
+ if (!entry->used) {
+ entry->signature->remove();
+ delete entry->signature;
+ progress = true;
+ }
+ delete(entry);
+ }
+
+ /* We don't just do this above when we nuked a signature because of
+ * const pointers.
+ */
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_function *func = ir->as_function();
+
+ if (func && func->signatures.is_empty()) {
+ /* At this point (post-linking), the symbol table is no
+ * longer in use, so not removing the function from the
+ * symbol table should be OK.
+ */
+ func->remove();
+ delete func;
+ progress = true;
+ }
+ }
+
+ return progress;
+}
--- /dev/null
+/*
+ * 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 ir_function_inlining.cpp
+ *
+ * Replaces calls to functions with the body of the function.
+ */
+
+#include <inttypes.h>
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_function_inlining.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+#include "program/hash_table.h"
+
+static void
+do_sampler_replacement(exec_list *instructions,
+ ir_variable *sampler,
+ ir_dereference *deref);
+
+class ir_function_inlining_visitor : public ir_hierarchical_visitor {
+public:
+ ir_function_inlining_visitor()
+ {
+ progress = false;
+ }
+
+ virtual ~ir_function_inlining_visitor()
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_expression *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_return *);
+ virtual ir_visitor_status visit_enter(ir_texture *);
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+
+ bool progress;
+};
+
+
+bool
+automatic_inlining_predicate(ir_instruction *ir)
+{
+ ir_call *call = ir->as_call();
+
+ if (call && can_inline(call))
+ return true;
+
+ return false;
+}
+
+bool
+do_function_inlining(exec_list *instructions)
+{
+ ir_function_inlining_visitor v;
+
+ do_expression_flattening(instructions, automatic_inlining_predicate);
+
+ v.run(instructions);
+
+ return v.progress;
+}
+
+static void
+replace_return_with_assignment(ir_instruction *ir, void *data)
+{
+ void *ctx = talloc_parent(ir);
+ ir_variable *retval = (ir_variable *)data;
+ ir_return *ret = ir->as_return();
+
+ if (ret) {
+ if (ret->value) {
+ ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
+ ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
+ } else {
+ /* un-valued return has to be the last return, or we shouldn't
+ * have reached here. (see can_inline()).
+ */
+ assert(ret->next->is_tail_sentinel());
+ ret->remove();
+ }
+ }
+}
+
+ir_rvalue *
+ir_call::generate_inline(ir_instruction *next_ir)
+{
+ void *ctx = talloc_parent(this);
+ ir_variable **parameters;
+ int num_parameters;
+ int i;
+ ir_variable *retval = NULL;
+ struct hash_table *ht;
+
+ ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
+
+ num_parameters = 0;
+ foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
+ num_parameters++;
+
+ parameters = new ir_variable *[num_parameters];
+
+ /* Generate storage for the return value. */
+ if (this->callee->return_type) {
+ retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
+ ir_var_auto);
+ next_ir->insert_before(retval);
+ }
+
+ /* Generate the declarations for the parameters to our inlined code,
+ * and set up the mapping of real function body variables to ours.
+ */
+ i = 0;
+ exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
+ exec_list_iterator param_iter = this->actual_parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
+ ir_rvalue *param = (ir_rvalue *) param_iter.get();
+
+ /* Generate a new variable for the parameter. */
+ if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
+ /* For samplers, we want the inlined sampler references
+ * referencing the passed in sampler variable, since that
+ * will have the location information, which an assignment of
+ * a sampler wouldn't. Fix it up below.
+ */
+ parameters[i] = NULL;
+ } else {
+ parameters[i] = sig_param->clone(ctx, ht);
+ parameters[i]->mode = ir_var_auto;
+
+ /* Remove the read-only decoration becuase we're going to write
+ * directly to this variable. If the cloned variable is left
+ * read-only and the inlined function is inside a loop, the loop
+ * analysis code will get confused.
+ */
+ parameters[i]->read_only = false;
+ next_ir->insert_before(parameters[i]);
+ }
+
+ /* Move the actual param into our param variable if it's an 'in' type. */
+ if (parameters[i] && (sig_param->mode == ir_var_in ||
+ sig_param->mode == ir_var_inout)) {
+ ir_assignment *assign;
+
+ assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
+ param, NULL);
+ next_ir->insert_before(assign);
+ }
+
+ sig_param_iter.next();
+ param_iter.next();
+ }
+
+ exec_list new_instructions;
+
+ /* Generate the inlined body of the function to a new list */
+ foreach_iter(exec_list_iterator, iter, callee->body) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_instruction *new_ir = ir->clone(ctx, ht);
+
+ new_instructions.push_tail(new_ir);
+ visit_tree(new_ir, replace_return_with_assignment, retval);
+ }
+
+ /* If any samplers were passed in, replace any deref of the sampler
+ * with a deref of the sampler argument.
+ */
+ param_iter = this->actual_parameters.iterator();
+ sig_param_iter = this->callee->parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_instruction *const param = (ir_instruction *) param_iter.get();
+ ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
+
+ if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
+ ir_dereference *deref = param->as_dereference();
+
+ assert(deref);
+ do_sampler_replacement(&new_instructions, sig_param, deref);
+ }
+ param_iter.next();
+ sig_param_iter.next();
+ }
+
+ /* Now push those new instructions in. */
+ foreach_iter(exec_list_iterator, iter, new_instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ next_ir->insert_before(ir);
+ }
+
+ /* Copy back the value of any 'out' parameters from the function body
+ * variables to our own.
+ */
+ i = 0;
+ param_iter = this->actual_parameters.iterator();
+ sig_param_iter = this->callee->parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_instruction *const param = (ir_instruction *) param_iter.get();
+ const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
+
+ /* Move our param variable into the actual param if it's an 'out' type. */
+ if (parameters[i] && (sig_param->mode == ir_var_out ||
+ sig_param->mode == ir_var_inout)) {
+ ir_assignment *assign;
+
+ assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
+ new(ctx) ir_dereference_variable(parameters[i]),
+ NULL);
+ next_ir->insert_before(assign);
+ }
+
+ param_iter.next();
+ sig_param_iter.next();
+ }
+
+ delete [] parameters;
+
+ hash_table_dtor(ht);
+
+ if (retval)
+ return new(ctx) ir_dereference_variable(retval);
+ else
+ return NULL;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_expression *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_return *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_texture *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_call *ir)
+{
+ if (can_inline(ir)) {
+ /* If the call was part of some tree, then it should have been
+ * flattened out or we shouldn't have seen it because of a
+ * visit_continue_with_parent in this visitor.
+ */
+ assert(ir == base_ir);
+
+ (void) ir->generate_inline(ir);
+ ir->remove();
+ this->progress = true;
+ }
+
+ return visit_continue;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
+{
+ ir_call *call = ir->rhs->as_call();
+ if (!call || !can_inline(call))
+ return visit_continue;
+
+ /* generates the parameter setup, function body, and returns the return
+ * value of the function
+ */
+ ir_rvalue *rhs = call->generate_inline(ir);
+ assert(rhs);
+
+ ir->rhs = rhs;
+ this->progress = true;
+
+ return visit_continue;
+}
+
+/**
+ * Replaces references to the "sampler" variable with a clone of "deref."
+ *
+ * From the spec, samplers can appear in the tree as function
+ * (non-out) parameters and as the result of array indexing and
+ * structure field selection. In our builtin implementation, they
+ * also appear in the sampler field of an ir_tex instruction.
+ */
+
+class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
+public:
+ ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
+ {
+ this->sampler = sampler;
+ this->deref = deref;
+ }
+
+ virtual ~ir_sampler_replacement_visitor()
+ {
+ }
+
+ virtual ir_visitor_status visit_leave(ir_call *);
+ virtual ir_visitor_status visit_leave(ir_dereference_array *);
+ virtual ir_visitor_status visit_leave(ir_dereference_record *);
+ virtual ir_visitor_status visit_leave(ir_texture *);
+
+ void replace_deref(ir_dereference **deref);
+ void replace_rvalue(ir_rvalue **rvalue);
+
+ ir_variable *sampler;
+ ir_dereference *deref;
+};
+
+void
+ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
+{
+ ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
+ if (deref_var && deref_var->var == this->sampler) {
+ *deref = this->deref->clone(talloc_parent(*deref), NULL);
+ }
+}
+
+void
+ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return;
+
+ ir_dereference *deref = (*rvalue)->as_dereference();
+
+ if (!deref)
+ return;
+
+ replace_deref(&deref);
+ *rvalue = deref;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
+{
+ replace_deref(&ir->sampler);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
+{
+ replace_rvalue(&ir->array);
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
+{
+ replace_rvalue(&ir->record);
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
+{
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ ir_rvalue *new_param = param;
+ replace_rvalue(&new_param);
+
+ if (new_param != param) {
+ param->replace_with(new_param);
+ }
+ }
+ return visit_continue;
+}
+
+static void
+do_sampler_replacement(exec_list *instructions,
+ ir_variable *sampler,
+ ir_dereference *deref)
+{
+ ir_sampler_replacement_visitor v(sampler, deref);
+
+ visit_list_elements(&v, instructions);
+}
--- /dev/null
+/*
+ * 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 ir_if_simplification.cpp
+ *
+ * Moves constant branches of if statements out to the surrounding
+ * instruction stream.
+ */
+
+#include "ir.h"
+
+class ir_if_simplification_visitor : public ir_hierarchical_visitor {
+public:
+ ir_if_simplification_visitor()
+ {
+ this->made_progress = false;
+ }
+
+ ir_visitor_status visit_leave(ir_if *);
+
+ bool made_progress;
+};
+
+bool
+do_if_simplification(exec_list *instructions)
+{
+ ir_if_simplification_visitor v;
+
+ v.run(instructions);
+ return v.made_progress;
+}
+
+
+ir_visitor_status
+ir_if_simplification_visitor::visit_leave(ir_if *ir)
+{
+ /* FINISHME: Ideally there would be a way to note that the condition results
+ * FINISHME: in a constant before processing both of the other subtrees.
+ * FINISHME: This can probably be done with some flags, but it would take
+ * FINISHME: some work to get right.
+ */
+ ir_constant *condition_constant = ir->condition->constant_expression_value();
+ if (condition_constant) {
+ /* Move the contents of the one branch of the conditional
+ * that matters out.
+ */
+ if (condition_constant->value.b[0]) {
+ foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) {
+ ir_instruction *then_ir = (ir_instruction *)then_iter.get();
+ ir->insert_before(then_ir);
+ }
+ } else {
+ foreach_iter(exec_list_iterator, else_iter, ir->else_instructions) {
+ ir_instruction *else_ir = (ir_instruction *)else_iter.get();
+ ir->insert_before(else_ir);
+ }
+ }
+ ir->remove();
+ this->made_progress = true;
+ }
+
+ return visit_continue;
+}
--- /dev/null
+/*
+ * 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 ir_noop_swizzle.cpp
+ *
+ * If a swizzle doesn't change the order or count of components, then
+ * remove the swizzle so that other optimization passes see the value
+ * behind it.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_rvalue_visitor.h"
+#include "ir_print_visitor.h"
+#include "glsl_types.h"
+
+class ir_noop_swizzle_visitor : public ir_rvalue_visitor {
+public:
+ ir_noop_swizzle_visitor()
+ {
+ this->progress = false;
+ }
+
+ void handle_rvalue(ir_rvalue **rvalue);
+ bool progress;
+};
+
+void
+ir_noop_swizzle_visitor::handle_rvalue(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return;
+
+ ir_swizzle *swiz = (*rvalue)->as_swizzle();
+ if (!swiz || swiz->type != swiz->val->type)
+ return;
+
+ int elems = swiz->val->type->vector_elements;
+ if (swiz->mask.x != 0)
+ return;
+ if (elems >= 2 && swiz->mask.y != 1)
+ return;
+ if (elems >= 3 && swiz->mask.z != 2)
+ return;
+ if (elems >= 4 && swiz->mask.w != 3)
+ return;
+
+ this->progress = true;
+ *rvalue = swiz->val;
+}
+
+bool
+do_noop_swizzle(exec_list *instructions)
+{
+ ir_noop_swizzle_visitor v;
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_structure_splitting.cpp
+ *
+ * If a structure is only ever referenced by its components, then
+ * split those components out to individual variables so they can be
+ * handled normally by other optimization passes.
+ *
+ * This skips structures like uniforms, which need to be accessible as
+ * structures for their access by the GL.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_print_visitor.h"
+#include "ir_rvalue_visitor.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+// XXX using variable_entry2 here to avoid collision (MSVC multiply-defined
+// function) with the variable_entry class seen in ir_variable_refcount.h
+// Perhaps we can use the one in ir_variable_refcount.h and make this class
+// here go away?
+class variable_entry2 : public exec_node
+{
+public:
+ variable_entry2(ir_variable *var)
+ {
+ this->var = var;
+ this->whole_structure_access = 0;
+ this->declaration = false;
+ this->components = NULL;
+ this->mem_ctx = NULL;
+ }
+
+ ir_variable *var; /* The key: the variable's pointer. */
+
+ /** Number of times the variable is referenced, including assignments. */
+ unsigned whole_structure_access;
+
+ bool declaration; /* If the variable had a decl in the instruction stream */
+
+ ir_variable **components;
+
+ /** talloc_parent(this->var) -- the shader's talloc context. */
+ void *mem_ctx;
+};
+
+
+class ir_structure_reference_visitor : public ir_hierarchical_visitor {
+public:
+ ir_structure_reference_visitor(void)
+ {
+ this->mem_ctx = talloc_new(NULL);
+ this->variable_list.make_empty();
+ }
+
+ ~ir_structure_reference_visitor(void)
+ {
+ talloc_free(mem_ctx);
+ }
+
+ virtual ir_visitor_status visit(ir_variable *);
+ virtual ir_visitor_status visit(ir_dereference_variable *);
+ virtual ir_visitor_status visit_enter(ir_dereference_record *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_function_signature *);
+
+ variable_entry2 *get_variable_entry2(ir_variable *var);
+
+ /* List of variable_entry */
+ exec_list variable_list;
+
+ void *mem_ctx;
+};
+
+variable_entry2 *
+ir_structure_reference_visitor::get_variable_entry2(ir_variable *var)
+{
+ assert(var);
+
+ if (!var->type->is_record() || var->mode == ir_var_uniform)
+ return NULL;
+
+ foreach_iter(exec_list_iterator, iter, this->variable_list) {
+ variable_entry2 *entry = (variable_entry2 *)iter.get();
+ if (entry->var == var)
+ return entry;
+ }
+
+ variable_entry2 *entry = new(mem_ctx) variable_entry2(var);
+ this->variable_list.push_tail(entry);
+ return entry;
+}
+
+
+ir_visitor_status
+ir_structure_reference_visitor::visit(ir_variable *ir)
+{
+ variable_entry2 *entry = this->get_variable_entry2(ir);
+
+ if (entry)
+ entry->declaration = true;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_structure_reference_visitor::visit(ir_dereference_variable *ir)
+{
+ ir_variable *const var = ir->variable_referenced();
+ variable_entry2 *entry = this->get_variable_entry2(var);
+
+ if (entry)
+ entry->whole_structure_access++;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir)
+{
+ (void) ir;
+ /* Don't descend into the ir_dereference_variable below. */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_structure_reference_visitor::visit_enter(ir_assignment *ir)
+{
+ if (ir->lhs->as_dereference_variable() &&
+ ir->rhs->as_dereference_variable() &&
+ !ir->condition) {
+ /* We'll split copies of a structure to copies of components, so don't
+ * descend to the ir_dereference_variables.
+ */
+ return visit_continue_with_parent;
+ }
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_structure_reference_visitor::visit_enter(ir_function_signature *ir)
+{
+ /* We don't want to descend into the function parameters and
+ * dead-code eliminate them, so just accept the body here.
+ */
+ visit_list_elements(this, &ir->body);
+ return visit_continue_with_parent;
+}
+
+class ir_structure_splitting_visitor : public ir_rvalue_visitor {
+public:
+ ir_structure_splitting_visitor(exec_list *vars)
+ {
+ this->variable_list = vars;
+ }
+
+ virtual ~ir_structure_splitting_visitor()
+ {
+ }
+
+ virtual ir_visitor_status visit_leave(ir_assignment *);
+
+ void split_deref(ir_dereference **deref);
+ void handle_rvalue(ir_rvalue **rvalue);
+ variable_entry2 *get_splitting_entry(ir_variable *var);
+
+ exec_list *variable_list;
+ void *mem_ctx;
+};
+
+variable_entry2 *
+ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var)
+{
+ assert(var);
+
+ if (!var->type->is_record())
+ return NULL;
+
+ foreach_iter(exec_list_iterator, iter, *this->variable_list) {
+ variable_entry2 *entry = (variable_entry2 *)iter.get();
+ if (entry->var == var) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+void
+ir_structure_splitting_visitor::split_deref(ir_dereference **deref)
+{
+ if ((*deref)->ir_type != ir_type_dereference_record)
+ return;
+
+ ir_dereference_record *deref_record = (ir_dereference_record *)*deref;
+ ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable();
+ if (!deref_var)
+ return;
+
+ variable_entry2 *entry = get_splitting_entry(deref_var->var);
+ if (!entry)
+ return;
+
+ unsigned int i;
+ for (i = 0; i < entry->var->type->length; i++) {
+ if (strcmp(deref_record->field,
+ entry->var->type->fields.structure[i].name) == 0)
+ break;
+ }
+ assert(i != entry->var->type->length);
+
+ *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]);
+}
+
+void
+ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return;
+
+ ir_dereference *deref = (*rvalue)->as_dereference();
+
+ if (!deref)
+ return;
+
+ split_deref(&deref);
+ *rvalue = deref;
+}
+
+ir_visitor_status
+ir_structure_splitting_visitor::visit_leave(ir_assignment *ir)
+{
+ ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable();
+ ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable();
+ variable_entry2 *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL;
+ variable_entry2 *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL;
+ const glsl_type *type = ir->rhs->type;
+
+ if ((lhs_entry || rhs_entry) && !ir->condition) {
+ for (unsigned int i = 0; i < type->length; i++) {
+ ir_dereference *new_lhs, *new_rhs;
+ void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx;
+
+ if (lhs_entry) {
+ new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]);
+ } else {
+ new_lhs = new(mem_ctx)
+ ir_dereference_record(ir->lhs->clone(mem_ctx, NULL),
+ type->fields.structure[i].name);
+ }
+
+ if (rhs_entry) {
+ new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]);
+ } else {
+ new_rhs = new(mem_ctx)
+ ir_dereference_record(ir->rhs->clone(mem_ctx, NULL),
+ type->fields.structure[i].name);
+ }
+
+ ir->insert_before(new(mem_ctx) ir_assignment(new_lhs,
+ new_rhs,
+ NULL));
+ }
+ ir->remove();
+ } else {
+ handle_rvalue(&ir->rhs);
+ split_deref(&ir->lhs);
+ }
+
+ handle_rvalue(&ir->condition);
+
+ return visit_continue;
+}
+
+bool
+do_structure_splitting(exec_list *instructions)
+{
+ ir_structure_reference_visitor refs;
+
+ visit_list_elements(&refs, instructions);
+
+ /* Trim out variables we can't split. */
+ foreach_iter(exec_list_iterator, iter, refs.variable_list) {
+ variable_entry2 *entry = (variable_entry2 *)iter.get();
+
+ if (debug) {
+ printf("structure %s@%p: decl %d, whole_access %d\n",
+ entry->var->name, (void *) entry->var, entry->declaration,
+ entry->whole_structure_access);
+ }
+
+ if (!entry->declaration || entry->whole_structure_access) {
+ entry->remove();
+ }
+ }
+
+ if (refs.variable_list.is_empty())
+ return false;
+
+ void *mem_ctx = talloc_new(NULL);
+
+ /* Replace the decls of the structures to be split with their split
+ * components.
+ */
+ foreach_iter(exec_list_iterator, iter, refs.variable_list) {
+ variable_entry2 *entry = (variable_entry2 *)iter.get();
+ const struct glsl_type *type = entry->var->type;
+
+ entry->mem_ctx = talloc_parent(entry->var);
+
+ entry->components = talloc_array(mem_ctx,
+ ir_variable *,
+ type->length);
+
+ for (unsigned int i = 0; i < entry->var->type->length; i++) {
+ const char *name = talloc_asprintf(mem_ctx, "%s_%s",
+ entry->var->name,
+ type->fields.structure[i].name);
+
+ entry->components[i] =
+ new(entry->mem_ctx) ir_variable(type->fields.structure[i].type,
+ name,
+ ir_var_temporary);
+ entry->var->insert_before(entry->components[i]);
+ }
+
+ entry->var->remove();
+ }
+
+ ir_structure_splitting_visitor split(&refs.variable_list);
+ visit_list_elements(&split, instructions);
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
--- /dev/null
+/*
+ * 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 ir_swizzle_swizzle.cpp
+ *
+ * Eliminates the second swizzle in a swizzle chain.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor {
+public:
+ ir_swizzle_swizzle_visitor()
+ {
+ progress = false;
+ }
+
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+
+ bool progress;
+};
+
+ir_visitor_status
+ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir)
+{
+ int mask2[4];
+
+ ir_swizzle *swiz2 = ir->val->as_swizzle();
+ if (!swiz2)
+ return visit_continue;
+
+ memset(&mask2, 0, sizeof(mask2));
+ if (swiz2->mask.num_components >= 1)
+ mask2[0] = swiz2->mask.x;
+ if (swiz2->mask.num_components >= 2)
+ mask2[1] = swiz2->mask.y;
+ if (swiz2->mask.num_components >= 3)
+ mask2[2] = swiz2->mask.z;
+ if (swiz2->mask.num_components >= 4)
+ mask2[3] = swiz2->mask.w;
+
+ if (ir->mask.num_components >= 1)
+ ir->mask.x = mask2[ir->mask.x];
+ if (ir->mask.num_components >= 2)
+ ir->mask.y = mask2[ir->mask.y];
+ if (ir->mask.num_components >= 3)
+ ir->mask.z = mask2[ir->mask.z];
+ if (ir->mask.num_components >= 4)
+ ir->mask.w = mask2[ir->mask.w];
+
+ ir->val = swiz2->val;
+
+ this->progress = true;
+
+ return visit_continue;
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_swizzle_swizzle(exec_list *instructions)
+{
+ ir_swizzle_swizzle_visitor v;
+
+ v.run(instructions);
+
+ return v.progress;
+}
--- /dev/null
+/*
+ * 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 ir_tree_grafting.cpp
+ *
+ * Takes assignments to variables that are dereferenced only once and
+ * pastes the RHS expression into where the variable is dereferenced.
+ *
+ * In the process of various operations like function inlining and
+ * tertiary op handling, we'll end up with our expression trees having
+ * been chopped up into a series of assignments of short expressions
+ * to temps. Other passes like ir_algebraic.cpp would prefer to see
+ * the deepest expression trees they can to try to optimize them.
+ *
+ * This is a lot like copy propagaton. In comparison, copy
+ * propagation only acts on plain copies, not arbitrary expressions on
+ * the RHS. Generally, we wouldn't want to go pasting some
+ * complicated expression everywhere it got used, though, so we don't
+ * handle expressions in that pass.
+ *
+ * The hard part is making sure we don't move an expression across
+ * some other assignments that would change the value of the
+ * expression. So we split this into two passes: First, find the
+ * variables in our scope which are written to once and read once, and
+ * then go through basic blocks seeing if we find an opportunity to
+ * move those expressions safely.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_variable_refcount.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+class ir_tree_grafting_visitor : public ir_hierarchical_visitor {
+public:
+ ir_tree_grafting_visitor(ir_assignment *graft_assign,
+ ir_variable *graft_var)
+ {
+ this->progress = false;
+ this->graft_assign = graft_assign;
+ this->graft_var = graft_var;
+ }
+
+ virtual ir_visitor_status visit_leave(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_expression *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_swizzle *);
+ virtual ir_visitor_status visit_enter(class ir_texture *);
+
+ bool do_graft(ir_rvalue **rvalue);
+
+ bool progress;
+ ir_variable *graft_var;
+ ir_assignment *graft_assign;
+};
+
+struct find_deref_info {
+ ir_variable *var;
+ bool found;
+};
+
+void
+dereferences_variable_callback(ir_instruction *ir, void *data)
+{
+ struct find_deref_info *info = (struct find_deref_info *)data;
+ ir_dereference_variable *deref = ir->as_dereference_variable();
+
+ if (deref && deref->var == info->var)
+ info->found = true;
+}
+
+static bool
+dereferences_variable(ir_instruction *ir, ir_variable *var)
+{
+ struct find_deref_info info;
+
+ info.var = var;
+ info.found = false;
+
+ visit_tree(ir, dereferences_variable_callback, &info);
+
+ return info.found;
+}
+
+bool
+ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return false;
+
+ ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
+
+ if (!deref || deref->var != this->graft_var)
+ return false;
+
+ if (debug) {
+ printf("GRAFTING:\n");
+ this->graft_assign->print();
+ printf("\n");
+ printf("TO:\n");
+ (*rvalue)->print();
+ printf("\n");
+ }
+
+ this->graft_assign->remove();
+ *rvalue = this->graft_assign->rhs;
+
+ this->progress = true;
+ return true;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_loop *ir)
+{
+ (void)ir;
+ /* Do not traverse into the body of the loop since that is a
+ * different basic block.
+ */
+ return visit_stop;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_leave(ir_assignment *ir)
+{
+ if (do_graft(&ir->rhs) ||
+ do_graft(&ir->condition))
+ return visit_stop;
+
+ /* If this assignment updates a variable used in the assignment
+ * we're trying to graft, then we're done.
+ */
+ if (dereferences_variable(this->graft_assign->rhs,
+ ir->lhs->variable_referenced())) {
+ if (debug) {
+ printf("graft killed by: ");
+ ir->print();
+ printf("\n");
+ }
+ return visit_stop;
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_function *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir)
+{
+ (void)ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_call *ir)
+{
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ /* Reminder: iterating ir_call iterates its parameters. */
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_variable *sig_param = (ir_variable *)sig_iter.get();
+ ir_rvalue *ir = (ir_rvalue *)iter.get();
+ ir_rvalue *new_ir = ir;
+
+ if (sig_param->mode != ir_var_in)
+ continue;
+
+ if (do_graft(&new_ir)) {
+ ir->replace_with(new_ir);
+ return visit_stop;
+ }
+ sig_iter.next();
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_expression *ir)
+{
+ for (unsigned int i = 0; i < ir->get_num_operands(); i++) {
+ if (do_graft(&ir->operands[i]))
+ return visit_stop;
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_if *ir)
+{
+ if (do_graft(&ir->condition))
+ return visit_stop;
+
+ /* Do not traverse into the body of the if-statement since that is a
+ * different basic block.
+ */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir)
+{
+ if (do_graft(&ir->val))
+ return visit_stop;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_texture *ir)
+{
+ if (do_graft(&ir->coordinate) ||
+ do_graft(&ir->projector) ||
+ do_graft(&ir->shadow_comparitor))
+ return visit_stop;
+
+ switch (ir->op) {
+ case ir_tex:
+ break;
+ case ir_txb:
+ if (do_graft(&ir->lod_info.bias))
+ return visit_stop;
+ break;
+ case ir_txf:
+ case ir_txl:
+ if (do_graft(&ir->lod_info.lod))
+ return visit_stop;
+ break;
+ case ir_txd:
+ if (do_graft(&ir->lod_info.grad.dPdx) ||
+ do_graft(&ir->lod_info.grad.dPdy))
+ return visit_stop;
+ break;
+ }
+
+ return visit_continue;
+}
+
+struct tree_grafting_info {
+ ir_variable_refcount_visitor *refs;
+ bool progress;
+};
+
+static bool
+try_tree_grafting(ir_assignment *start,
+ ir_variable *lhs_var,
+ ir_instruction *bb_last)
+{
+ ir_tree_grafting_visitor v(start, lhs_var);
+
+ if (debug) {
+ printf("trying to graft: ");
+ lhs_var->print();
+ printf("\n");
+ }
+
+ for (ir_instruction *ir = (ir_instruction *)start->next;
+ ir != bb_last->next;
+ ir = (ir_instruction *)ir->next) {
+
+ if (debug) {
+ printf("- ");
+ ir->print();
+ printf("\n");
+ }
+
+ ir_visitor_status s = ir->accept(&v);
+ if (s == visit_stop)
+ return v.progress;
+ }
+
+ return false;
+}
+
+static void
+tree_grafting_basic_block(ir_instruction *bb_first,
+ ir_instruction *bb_last,
+ void *data)
+{
+ struct tree_grafting_info *info = (struct tree_grafting_info *)data;
+ ir_instruction *ir, *next;
+
+ for (ir = bb_first, next = (ir_instruction *)ir->next;
+ ir != bb_last->next;
+ ir = next, next = (ir_instruction *)ir->next) {
+ ir_assignment *assign = ir->as_assignment();
+
+ if (!assign)
+ continue;
+
+ ir_variable *lhs_var = assign->whole_variable_written();
+ if (!lhs_var)
+ continue;
+
+ if (lhs_var->mode == ir_var_out ||
+ lhs_var->mode == ir_var_inout)
+ continue;
+
+ variable_entry *entry = info->refs->get_variable_entry(lhs_var);
+
+ if (!entry->declaration ||
+ entry->assigned_count != 1 ||
+ entry->referenced_count != 2)
+ continue;
+
+ assert(assign == entry->assign);
+
+ /* Found a possibly graftable assignment. Now, walk through the
+ * rest of the BB seeing if the deref is here, and if nothing interfered with
+ * pasting its expression's values in between.
+ */
+ info->progress |= try_tree_grafting(assign, lhs_var, bb_last);
+ }
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_tree_grafting(exec_list *instructions)
+{
+ ir_variable_refcount_visitor refs;
+ struct tree_grafting_info info;
+
+ info.progress = false;
+ info.refs = &refs;
+
+ visit_list_elements(info.refs, instructions);
+
+ call_for_basic_blocks(instructions, tree_grafting_basic_block, &info);
+
+ return info.progress;
+}