glsl: lower unconditional returns and continues in loops.

Previously, lower_jumps.cpp would only lower return and continue
statements that appeared inside conditionals.  This patch makes it
lower unconditional returns and continue statements that occur inside
a loop.

Such unconditional flow control statements would be unlikely to be
explicitly coded by a reasonable user, however they might arise as a
result of other optimizations.

Without this patch, lower_jumps.cpp might not lower certain return and
continue statements, causing some backends to fail.

Fixes unit tests test_lower_return_void_at_end_of_loop and
test_remove_continue_at_end_of_loop.
(cherry picked from commit 03145ba655)

Conflicts:

	src/glsl/lower_jumps.cpp
This commit is contained in:
Paul Berry 2011-10-02 18:44:31 +02:00 committed by Marek Olšák
parent d1786cea1c
commit acd2a03ffb

View file

@ -219,6 +219,43 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor {
}
}
/**
* Insert the instructions necessary to lower a return statement,
* before the given return instruction.
*/
void insert_lowered_return(ir_return *ir)
{
ir_variable* return_flag = this->function.get_return_flag();
if(!this->function.signature->return_type->is_void()) {
ir_variable* return_value = this->function.get_return_value();
ir->insert_before(
new(ir) ir_assignment(
new (ir) ir_dereference_variable(return_value),
ir->value));
}
ir->insert_before(
new(ir) ir_assignment(
new (ir) ir_dereference_variable(return_flag),
new (ir) ir_constant(true)));
this->loop.may_set_return_flag = true;
}
/**
* If the given instruction is a return, lower it to instructions
* that store the return value (if there is one), set the return
* flag, and then break.
*
* It is safe to pass NULL to this function.
*/
void lower_return_unconditionally(ir_instruction *ir)
{
if (get_jump_strength(ir) != strength_return) {
return;
}
insert_lowered_return((ir_return*)ir);
ir->replace_with(new(ir) ir_loop_jump(ir_loop_jump::jump_break));
}
virtual void visit(class ir_loop_jump * ir)
{
truncate_after_instruction(ir);
@ -369,13 +406,12 @@ retry: /* we get here if we put code after the if inside a branch */
break;
if(jump_strengths[lower] == strength_return) {
ir_variable* return_flag = this->function.get_return_flag();
if(!this->function.signature->return_type->is_void()) {
ir_variable* return_value = this->function.get_return_value();
jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_value), ((ir_return*)jumps[lower])->value, NULL));
}
jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(return_flag), new (ir) ir_constant(true), NULL));
this->loop.may_set_return_flag = true;
/* To lower a return, we create a return flag (if the
* function doesn't have one already) and add instructions
* that: 1. store the return value (if this function has a
* non-void return) and 2. set the return flag
*/
insert_lowered_return((ir_return*)jumps[lower]);
if(this->loop.loop) {
ir_loop_jump* lowered = 0;
lowered = new(ir) ir_loop_jump(ir_loop_jump::jump_break);
@ -496,8 +532,28 @@ lower_continue:
loop_record saved_loop = this->loop;
this->loop = loop_record(this->function.signature, ir);
/* Recursively lower nested jumps. This satisfies the
* CONTAINED_JUMPS_LOWERED postcondition, except in the case of
* an unconditional continue or return at the bottom of the
* loop, which are handled below.
*/
block_record body = visit_block(&ir->body_instructions);
/* If the loop ends in an unconditional continue, eliminate it
* because it is redundant.
*/
ir_instruction *ir_last
= (ir_instruction *) ir->body_instructions.get_tail();
if (get_jump_strength(ir_last) == strength_continue) {
ir_last->remove();
}
/* If the loop ends in an unconditional return, and we are
* lowering returns, lower it.
*/
if (this->function.lower_return)
lower_return_unconditionally(ir_last);
if(body.min_strength >= strength_break) {
/* FINISHME: turn the this->loop into an if, or replace it with its body */
}