Commit b8481d83 authored by Luca Barbieri's avatar Luca Barbieri

glsl/loop_unroll: unroll loops with cond breaks anywhere, not just the end

Currently we only unroll loops with conditional breaks at the end, which is
the form that ir_lower_jumps generates.

However, if breaks are not lowered, they tend to appear at the beginning, so
add support for a conditional break anywhere.
parent 20c87414
......@@ -73,44 +73,77 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
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();
ir_instruction *last_ir = ((ir_instruction*)ir->body_instructions.get_tail());
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 {
ir_if *ir_if = 0;
ir_instruction *break_ir = 0;
bool continue_from_then_branch = false;
foreach_list(node, &ir->body_instructions) {
/* recognize loops in the form produced by ir_lower_jumps */
ir_instruction* cur_ir = (ir_instruction*)node;
assert(last_ir != NULL);
ir_if = cur_ir->as_if();
if (ir_if) {
/* 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 *ir_if_last =
(ir_instruction *) ir_if->then_instructions.get_tail();
if (ir_if_last && ir_if_last->ir_type == ir_type_loop_jump
&& ((ir_loop_jump*) ir_if_last)->is_break()) {
continue_from_then_branch = false;
break_ir = ir_if_last;
break;
} else {
ir_if_last = (ir_instruction *) ir_if->then_instructions.get_tail();
if (ir_if_last && ir_if_last->ir_type == ir_type_loop_jump
&& ((ir_loop_jump*) ir_if_last)->is_break())
{
break_ir = ir_if_last;
continue_from_then_branch = true;
break;
}
}
}
}
if(!break_ir)
return visit_continue;
/* move instructions after then if in the continue branch */
while (!ir_if->get_next()->is_tail_sentinel()) {
ir_instruction *move_ir = (ir_instruction *)ir_if->get_next();
move_ir->remove();
if(continue_from_then_branch)
ir_if->then_instructions.push_tail(move_ir);
else
ir_if->else_instructions.push_tail(move_ir);
}
/* Remove the break from the if-statement.
*/
break_ir->remove();
void *const mem_ctx = talloc_parent(ir);
ir_instruction *ir_to_replace = ir;
......@@ -121,18 +154,18 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
copy_list.make_empty();
clone_ir_list(mem_ctx, &copy_list, &ir->body_instructions);
last_if = ((ir_instruction*)copy_list.get_tail())->as_if();
assert(last_if);
ir_if = ((ir_instruction*)copy_list.get_tail())->as_if();
assert(ir_if);
ir_to_replace->insert_before(&copy_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);
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;
? &ir_if->then_instructions : &ir_if->else_instructions;
list->push_tail(ir_to_replace);
}
......@@ -141,18 +174,7 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment