glsl: Unroll loops with conditional breaks anywhere (not just the end)
authorLuca Barbieri <luca@luca-barbieri.com>
Wed, 1 Dec 2010 23:12:07 +0000 (15:12 -0800)
committerIan Romanick <ian.d.romanick@intel.com>
Fri, 10 Dec 2010 00:42:05 +0000 (16:42 -0800)
Currently we only unroll loops with conditional breaks at the end, which is
the form that lower_jumps generates.

However, if breaks are not lowered, they tend to appear at the beginning, so
add support for a conditional break anywhere.

Signed-off-by: Luca Barbieri <luca@luca-barbieri.com>
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/loop_unroll.cpp

index c5001ba953040629026f5fe762b9e4b035480da8..46000524ba105fdc56358d32261b44dc952711d5 100644 (file)
@@ -81,42 +81,74 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
    if (ls->num_loop_jumps > 1)
       return visit_continue;
    else if (ls->num_loop_jumps) {
-      /* recognize loops in the form produced by ir_lower_jumps */
-      ir_instruction *last_ir =
-        ((ir_instruction*)ir->body_instructions.get_tail());
-
+      ir_instruction *last_ir = (ir_instruction *) ir->body_instructions.get_tail();
       assert(last_ir != NULL);
 
-      ir_if *last_if = last_ir->as_if();
-      if (last_if) {
-        bool continue_from_then_branch;
-
-        /* Determine which if-statement branch, if any, ends with a break.
-         * The branch that did *not* have the break will get a temporary
-         * continue inserted in each iteration of the loop unroll.
-         *
-         * Note that since ls->num_loop_jumps is <= 1, it is impossible for
-         * both branches to end with a break.
-         */
-        ir_instruction *last =
-           (ir_instruction *) last_if->then_instructions.get_tail();
-
-        if (is_break(last)) {
-           continue_from_then_branch = false;
-        } else {
-           last = (ir_instruction *) last_if->else_instructions.get_tail();
-
-           if (is_break(last))
-              continue_from_then_branch = true;
-           else
-              /* Bail out if neither if-statement branch ends with a break.
+      if (is_break(last_ir)) {
+         /* If the only loop-jump is a break at the end of the loop, the loop
+          * will execute exactly once.  Remove the break, set the iteration
+          * count, and fall through to the normal unroller.
+          */
+         last_ir->remove();
+         iterations = 1;
+
+         this->progress = true;
+      } else {
+         ir_if *ir_if = NULL;
+         ir_instruction *break_ir = NULL;
+         bool continue_from_then_branch = false;
+
+         foreach_list(node, &ir->body_instructions) {
+            /* recognize loops in the form produced by ir_lower_jumps */
+            ir_instruction *cur_ir = (ir_instruction *) node;
+
+            ir_if = cur_ir->as_if();
+            if (ir_if != NULL) {
+              /* Determine which if-statement branch, if any, ends with a
+               * break.  The branch that did *not* have the break will get a
+               * temporary continue inserted in each iteration of the loop
+               * unroll.
+               *
+               * Note that since ls->num_loop_jumps is <= 1, it is impossible
+               * for both branches to end with a break.
                */
-              return visit_continue;
-        }
+               ir_instruction *ir_if_last =
+                  (ir_instruction *) ir_if->then_instructions.get_tail();
+
+               if (is_break(ir_if_last)) {
+                  continue_from_then_branch = false;
+                  break_ir = ir_if_last;
+                  break;
+               } else {
+                  ir_if_last =
+                    (ir_instruction *) ir_if->else_instructions.get_tail();
+
+                  if (is_break(ir_if_last)) {
+                     break_ir = ir_if_last;
+                     continue_from_then_branch = true;
+                     break;
+                  }
+               }
+            }
+         }
+
+         if (break_ir == NULL)
+            return visit_continue;
 
-        /* Remove the break from the if-statement.
-         */
-        last->remove();
+         /* move instructions after then if in the continue branch */
+         while (!ir_if->get_next()->is_tail_sentinel()) {
+            ir_instruction *move_ir = (ir_instruction *) ir_if->get_next();
+
+            move_ir->remove();
+            if (continue_from_then_branch)
+               ir_if->then_instructions.push_tail(move_ir);
+            else
+               ir_if->else_instructions.push_tail(move_ir);
+         }
+
+         /* Remove the break from the if-statement.
+          */
+         break_ir->remove();
 
          void *const mem_ctx = talloc_parent(ir);
          ir_instruction *ir_to_replace = ir;
@@ -127,8 +159,8 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
             copy_list.make_empty();
             clone_ir_list(mem_ctx, &copy_list, &ir->body_instructions);
 
-            last_if = ((ir_instruction*)copy_list.get_tail())->as_if();
-            assert(last_if);
+            ir_if = ((ir_instruction *) copy_list.get_tail())->as_if();
+            assert(ir_if != NULL);
 
             ir_to_replace->insert_before(&copy_list);
             ir_to_replace->remove();
@@ -138,7 +170,7 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
               new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue);
 
             exec_list *const list = (continue_from_then_branch)
-              ? &last_if->then_instructions : &last_if->else_instructions;
+               ? &ir_if->then_instructions : &ir_if->else_instructions;
 
             list->push_tail(ir_to_replace);
          }
@@ -147,17 +179,7 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
 
          this->progress = true;
          return visit_continue;
-      } else if (is_break(last_ir)) {
-        /* If the only loop-jump is a break at the end of the loop, the loop
-         * will execute exactly once.  Remove the break, set the iteration
-         * count, and fall through to the normal unroller.
-         */
-         last_ir->remove();
-        iterations = 1;
-
-        this->progress = true;
-      } else
-         return visit_continue;
+      }
    }
 
    void *const mem_ctx = talloc_parent(ir);