X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flower_if_to_cond_assign.cpp;h=2c5d5612d0d805783ef8ad895c9ba0b80cebfb6b;hb=f01f754ca13373d62f5f4ba5ff76d83aa4eac62b;hp=cf48cfb8d611f92a786e64d17581415693824c4f;hpb=e6b71530daea3059ee362d4df51575e27e026b22;p=mesa.git diff --git a/src/glsl/lower_if_to_cond_assign.cpp b/src/glsl/lower_if_to_cond_assign.cpp index cf48cfb8d61..2c5d5612d0d 100644 --- a/src/glsl/lower_if_to_cond_assign.cpp +++ b/src/glsl/lower_if_to_cond_assign.cpp @@ -24,33 +24,65 @@ /** * \file lower_if_to_cond_assign.cpp * - * This attempts to flatten all if statements to conditional - * assignments for GPUs that don't do control flow. + * This attempts to flatten if-statements to conditional assignments for + * GPUs with limited or no flow control support. * * 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. + * + * Drivers for GPUs with no control flow support should simply call + * + * lower_if_to_cond_assign(instructions) + * + * to attempt to flatten all if-statements. + * + * Some GPUs (such as i965 prior to gen6) do support control flow, but have a + * maximum nesting depth N. Drivers for such hardware can call + * + * lower_if_to_cond_assign(instructions, N) + * + * to attempt to flatten any if-statements appearing at depth > N. */ #include "glsl_types.h" #include "ir.h" +#include "program/hash_table.h" class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor { public: - ir_if_to_cond_assign_visitor() + ir_if_to_cond_assign_visitor(unsigned max_depth) { this->progress = false; + this->max_depth = max_depth; + this->depth = 0; + + this->condition_variables = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); } + ~ir_if_to_cond_assign_visitor() + { + hash_table_dtor(this->condition_variables); + } + + ir_visitor_status visit_enter(ir_if *); ir_visitor_status visit_leave(ir_if *); bool progress; + unsigned max_depth; + unsigned depth; + + struct hash_table *condition_variables; }; bool -do_if_to_cond_assign(exec_list *instructions) +lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth) { - ir_if_to_cond_assign_visitor v; + if (max_depth == UINT_MAX) + return false; + + ir_if_to_cond_assign_visitor v(max_depth); visit_list_elements(&v, instructions); @@ -76,40 +108,43 @@ check_control_flow(ir_instruction *ir, void *data) void move_block_to_cond_assign(void *mem_ctx, - ir_if *if_ir, ir_variable *cond_var, bool then) + ir_if *if_ir, ir_rvalue *cond_expr, + exec_list *instructions, + struct hash_table *ht) { - 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(); + foreach_list_safe(node, instructions) { + ir_instruction *ir = (ir_instruction *) node; 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); + if (hash_table_find(ht, assign) == NULL) { + hash_table_insert(ht, assign, assign); + + /* If the LHS of the assignment is a condition variable that was + * previously added, insert an additional assignment of false to + * the variable. + */ + const bool assign_to_cv = + hash_table_find(ht, assign->lhs->variable_referenced()) != NULL; + + if (!assign->condition) { + if (assign_to_cv) { + assign->rhs = + new(mem_ctx) ir_expression(ir_binop_logic_and, + glsl_type::bool_type, + cond_expr->clone(mem_ctx, NULL), + assign->rhs); + } else { + assign->condition = cond_expr->clone(mem_ctx, NULL); + } + } else { + assign->condition = + new(mem_ctx) ir_expression(ir_binop_logic_and, + glsl_type::bool_type, + cond_expr->clone(mem_ctx, NULL), + assign->condition); + } } } @@ -119,13 +154,24 @@ move_block_to_cond_assign(void *mem_ctx, } } +ir_visitor_status +ir_if_to_cond_assign_visitor::visit_enter(ir_if *ir) +{ + (void) ir; + this->depth++; + + return visit_continue; +} + ir_visitor_status ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) { + /* Only flatten when beyond the GPU's maximum supported nesting depth. */ + if (this->depth-- <= this->max_depth) + return visit_continue; + 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) { @@ -139,26 +185,64 @@ ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) if (found_control_flow) return visit_continue; - void *mem_ctx = talloc_parent(ir); + void *mem_ctx = ralloc_parent(ir); - /* Store the condition to a variable so the assignment conditions are - * simpler. + /* Store the condition to a variable. Move all of the instructions from + * the then-clause of the if-statement. Use the condition variable as a + * condition for all assignments. */ - 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_variable *const then_var = + new(mem_ctx) ir_variable(glsl_type::bool_type, + "if_to_cond_assign_then", + ir_var_temporary); + ir->insert_before(then_var); + + ir_dereference_variable *then_cond = + new(mem_ctx) ir_dereference_variable(then_var); + + assign = new(mem_ctx) ir_assignment(then_cond, ir->condition); 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, then_cond, + &ir->then_instructions, + this->condition_variables); + + /* Add the new condition variable to the hash table. This allows us to + * find this variable when lowering other (enclosing) if-statements. */ - move_block_to_cond_assign(mem_ctx, ir, cond_var, true); - move_block_to_cond_assign(mem_ctx, ir, cond_var, false); + hash_table_insert(this->condition_variables, then_var, then_var); + + /* If there are instructions in the else-clause, store the inverse of the + * condition to a variable. Move all of the instructions from the + * else-clause if the if-statement. Use the (inverse) condition variable + * as a condition for all assignments. + */ + if (!ir->else_instructions.is_empty()) { + ir_variable *const else_var = + new(mem_ctx) ir_variable(glsl_type::bool_type, + "if_to_cond_assign_else", + ir_var_temporary); + ir->insert_before(else_var); + + ir_dereference_variable *else_cond = + new(mem_ctx) ir_dereference_variable(else_var); + + ir_rvalue *inverse = + new(mem_ctx) ir_expression(ir_unop_logic_not, + then_cond->clone(mem_ctx, NULL)); + + assign = new(mem_ctx) ir_assignment(else_cond, inverse); + ir->insert_before(assign); + + move_block_to_cond_assign(mem_ctx, ir, else_cond, + &ir->else_instructions, + this->condition_variables); + + /* Add the new condition variable to the hash table. This allows us to + * find this variable when lowering other (enclosing) if-statements. + */ + hash_table_insert(this->condition_variables, else_var, else_var); + } ir->remove();