diff options
Diffstat (limited to 'src/glsl')
-rw-r--r-- | src/glsl/loop_unroll.cpp | 93 |
1 files changed, 89 insertions, 4 deletions
diff --git a/src/glsl/loop_unroll.cpp b/src/glsl/loop_unroll.cpp index 80f9217159..90797bde37 100644 --- a/src/glsl/loop_unroll.cpp +++ b/src/glsl/loop_unroll.cpp @@ -47,6 +47,7 @@ ir_visitor_status loop_unroll_visitor::visit_leave(ir_loop *ir) { loop_variable_state *const ls = this->state->get(ir); + int iterations; /* If we've entered a loop that hasn't been analyzed, something really, * really bad has happened. @@ -56,23 +57,107 @@ loop_unroll_visitor::visit_leave(ir_loop *ir) return visit_continue; } + iterations = ls->max_iterations; + /* Don't try to unroll loops where the number of iterations is not known * at compile-time. */ - if (ls->max_iterations < 0) + if (iterations < 0) return visit_continue; /* Don't try to unroll loops that have zillions of iterations either. */ - if (ls->max_iterations > max_iterations) + if (iterations > max_iterations) return visit_continue; - if (ls->num_loop_jumps > 0) + if (ls->num_loop_jumps > 1) return visit_continue; + else if (ls->num_loop_jumps) { + /* recognize loops in the form produced by ir_lower_jumps */ + ir_instruction *last_ir = + ((ir_instruction*)ir->body_instructions.get_tail()); + + assert(last_ir != NULL); + + ir_if *last_if = last_ir->as_if(); + if (last_if) { + bool continue_from_then_branch; + + /* Determine which if-statement branch, if any, ends with a break. + * The branch that did *not* have the break will get a temporary + * continue inserted in each iteration of the loop unroll. + * + * Note that since ls->num_loop_jumps is <= 1, it is impossible for + * both branches to end with a break. + */ + ir_instruction *last = + (ir_instruction *) last_if->then_instructions.get_tail(); + + if (last && last->ir_type == ir_type_loop_jump + && ((ir_loop_jump*) last)->is_break()) { + continue_from_then_branch = false; + } else { + last = (ir_instruction *) last_if->then_instructions.get_tail(); + + if (last && last->ir_type == ir_type_loop_jump + && ((ir_loop_jump*) last)->is_break()) + continue_from_then_branch = true; + else + /* Bail out if neither if-statement branch ends with a break. + */ + return visit_continue; + } + + /* Remove the break from the if-statement. + */ + last->remove(); + + void *const mem_ctx = talloc_parent(ir); + ir_instruction *ir_to_replace = ir; + + for (int i = 0; i < iterations; i++) { + exec_list copy_list; + + copy_list.make_empty(); + clone_ir_list(mem_ctx, ©_list, &ir->body_instructions); + + last_if = ((ir_instruction*)copy_list.get_tail())->as_if(); + assert(last_if); + + ir_to_replace->insert_before(©_list); + ir_to_replace->remove(); + + /* placeholder that will be removed in the next iteration */ + ir_to_replace = + new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue); + + exec_list *const list = (continue_from_then_branch) + ? &last_if->then_instructions : &last_if->else_instructions; + + list->push_tail(ir_to_replace); + } + + ir_to_replace->remove(); + + this->progress = true; + return visit_continue; + } else if (last_ir->ir_type == ir_type_loop_jump + && ((ir_loop_jump *)last_ir)->is_break()) { + /* If the only loop-jump is a break at the end of the loop, the loop + * will execute exactly once. Remove the break, set the iteration + * count, and fall through to the normal unroller. + */ + last_ir->remove(); + iterations = 1; + + this->progress = true; + } else + return visit_continue; + } void *const mem_ctx = talloc_parent(ir); - for (int i = 0; i < ls->max_iterations; i++) { + for (int i = 0; i < iterations; i++) { exec_list copy_list; copy_list.make_empty(); |