diff options
author | Luca Barbieri <luca@luca-barbieri.com> | 2010-09-14 04:15:25 +0200 |
---|---|---|
committer | Luca Barbieri <luca@luca-barbieri.com> | 2010-09-14 15:50:47 +0200 |
commit | 11f5a8dbe753dacfa371002d272ea842998a2b16 (patch) | |
tree | 44ea1518566ebda03dbbde4ad03248e5d8e0a7df | |
parent | 759b5db9168f7c0ecc55d259b518f8c32be946fb (diff) |
glsl/loop_analysis: fix miscompilation with continues before cond breaks
In the following case, we currently incorrectly determine that the loop
has at most 8 iterations, while it is in fact an infinite loop.
for(;; ++i)
{
foo();
if(i >= 4) continue;
if(i < 8) break;
}
To fix this, stop looking for induction variable terminators if we hit
a continue, or a continue nested inside ifs.
-rw-r--r-- | src/glsl/loop_analysis.cpp | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/src/glsl/loop_analysis.cpp b/src/glsl/loop_analysis.cpp index 91e34da0ee..687825cc93 100644 --- a/src/glsl/loop_analysis.cpp +++ b/src/glsl/loop_analysis.cpp @@ -32,6 +32,37 @@ static bool all_expression_operands_are_loop_constant(ir_rvalue *, static ir_rvalue *get_basic_induction_increment(ir_assignment *, hash_table *); +struct contains_continue_visitor : public ir_hierarchical_visitor +{ + bool contains_continue; + + contains_continue_visitor() + { + this->contains_continue = false; + } + + virtual ir_visitor_status visit(ir_loop_jump *ir) + { + if(ir->is_continue()) { + contains_continue = true; + return visit_stop; + } + return visit_continue; + } + + virtual ir_visitor_status visit_enter(class ir_loop *) + { + /* continues inside nested loops are harmless */ + return visit_continue_with_parent; + } +}; + +bool contains_continue(ir_instruction* ir) +{ + contains_continue_visitor visitor; + ir->accept(&visitor); + return visitor.contains_continue; +} loop_state::loop_state() { @@ -212,6 +243,12 @@ loop_analysis::visit_leave(ir_loop *ir) if (((ir_instruction *) node)->as_variable()) continue; + /* If we find a continue, we cannot go ahead, because + * the following instructions may never get executed + */ + if(contains_continue((ir_instruction*) node)) + break; + ir_if *if_stmt = ((ir_instruction *) node)->as_if(); if ((if_stmt != NULL) && is_loop_terminator(if_stmt)) @@ -464,6 +501,9 @@ get_basic_induction_increment(ir_assignment *ir, hash_table *var_hash) * Detects if-statements of the form * * (if (expression bool ...) (break)) + * + * NOTE: if we ever extend it to allow other instructions before the break, we + * need to return false if contains_continue() apply to any of those instructions. */ bool is_loop_terminator(ir_if *ir) |