diff options
author | Paul Berry <[email protected]> | 2011-07-01 17:29:35 -0700 |
---|---|---|
committer | Paul Berry <[email protected]> | 2011-07-08 09:59:30 -0700 |
commit | 067c9d7bd776260298ceabda026425ed7e4eb161 (patch) | |
tree | a119596fb68b44926f8c6a299ab3e9d9b74de678 | |
parent | e71b4ab8a64bf978b2036976a41e30996eebb0c8 (diff) |
glsl: Lower break instructions when necessary at the end of a loop.
Normally lower_jumps.cpp doesn't need to lower a break instruction
that occurs at the end of a loop, because all back-ends can produce
proper GPU instructions for a break instruction in this "canonical"
location. However, if other break instructions within the loop are
already being lowered, then a break instruction at the end of the loop
needs to be lowered too, since after the optimization is complete a
new conditional break will be inserted at the end of the loop.
Without this patch, lower_jumps.cpp may require multiple passes in
order to lower all jumps. This results in sub-optimal output because
lower_jumps.cpp produces a brand new set of temporary variables each
time it is run, and the redundant temporary variables are not
guaranteed to be eliminated by later optimization passes.
Fixes unit test test_lower_breaks_6.
-rw-r--r-- | src/glsl/lower_jumps.cpp | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/src/glsl/lower_jumps.cpp b/src/glsl/lower_jumps.cpp index 07897825b49..61874990a94 100644 --- a/src/glsl/lower_jumps.cpp +++ b/src/glsl/lower_jumps.cpp @@ -341,6 +341,50 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor { ir->replace_with(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); } + /** + * Create the necessary instruction to replace a break instruction. + */ + ir_instruction *create_lowered_break() + { + void *ctx = this->function.signature; + return new(ctx) ir_assignment( + new(ctx) ir_dereference_variable(this->loop.get_break_flag()), + new(ctx) ir_constant(true), + 0); + } + + /** + * If the given instruction is a break, lower it to an instruction + * that sets the break flag, without consulting + * should_lower_jump(). + * + * It is safe to pass NULL to this function. + */ + void lower_break_unconditionally(ir_instruction *ir) + { + if (get_jump_strength(ir) != strength_break) { + return; + } + ir->replace_with(create_lowered_break()); + } + + /** + * If the block ends in a conditional or unconditional break, lower + * it, even though should_lower_jump() says it needn't be lowered. + */ + void lower_final_breaks(exec_list *block) + { + ir_instruction *ir = (ir_instruction *) block->get_tail(); + lower_break_unconditionally(ir); + ir_if *ir_if = ir->as_if(); + if (ir_if) { + lower_break_unconditionally( + (ir_instruction *) ir_if->then_instructions.get_tail()); + lower_break_unconditionally( + (ir_instruction *) ir_if->else_instructions.get_tail()); + } + } + virtual void visit(class ir_loop_jump * ir) { /* Eliminate all instructions after each one, since they are @@ -616,7 +660,7 @@ retry: /* we get here if we put code after the if inside a branch */ * The visit() function for the loop will ensure that the * break flag is checked after executing the loop body. */ - jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(this->loop.get_break_flag()), new (ir) ir_constant(true), 0)); + jumps[lower]->insert_before(create_lowered_break()); goto lower_continue; } else if(jump_strengths[lower] == strength_continue) { lower_continue: @@ -836,6 +880,9 @@ lower_continue: } if(this->loop.break_flag) { + /* We only get here if we are lowering breaks */ + assert (lower_break); + /* If a break flag was generated while visiting the body of * the loop, then at least one break was lowered, so we need * to generate an if statement at the end of the loop that @@ -843,7 +890,13 @@ lower_continue: * generate won't violate the CONTAINED_JUMPS_LOWERED * postcondition, because should_lower_jump() always returns * false for a break that happens at the end of a loop. + * + * However, if the loop already ends in a conditional or + * unconditional break, then we need to lower that break, + * because it won't be at the end of the loop anymore. */ + lower_final_breaks(&ir->body_instructions); + ir_if* break_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.break_flag)); break_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); ir->body_instructions.push_tail(break_if); |