Mesa (shader-work): glsl/loop_unroll: unroll loops with cond breaks anywhere, not just the end

Luca Barbieri lb at kemper.freedesktop.org
Tue Sep 14 08:36:13 PDT 2010


Module: Mesa
Branch: shader-work
Commit: fa93dadda31decb7869fc856f58b5eaade284923
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=fa93dadda31decb7869fc856f58b5eaade284923

Author: Luca Barbieri <luca at luca-barbieri.com>
Date:   Tue Sep 14 04:56:34 2010 +0200

glsl/loop_unroll: unroll loops with cond breaks anywhere, not just the end

Currently we only unroll loops with conditional breaks at the end, which is
the form that ir_lower_jumps generates.

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

---

 src/glsl/loop_unroll.cpp |  130 +++++++++++++++++++++++++++-------------------
 1 files changed, 76 insertions(+), 54 deletions(-)

diff --git a/src/glsl/loop_unroll.cpp b/src/glsl/loop_unroll.cpp
index 90797bd..6308907 100644
--- a/src/glsl/loop_unroll.cpp
+++ b/src/glsl/loop_unroll.cpp
@@ -73,44 +73,77 @@ 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());
-
-      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 (last && last->ir_type == ir_type_loop_jump
-	     && ((ir_loop_jump*) last)->is_break()) {
-	    continue_from_then_branch = false;
-	 } else {
-	    last = (ir_instruction *) last_if->then_instructions.get_tail();
-
-	    if (last && last->ir_type == ir_type_loop_jump
-		&& ((ir_loop_jump*) last)->is_break())
-	       continue_from_then_branch = true;
-	    else
-	       /* Bail out if neither if-statement branch ends with a break.
-		*/
-	       return visit_continue;
-	 }
-
-	 /* Remove the break from the if-statement.
-	  */
-	 last->remove();
+      ir_instruction *last_ir = ((ir_instruction*)ir->body_instructions.get_tail());
+
+      if (last_ir->ir_type == ir_type_loop_jump
+            && ((ir_loop_jump*)last_ir)->is_break()) {
+         /* 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 = 0;
+         ir_instruction *break_ir = 0;
+         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;
+
+            assert(last_ir != NULL);
+
+            ir_if = cur_ir->as_if();
+            if (ir_if) {
+               /* 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 *ir_if_last =
+                  (ir_instruction *) ir_if->then_instructions.get_tail();
+
+               if (ir_if_last && ir_if_last->ir_type == ir_type_loop_jump
+                   && ((ir_loop_jump*) ir_if_last)->is_break()) {
+                  continue_from_then_branch = false;
+                  break_ir = ir_if_last;
+                  break;
+               } else {
+                  ir_if_last = (ir_instruction *) ir_if->then_instructions.get_tail();
+
+                  if (ir_if_last && ir_if_last->ir_type == ir_type_loop_jump
+                      && ((ir_loop_jump*) ir_if_last)->is_break())
+                  {
+                     break_ir = ir_if_last;
+                     continue_from_then_branch = true;
+                     break;
+                  }
+               }
+            }
+         }
+
+         if(!break_ir)
+            return visit_continue;
+
+         /* 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;
@@ -121,18 +154,18 @@ 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);
 
             ir_to_replace->insert_before(&copy_list);
             ir_to_replace->remove();
 
             /* placeholder that will be removed in the next iteration */
             ir_to_replace =
-	       new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue);
+               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);
          }
@@ -141,18 +174,7 @@ loop_unroll_visitor::visit_leave(ir_loop *ir)
 
          this->progress = true;
          return visit_continue;
-      } else if (last_ir->ir_type == ir_type_loop_jump
-		 && ((ir_loop_jump *)last_ir)->is_break()) {
-	 /* 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);



More information about the mesa-commit mailing list