diff options
-rw-r--r-- | src/glsl/Makefile | 2 | ||||
-rw-r--r-- | src/glsl/SConscript | 2 | ||||
-rw-r--r-- | src/glsl/glsl_parser_extras.cpp | 2 | ||||
-rw-r--r-- | src/glsl/ir_if_return.cpp | 246 | ||||
-rw-r--r-- | src/glsl/ir_lower_jumps.cpp | 544 | ||||
-rw-r--r-- | src/glsl/ir_optimization.h | 2 |
6 files changed, 548 insertions, 250 deletions
diff --git a/src/glsl/Makefile b/src/glsl/Makefile index e869b08cf69..a0e7c05d67f 100644 --- a/src/glsl/Makefile +++ b/src/glsl/Makefile @@ -50,10 +50,10 @@ CXX_SOURCES = \ ir_function_inlining.cpp \ ir_hierarchical_visitor.cpp \ ir_hv_accept.cpp \ - ir_if_return.cpp \ ir_if_simplification.cpp \ ir_if_to_cond_assign.cpp \ ir_import_prototypes.cpp \ + ir_lower_jumps.cpp \ ir_mat_op_to_vec.cpp \ ir_mod_to_fract.cpp \ ir_noop_swizzle.cpp \ diff --git a/src/glsl/SConscript b/src/glsl/SConscript index a568342073c..bd324eefef5 100644 --- a/src/glsl/SConscript +++ b/src/glsl/SConscript @@ -47,10 +47,10 @@ sources = [ 'ir_function_inlining.cpp', 'ir_hierarchical_visitor.cpp', 'ir_hv_accept.cpp', - 'ir_if_return.cpp', 'ir_if_simplification.cpp', 'ir_if_to_cond_assign.cpp', 'ir_import_prototypes.cpp', + 'ir_lower_jumps.cpp', 'ir_mat_op_to_vec.cpp', 'ir_mod_to_fract.cpp', 'ir_noop_swizzle.cpp', diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index 400203d261a..28241f7dd3f 100644 --- a/src/glsl/glsl_parser_extras.cpp +++ b/src/glsl/glsl_parser_extras.cpp @@ -711,7 +711,7 @@ do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iteration progress = do_constant_variable_unlinked(ir) || progress; progress = do_constant_folding(ir) || progress; progress = do_algebraic(ir) || progress; - progress = do_if_return(ir) || progress; + progress = do_lower_jumps(ir) || progress; progress = do_vec_index_to_swizzle(ir) || progress; progress = do_swizzle_swizzle(ir) || progress; progress = do_noop_swizzle(ir) || progress; diff --git a/src/glsl/ir_if_return.cpp b/src/glsl/ir_if_return.cpp deleted file mode 100644 index 5ab8759958f..00000000000 --- a/src/glsl/ir_if_return.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file ir_if_return.cpp - * - * This pass tries to normalize functions to always return from one - * place by moving around blocks of code in if statements. - * - * This helps on hardware with no branching support, and may even be a - * useful transform on hardware supporting control flow by turning - * masked returns into normal returns. - */ - -#include <string.h> -#include "glsl_types.h" -#include "ir.h" - -class ir_if_return_visitor : public ir_hierarchical_visitor { -public: - ir_if_return_visitor() - { - this->progress = false; - } - - ir_visitor_status visit_enter(ir_function_signature *); - ir_visitor_status visit_leave(ir_if *); - - ir_visitor_status move_outer_block_inside(ir_instruction *ir, - exec_list *inner_block); - void move_returns_after_block(ir_instruction *ir, - ir_return *then_return, - ir_return *else_return); - bool progress; -}; - -bool -do_if_return(exec_list *instructions) -{ - ir_if_return_visitor v; - - do { - v.progress = false; - visit_list_elements(&v, instructions); - } while (v.progress); - - return v.progress; -} - -/** - * Removes any instructions after a (unconditional) return, since they will - * never be executed. - */ -static void -truncate_after_instruction(ir_instruction *ir) -{ - if (!ir) - return; - - while (!ir->get_next()->is_tail_sentinel()) - ((ir_instruction *)ir->get_next())->remove(); -} - -/** - * Returns an ir_instruction of the first ir_return in the exec_list, or NULL. - */ -static ir_return * -find_return_in_block(exec_list *instructions) -{ - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - if (ir->ir_type == ir_type_return) - return (ir_return *)ir; - } - - return NULL; -} - -void -ir_if_return_visitor::move_returns_after_block(ir_instruction *ir, - ir_return *then_return, - ir_return *else_return) -{ - - if (!then_return->value) { - then_return->remove(); - else_return->remove(); - ir->insert_after(new(ir) ir_return(NULL)); - } else { - ir_assignment *assign; - ir_variable *new_var = new(ir) ir_variable(then_return->value->type, - "if_return_tmp", - ir_var_temporary); - ir->insert_before(new_var); - - assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(new_var), - then_return->value, NULL); - then_return->replace_with(assign); - - assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(new_var), - else_return->value, NULL); - else_return->replace_with(assign); - - ir_dereference_variable *deref = new(ir) ir_dereference_variable(new_var); - ir->insert_after(new(ir) ir_return(deref)); - } - this->progress = true; -} - -ir_visitor_status -ir_if_return_visitor::move_outer_block_inside(ir_instruction *ir, - exec_list *inner_block) -{ - if (!ir->get_next()->is_tail_sentinel()) { - while (!ir->get_next()->is_tail_sentinel()) { - ir_instruction *move_ir = (ir_instruction *)ir->get_next(); - - move_ir->remove(); - inner_block->push_tail(move_ir); - } - - /* If we move the instructions following ir inside the block, it - * will confuse the exec_list iteration in the parent that visited - * us. So stop the visit at this point. - */ - return visit_stop; - } else { - return visit_continue; - } -} - -/* Normalize a function to always have a return statement at the end. - * - * This avoids the ir_if handler needing to know whether it is at the - * top level of the function to know if there's an implicit return at - * the end of the outer block. - */ -ir_visitor_status -ir_if_return_visitor::visit_enter(ir_function_signature *ir) -{ - ir_return *ret; - - if (!ir->is_defined) - return visit_continue_with_parent; - if (strcmp(ir->function_name(), "main") == 0) - return visit_continue_with_parent; - - ret = find_return_in_block(&ir->body); - - if (ret) { - truncate_after_instruction(ret); - } else { - if (ir->return_type->is_void()) { - ir->body.push_tail(new(ir) ir_return(NULL)); - } else { - /* Probably, if we've got a function with a return value - * hitting this point, it's something like: - * - * float reduce_below_half(float val) - * { - * while () { - * if (val >= 0.5) - * val /= 2.0; - * else - * return val; - * } - * } - * - * So we gain a junk return statement of an undefined value - * at the end that never gets executed. However, a backend - * using this pass is probably desperate to get rid of - * function calls, so go ahead and do it for their sake in - * case it fixes apps. - */ - ir_variable *undef = new(ir) ir_variable(ir->return_type, - "if_return_undef", - ir_var_temporary); - ir->body.push_tail(undef); - - ir_dereference_variable *deref = new(ir) ir_dereference_variable(undef); - ir->body.push_tail(new(ir) ir_return(deref)); - } - } - - return visit_continue; -} - -ir_visitor_status -ir_if_return_visitor::visit_leave(ir_if *ir) -{ - ir_return *then_return; - ir_return *else_return; - - then_return = find_return_in_block(&ir->then_instructions); - else_return = find_return_in_block(&ir->else_instructions); - if (!then_return && !else_return) - return visit_continue; - - /* Trim off any trailing instructions after the return statements - * on both sides. - */ - truncate_after_instruction(then_return); - truncate_after_instruction(else_return); - - /* If both sides return, then we can move the returns to a single - * one outside the if statement. - */ - if (then_return && else_return) { - move_returns_after_block(ir, then_return, else_return); - return visit_continue; - } - - /* If only one side returns, then the block of code after the "if" - * is only executed by the other side, so those instructions don't - * need to be anywhere but that other side. - * - * This will usually pull a return statement up into the other - * side, so we'll trigger the above case on the next pass. - */ - if (then_return) { - return move_outer_block_inside(ir, &ir->else_instructions); - } else { - assert(else_return); - return move_outer_block_inside(ir, &ir->then_instructions); - } -} diff --git a/src/glsl/ir_lower_jumps.cpp b/src/glsl/ir_lower_jumps.cpp new file mode 100644 index 00000000000..79eb6f0939d --- /dev/null +++ b/src/glsl/ir_lower_jumps.cpp @@ -0,0 +1,544 @@ +/* + * Copyright © 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file ir_remove_loop_jumps.cpp + */ + +#include "glsl_types.h" +#include <string.h> +#include "ir.h" + +enum jump_strength +{ + strength_none, + strength_always_clears_execute_flag, + strength_continue, + strength_break, + strength_return, + strength_discard +}; + +struct block_record +{ + /* minimum jump strength (of lowered IR, not pre-lowering IR) + * + * If the block ends with a jump, must be the strength of the jump. + * Otherwise, the jump would be dead and have been deleted before) + * + * If the block doesn't end with a jump, it can be different than strength_none if all paths before it lead to some jump + * (e.g. an if with a return in one branch, and a break in the other, while not lowering them) + * Note that identical jumps are usually unified though. + */ + jump_strength min_strength; + + /* can anything clear the execute flag? */ + bool may_clear_execute_flag; + + block_record() + { + this->min_strength = strength_none; + this->may_clear_execute_flag = false; + } +}; + +struct loop_record +{ + ir_function_signature* signature; + ir_loop* loop; + + /* used to avoid lowering the break used to represent lowered breaks */ + unsigned nesting_depth; + bool in_if_at_the_end_of_the_loop; + + bool may_set_return_flag; + + ir_variable* break_flag; + ir_variable* execute_flag; /* cleared to emulate continue */ + + loop_record(ir_function_signature* p_signature = 0, ir_loop* p_loop = 0) + { + this->signature = p_signature; + this->loop = p_loop; + this->nesting_depth = 0; + this->in_if_at_the_end_of_the_loop = false; + this->may_set_return_flag = false; + this->break_flag = 0; + this->execute_flag = 0; + } + + ir_variable* get_execute_flag() + { + /* also supported for the "function loop" */ + if(!this->execute_flag) { + exec_list& list = this->loop ? this->loop->body_instructions : signature->body; + this->execute_flag = new(this->signature) ir_variable(glsl_type::bool_type, "execute_flag", ir_var_temporary); + list.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(execute_flag), new(this->signature) ir_constant(true), 0)); + list.push_head(this->execute_flag); + } + return this->execute_flag; + } + + ir_variable* get_break_flag() + { + assert(this->loop); + if(!this->break_flag) { + this->break_flag = new(this->signature) ir_variable(glsl_type::bool_type, "break_flag", ir_var_temporary); + this->loop->insert_before(this->break_flag); + this->loop->insert_before(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(break_flag), new(this->signature) ir_constant(false), 0)); + } + return this->break_flag; + } +}; + +struct function_record +{ + ir_function_signature* signature; + ir_variable* return_flag; /* used to break out of all loops and then jump to the return instruction */ + ir_variable* return_value; + bool is_main; + unsigned nesting_depth; + + function_record(ir_function_signature* p_signature = 0) + { + this->signature = p_signature; + this->return_flag = 0; + this->return_value = 0; + this->nesting_depth = 0; + this->is_main = this->signature && (strcmp(this->signature->function_name(), "main") == 0); + } + + ir_variable* get_return_flag() + { + if(!this->return_flag) { + this->return_flag = new(this->signature) ir_variable(glsl_type::bool_type, "return_flag", ir_var_temporary); + this->signature->body.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(return_flag), new(this->signature) ir_constant(false), 0)); + this->signature->body.push_head(this->return_flag); + } + return this->return_flag; + } + + ir_variable* get_return_value() + { + if(!this->return_value) { + assert(!this->signature->return_type->is_void()); + return_value = new(this->signature) ir_variable(this->signature->return_type, "return_value", ir_var_temporary); + this->signature->body.push_head(this->return_value); + } + return this->return_value; + } +}; + +struct ir_lower_jumps_visitor : public ir_control_flow_visitor { + bool progress; + + struct function_record function; + struct loop_record loop; + struct block_record block; + + bool pull_out_jumps; + bool lower_continue; + bool lower_break; + bool lower_sub_return; + bool lower_main_return; + + ir_lower_jumps_visitor() + { + this->progress = false; + } + + void truncate_after_instruction(exec_node *ir) + { + if (!ir) + return; + + while (!ir->get_next()->is_tail_sentinel()) { + ((ir_instruction *)ir->get_next())->remove(); + this->progress = true; + } + } + + void move_outer_block_inside(ir_instruction *ir, exec_list *inner_block) + { + while (!ir->get_next()->is_tail_sentinel()) { + ir_instruction *move_ir = (ir_instruction *)ir->get_next(); + + move_ir->remove(); + inner_block->push_tail(move_ir); + } + } + + virtual void visit(class ir_loop_jump * ir) + { + truncate_after_instruction(ir); + this->block.min_strength = ir->is_break() ? strength_break : strength_continue; + } + + virtual void visit(class ir_return * ir) + { + truncate_after_instruction(ir); + this->block.min_strength = strength_return; + } + + virtual void visit(class ir_discard * ir) + { + truncate_after_instruction(ir); + this->block.min_strength = strength_discard; + } + + enum jump_strength get_jump_strength(ir_instruction* ir) + { + if(!ir) + return strength_none; + else if(ir->ir_type == ir_type_loop_jump) { + if(((ir_loop_jump*)ir)->is_break()) + return strength_break; + else + return strength_continue; + } else if(ir->ir_type == ir_type_return) + return strength_return; + else if(ir->ir_type == ir_type_discard) + return strength_discard; + else + return strength_none; + } + + bool should_lower_jump(ir_jump* ir) + { + unsigned strength = get_jump_strength(ir); + bool lower; + switch(strength) + { + case strength_none: + lower = false; /* don't change this, code relies on it */ + break; + case strength_continue: + lower = lower_continue; + break; + case strength_break: + assert(this->loop.loop); + /* never lower "canonical break" */ + if(ir->get_next()->is_tail_sentinel() && (this->loop.nesting_depth == 0 + || (this->loop.nesting_depth == 1 && this->loop.in_if_at_the_end_of_the_loop))) + lower = false; + else + lower = lower_break; + break; + case strength_return: + /* never lower return at the end of a this->function */ + if(this->function.nesting_depth == 0 && ir->get_next()->is_tail_sentinel()) + lower = false; + else if (this->function.is_main) + lower = lower_main_return; + else + lower = lower_sub_return; + break; + case strength_discard: + lower = false; /* probably nothing needs this lowered */ + break; + } + return lower; + } + + block_record visit_block(exec_list* list) + { + block_record saved_block = this->block; + this->block = block_record(); + visit_exec_list(list, this); + block_record ret = this->block; + this->block = saved_block; + return ret; + } + + virtual void visit(ir_if *ir) + { + if(this->loop.nesting_depth == 0 && ir->get_next()->is_tail_sentinel()) + this->loop.in_if_at_the_end_of_the_loop = true; + + ++this->function.nesting_depth; + ++this->loop.nesting_depth; + + block_record block_records[2]; + ir_jump* jumps[2]; + + block_records[0] = visit_block(&ir->then_instructions); + block_records[1] = visit_block(&ir->else_instructions); + +retry: /* we get here if we put code after the if inside a branch */ + for(unsigned i = 0; i < 2; ++i) { + exec_list& list = i ? ir->else_instructions : ir->then_instructions; + jumps[i] = 0; + if(!list.is_empty() && get_jump_strength((ir_instruction*)list.get_tail())) + jumps[i] = (ir_jump*)list.get_tail(); + } + + for(;;) { + jump_strength jump_strengths[2]; + + for(unsigned i = 0; i < 2; ++i) { + if(jumps[i]) { + jump_strengths[i] = block_records[i].min_strength; + assert(jump_strengths[i] == get_jump_strength(jumps[i])); + } else + jump_strengths[i] = strength_none; + } + + /* move both jumps out if possible */ + if(pull_out_jumps && jump_strengths[0] == jump_strengths[1]) { + bool unify = true; + if(jump_strengths[0] == strength_continue) + ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_continue)); + else if(jump_strengths[0] == strength_break) + ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); + /* FINISHME: unify returns with identical expressions */ + else if(jump_strengths[0] == strength_return && this->function.signature->return_type->is_void()) + ir->insert_after(new(ir) ir_return(NULL)); + /* FINISHME: unify discards */ + else + unify = false; + + if(unify) { + jumps[0]->remove(); + jumps[1]->remove(); + this->progress = true; + + jumps[0] = 0; + jumps[1] = 0; + block_records[0].min_strength = strength_none; + block_records[1].min_strength = strength_none; + break; + } + } + + /* lower a jump: if both need to lowered, start with the strongest one, so that + * we might later unify the lowered version with the other one + */ + bool should_lower[2]; + for(unsigned i = 0; i < 2; ++i) + should_lower[i] = should_lower_jump(jumps[i]); + + int lower; + if(should_lower[1] && should_lower[0]) + lower = jump_strengths[1] > jump_strengths[0]; + else if(should_lower[0]) + lower = 0; + else if(should_lower[1]) + lower = 1; + else + 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; + if(this->loop.loop) { + ir_loop_jump* lowered = 0; + lowered = new(ir) ir_loop_jump(ir_loop_jump::jump_break); + block_records[lower].min_strength = strength_break; + jumps[lower]->replace_with(lowered); + jumps[lower] = lowered; + } else + goto lower_continue; + this->progress = true; + } else if(jump_strengths[lower] == strength_break) { + /* We can't lower to an actual continue because that would execute the increment. + * + * In the lowered code, we instead put the break check between the this->loop body and the increment, + * which is impossible with a real continue as defined by the GLSL IR currently. + * + * Smarter options (such as undoing the increment) are possible but it's not worth implementing them, + * because if break is lowered, continue is almost surely lowered too. + */ + jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(this->loop.get_break_flag()), new (ir) ir_constant(true), 0)); + goto lower_continue; + } else if(jump_strengths[lower] == strength_continue) { +lower_continue: + ir_variable* execute_flag = this->loop.get_execute_flag(); + jumps[lower]->replace_with(new(ir) ir_assignment(new (ir) ir_dereference_variable(execute_flag), new (ir) ir_constant(false), 0)); + jumps[lower] = 0; + block_records[lower].min_strength = strength_always_clears_execute_flag; + block_records[lower].may_clear_execute_flag = true; + this->progress = true; + break; + } + } + + /* move out a jump out if possible */ + if(pull_out_jumps) { + int move_out = -1; + if(jumps[0] && block_records[1].min_strength >= strength_continue) + move_out = 0; + else if(jumps[1] && block_records[0].min_strength >= strength_continue) + move_out = 1; + + if(move_out >= 0) + { + jumps[move_out]->remove(); + ir->insert_after(jumps[move_out]); + jumps[move_out] = 0; + block_records[move_out].min_strength = strength_none; + this->progress = true; + } + } + + if(block_records[0].min_strength < block_records[1].min_strength) + this->block.min_strength = block_records[0].min_strength; + else + this->block.min_strength = block_records[1].min_strength; + this->block.may_clear_execute_flag = this->block.may_clear_execute_flag || block_records[0].may_clear_execute_flag || block_records[1].may_clear_execute_flag; + + if(this->block.min_strength) + truncate_after_instruction(ir); + else if(this->block.may_clear_execute_flag) + { + int move_into = -1; + if(block_records[0].min_strength && !block_records[1].may_clear_execute_flag) + move_into = 1; + else if(block_records[1].min_strength && !block_records[0].may_clear_execute_flag) + move_into = 0; + + if(move_into >= 0) { + assert(!block_records[move_into].min_strength && !block_records[move_into].may_clear_execute_flag); /* otherwise, we just truncated */ + + exec_list* list = move_into ? &ir->else_instructions : &ir->then_instructions; + exec_node* next = ir->get_next(); + if(!next->is_tail_sentinel()) { + move_outer_block_inside(ir, list); + + exec_list list; + list.head = next; + block_records[move_into] = visit_block(&list); + + this->progress = true; + goto retry; + } + } else { + ir_instruction* ir_after; + for(ir_after = (ir_instruction*)ir->get_next(); !ir_after->is_tail_sentinel();) + { + ir_if* ir_if = ir_after->as_if(); + if(ir_if && ir_if->else_instructions.is_empty()) { + ir_dereference_variable* ir_if_cond_deref = ir_if->condition->as_dereference_variable(); + if(ir_if_cond_deref && ir_if_cond_deref->var == this->loop.execute_flag) { + ir_instruction* ir_next = (ir_instruction*)ir_after->get_next(); + ir_after->insert_before(&ir_if->then_instructions); + ir_after->remove(); + ir_after = ir_next; + continue; + } + } + ir_after = (ir_instruction*)ir_after->get_next(); + + /* only set this if we find any unprotected instruction */ + this->progress = true; + } + + if(!ir->get_next()->is_tail_sentinel()) { + assert(this->loop.execute_flag); + ir_if* if_execute = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.execute_flag)); + move_outer_block_inside(ir, &if_execute->then_instructions); + ir->insert_after(if_execute); + } + } + } + --this->loop.nesting_depth; + --this->function.nesting_depth; + } + + virtual void visit(ir_loop *ir) + { + ++this->function.nesting_depth; + loop_record saved_loop = this->loop; + this->loop = loop_record(this->function.signature, ir); + + block_record body = visit_block(&ir->body_instructions); + + if(body.min_strength >= strength_break) { + /* FINISHME: turn the this->loop into an if, or replace it with its body */ + } + + if(this->loop.break_flag) { + 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); + } + + if(this->loop.may_set_return_flag) { + assert(this->function.return_flag); + ir_if* return_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->function.return_flag)); + return_if->then_instructions.push_tail(new(ir) ir_loop_jump(saved_loop.loop ? ir_loop_jump::jump_break : ir_loop_jump::jump_continue)); + ir->insert_after(return_if); + } + + this->loop = saved_loop; + --this->function.nesting_depth; + } + + virtual void visit(ir_function_signature *ir) + { + /* these are not strictly necessary */ + assert(!this->function.signature); + assert(!this->loop.loop); + + function_record saved_function = this->function; + loop_record saved_loop = this->loop; + this->function = function_record(ir); + this->loop = loop_record(ir); + + assert(!this->loop.loop); + visit_block(&ir->body); + + if(this->function.return_value) + ir->body.push_tail(new(ir) ir_return(new (ir) ir_dereference_variable(this->function.return_value))); + + this->loop = saved_loop; + this->function = saved_function; + } + + virtual void visit(class ir_function * ir) + { + visit_block(&ir->signatures); + } +}; + +bool +do_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_return, bool lower_main_return, bool lower_continue, bool lower_break) +{ + ir_lower_jumps_visitor v; + v.pull_out_jumps = pull_out_jumps; + v.lower_continue = lower_continue; + v.lower_break = lower_break; + v.lower_sub_return = lower_sub_return; + v.lower_main_return = lower_main_return; + + do { + v.progress = false; + visit_exec_list(instructions, &v); + } while (v.progress); + + return v.progress; +} diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h index 5347158a0d8..726a6ad97dd 100644 --- a/src/glsl/ir_optimization.h +++ b/src/glsl/ir_optimization.h @@ -43,7 +43,7 @@ bool do_dead_functions(exec_list *instructions); bool do_div_to_mul_rcp(exec_list *instructions); bool do_explog_to_explog2(exec_list *instructions); bool do_function_inlining(exec_list *instructions); -bool do_if_return(exec_list *instructions); +bool do_lower_jumps(exec_list *instructions, bool pull_out_jumps = true, bool lower_sub_return = true, bool lower_main_return = false, bool lower_continue = false, bool lower_break = false); bool do_if_simplification(exec_list *instructions); bool do_if_to_cond_assign(exec_list *instructions); bool do_mat_op_to_vec(exec_list *instructions); |