summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Barbieri <luca@luca-barbieri.com>2010-09-14 04:15:25 +0200
committerLuca Barbieri <luca@luca-barbieri.com>2010-09-14 15:50:47 +0200
commit11f5a8dbe753dacfa371002d272ea842998a2b16 (patch)
tree44ea1518566ebda03dbbde4ad03248e5d8e0a7df
parent759b5db9168f7c0ecc55d259b518f8c32be946fb (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.cpp40
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)