diff --git a/src/compiler/glsl/loop_analysis.cpp b/src/compiler/glsl/loop_analysis.cpp index aae8fc62cd0..9429e69c2a7 100644 --- a/src/compiler/glsl/loop_analysis.cpp +++ b/src/compiler/glsl/loop_analysis.cpp @@ -88,7 +88,7 @@ find_initial_value(ir_loop *loop, ir_variable *var) static int calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, enum ir_expression_operation op, bool continue_from_then, - bool swap_compare_operands) + bool swap_compare_operands, bool inc_before_terminator) { if (from == NULL || to == NULL || increment == NULL) return -1; @@ -118,6 +118,32 @@ calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, int iter_value = iter->get_int_component(0); + /* Code after this block works under assumption that iterator will be + * incremented or decremented until it hits the limit, + * however the loop condition can be false on the first iteration. + * Handle such loops first. + */ + { + ir_rvalue *first_value = from; + if (inc_before_terminator) { + first_value = + new(mem_ctx) ir_expression(ir_binop_add, from->type, from, increment); + } + + ir_expression *cmp = swap_compare_operands + ? new(mem_ctx) ir_expression(op, glsl_type::bool_type, to, first_value) + : new(mem_ctx) ir_expression(op, glsl_type::bool_type, first_value, to); + if (continue_from_then) + cmp = new(mem_ctx) ir_expression(ir_unop_logic_not, cmp); + + ir_constant *const cmp_result = cmp->constant_expression_value(mem_ctx); + assert(cmp_result != NULL); + if (cmp_result->get_bool_component(0)) { + ralloc_free(mem_ctx); + return 0; + } + } + /* Make sure that the calculated number of iterations satisfies the exit * condition. This is needed to catch off-by-one errors and some types of * ill-formed loops. For example, we need to detect that the following @@ -172,6 +198,11 @@ calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, } ralloc_free(mem_ctx); + + if (inc_before_terminator) { + iter_value--; + } + return (valid_loop) ? iter_value : -1; } @@ -611,13 +642,13 @@ loop_analysis::visit_leave(ir_loop *ir) loop_variable *lv = ls->get(var); if (lv != NULL && lv->is_induction_var()) { + bool inc_before_terminator = + incremented_before_terminator(ir, var, t->ir); + t->iterations = calculate_iterations(init, limit, lv->increment, cmp, t->continue_from_then, - swap_compare_operands); - - if (incremented_before_terminator(ir, var, t->ir)) { - t->iterations--; - } + swap_compare_operands, + inc_before_terminator); if (t->iterations >= 0 && (ls->limiting_terminator == NULL || diff --git a/src/compiler/glsl/loop_unroll.cpp b/src/compiler/glsl/loop_unroll.cpp index 7e97c3cddf1..1a9229acc34 100644 --- a/src/compiler/glsl/loop_unroll.cpp +++ b/src/compiler/glsl/loop_unroll.cpp @@ -390,17 +390,10 @@ loop_unroll_visitor::visit_leave(ir_loop *ir) return visit_continue; } - if (ls->limiting_terminator != NULL) { - /* If the limiting terminator has an iteration count of zero, then we've - * proven that the loop cannot run, so delete it. - */ - int iterations = ls->limiting_terminator->iterations; - if (iterations == 0) { - ir->remove(); - this->progress = true; - return visit_continue; - } - } + /* Limiting terminator may have iteration count of zero, + * this is a valid case because the loop may break during + * the first iteration. + */ /* Remove the conditional break statements associated with all terminators * that are associated with a fixed iteration count, except for the one