X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fcompiler%2Fglsl%2Flower_if_to_cond_assign.cpp;h=37f1ec8600c82e0794389b36a4ae0112ef67638f;hb=f9ab60202d48c72afa6a6f2a8c27db1e0777ed16;hp=6a7034794b2c565587f652b257a4156e79256714;hpb=741744f691d6ef63e9f9a4c03136f969f2ffb0bf;p=mesa.git diff --git a/src/compiler/glsl/lower_if_to_cond_assign.cpp b/src/compiler/glsl/lower_if_to_cond_assign.cpp index 6a7034794b2..37f1ec8600c 100644 --- a/src/compiler/glsl/lower_if_to_cond_assign.cpp +++ b/src/compiler/glsl/lower_if_to_cond_assign.cpp @@ -24,8 +24,14 @@ /** * \file lower_if_to_cond_assign.cpp * - * This attempts to flatten if-statements to conditional assignments for - * GPUs with limited or no flow control support. + * This flattens if-statements to conditional assignments if: + * + * - the GPU has limited or no flow control support + * (controlled by max_depth) + * + * - small conditional branches are more expensive than conditional assignments + * (controlled by min_branch_cost, that's the cost for a branch to be + * preserved) * * It can't handle other control flow being inside of its block, such * as calls or loops. Hopefully loop unrolling and inlining will take @@ -47,46 +53,62 @@ #include "compiler/glsl_types.h" #include "ir.h" -#include "program/hash_table.h" +#include "util/set.h" +#include "util/hash_table.h" /* Needed for the hashing functions */ +#include "main/macros.h" /* for MAX2 */ namespace { class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor { public: - ir_if_to_cond_assign_visitor(unsigned max_depth) + ir_if_to_cond_assign_visitor(gl_shader_stage stage, + unsigned max_depth, + unsigned min_branch_cost) { this->progress = false; + this->stage = stage; this->max_depth = max_depth; + this->min_branch_cost = min_branch_cost; this->depth = 0; - this->condition_variables = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); + this->condition_variables = + _mesa_set_create(NULL, _mesa_hash_pointer, + _mesa_key_pointer_equal); } ~ir_if_to_cond_assign_visitor() { - hash_table_dtor(this->condition_variables); + _mesa_set_destroy(this->condition_variables, NULL); } ir_visitor_status visit_enter(ir_if *); ir_visitor_status visit_leave(ir_if *); + bool found_unsupported_op; + bool found_expensive_op; + bool found_dynamic_arrayref; + bool is_then; bool progress; + gl_shader_stage stage; + unsigned then_cost; + unsigned else_cost; + unsigned min_branch_cost; unsigned max_depth; unsigned depth; - struct hash_table *condition_variables; + struct set *condition_variables; }; } /* anonymous namespace */ bool -lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth) +lower_if_to_cond_assign(gl_shader_stage stage, exec_list *instructions, + unsigned max_depth, unsigned min_branch_cost) { if (max_depth == UINT_MAX) return false; - ir_if_to_cond_assign_visitor v(max_depth); + ir_if_to_cond_assign_visitor v(stage, max_depth, min_branch_cost); visit_list_elements(&v, instructions); @@ -94,17 +116,53 @@ lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth) } void -check_control_flow(ir_instruction *ir, void *data) +check_ir_node(ir_instruction *ir, void *data) { - bool *found_control_flow = (bool *)data; + ir_if_to_cond_assign_visitor *v = (ir_if_to_cond_assign_visitor *)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; + case ir_type_emit_vertex: + case ir_type_end_primitive: + case ir_type_barrier: + v->found_unsupported_op = true; + break; + + case ir_type_dereference_variable: { + ir_variable *var = ir->as_dereference_variable()->variable_referenced(); + + /* Lowering branches with TCS output accesses breaks many piglit tests, + * so don't touch them for now. + */ + if (v->stage == MESA_SHADER_TESS_CTRL && + var->data.mode == ir_var_shader_out) + v->found_unsupported_op = true; + break; + } + + /* SSBO, images, atomic counters are handled by ir_type_call */ + case ir_type_texture: + v->found_expensive_op = true; break; + + case ir_type_dereference_array: { + ir_dereference_array *deref = ir->as_dereference_array(); + + if (deref->array_index->ir_type != ir_type_constant) + v->found_dynamic_arrayref = true; + } /* fall-through */ + case ir_type_expression: + case ir_type_dereference_record: + if (v->is_then) + v->then_cost++; + else + v->else_cost++; + break; + default: break; } @@ -114,38 +172,39 @@ void move_block_to_cond_assign(void *mem_ctx, ir_if *if_ir, ir_rvalue *cond_expr, exec_list *instructions, - struct hash_table *ht) + struct set *set) { foreach_in_list_safe(ir_instruction, ir, instructions) { if (ir->ir_type == ir_type_assignment) { ir_assignment *assign = (ir_assignment *)ir; - if (hash_table_find(ht, assign) == NULL) { - hash_table_insert(ht, assign, assign); + if (_mesa_set_search(set, assign) == NULL) { + _mesa_set_add(set, 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; + _mesa_set_search( + set, 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); + 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); + new(mem_ctx) ir_expression(ir_binop_logic_and, + glsl_type::bool_type, + cond_expr->clone(mem_ctx, NULL), + assign->condition); } } } @@ -168,21 +227,46 @@ ir_if_to_cond_assign_visitor::visit_enter(ir_if *ir) ir_visitor_status ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) { + bool must_lower = this->depth-- > this->max_depth; + /* Only flatten when beyond the GPU's maximum supported nesting depth. */ - if (this->depth-- <= this->max_depth) + if (!must_lower && this->min_branch_cost == 0) return visit_continue; - bool found_control_flow = false; + this->found_unsupported_op = false; + this->found_expensive_op = false; + this->found_dynamic_arrayref = false; + this->then_cost = 0; + this->else_cost = 0; + ir_assignment *assign; /* Check that both blocks don't contain anything we can't support. */ + this->is_then = true; foreach_in_list(ir_instruction, then_ir, &ir->then_instructions) { - visit_tree(then_ir, check_control_flow, &found_control_flow); + visit_tree(then_ir, check_ir_node, this); } + + this->is_then = false; foreach_in_list(ir_instruction, else_ir, &ir->else_instructions) { - visit_tree(else_ir, check_control_flow, &found_control_flow); + visit_tree(else_ir, check_ir_node, this); } - if (found_control_flow) + + if (this->found_unsupported_op) + return visit_continue; /* can't handle inner unsupported opcodes */ + + /* Skip if the branch cost is high enough or if there's an expensive op. + * + * Also skip if non-constant array indices were encountered, since those + * can be out-of-bounds for a not-taken branch, and so generating an + * assignment would be incorrect. In the case of must_lower, it's up to the + * backend to deal with any potential fall-out (perhaps by translating the + * assignments to hardware-predicated moves). + */ + if (!must_lower && + (this->found_expensive_op || + this->found_dynamic_arrayref || + MAX2(this->then_cost, this->else_cost) >= this->min_branch_cost)) return visit_continue; void *mem_ctx = ralloc_parent(ir); @@ -210,7 +294,7 @@ ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) /* 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, then_var, then_var); + _mesa_set_add(this->condition_variables, 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 @@ -241,7 +325,7 @@ ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) /* 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); + _mesa_set_add(this->condition_variables, else_var); } ir->remove();