<div dir="ltr">On 28 October 2013 11:31, Matt Turner <span dir="ltr"><<a href="mailto:mattst88@gmail.com" target="_blank">mattst88@gmail.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

The pass previously searched only backward from an ENDIF (and ELSE) to<br>
find MOVs with the same destination. This commit extends the pass to<br>
also search forward from the IF (and ELSE) to find matching MOVs which<br>
it can replace with SEL instructions before the IF.<br>
<br>
E.g., the pass can now optimize<br>
<br>
   (+f0) IF<br>
   MOV dst0 ...<br>
   MOV dst1 ...<br>
   MOV dst2 ...<br>
   ELSE<br>
   MOV dst0 ...<br>
   MOV dst1 ...<br>
   MOV dst3 ...<br>
   ENDIF<br>
<br>
total instructions in shared programs: 1287457 -> 1287432 (-0.00%)<br>
instructions in affected programs:     15664 -> 15639 (-0.16%)<br>
---<br>
 src/mesa/drivers/dri/i965/brw_fs_sel_peephole.cpp | 131 +++++++++++++++++++++-<br>
 1 file changed, 129 insertions(+), 2 deletions(-)<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/brw_fs_sel_peephole.cpp b/src/mesa/drivers/dri/i965/brw_fs_sel_peephole.cpp<br>
index 4c0eba9..ebde78b 100644<br>
--- a/src/mesa/drivers/dri/i965/brw_fs_sel_peephole.cpp<br>
+++ b/src/mesa/drivers/dri/i965/brw_fs_sel_peephole.cpp<br>
@@ -86,6 +86,58 @@ find_if_else_from_endif(const cfg_t *cfg, int start_block,<br>
 }<br>
<br>
 /**<br>
+ * For a given control flow graph <cfg> and number <start_block> of the basic<br>
+ * block ending with an IF instruction, return pointers to the associated<br>
+ * ELSE and ENDIF instructions.<br>
+ *<br>
+ * If no ELSE instruction is found before the associated ENDIF, return false.<br>
+ * Otherwise return true.<br>
+ */<br>
+static bool<br>
+find_else_endif_from_if(const cfg_t *cfg, int start_block,<br>
+                        fs_inst **else_inst, fs_inst **endif_inst)<br></blockquote><div><br></div><div>I have similar concerns about this function to the concerns I had about find_if_else_from_endif() in patch 10.5.<br>

</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+{<br>
+   assert(start_block < cfg->num_blocks - 1);<br>
+   assert(else_inst);<br>
+   assert(endif_inst);<br>
+<br>
+   *else_inst = NULL;<br>
+   *endif_inst = NULL;<br>
+<br>
+   int depth = 0;<br>
+   for (int b = start_block + 1; b < cfg->num_blocks; b++) {<br>
+      bblock_t *block = cfg->blocks[b];<br>
+<br>
+      switch (block->end->opcode) {<br>
+      case BRW_OPCODE_ENDIF:<br>
+         if (depth == 0) {<br>
+            *endif_inst = (fs_inst *) block->end;<br>
+            return *else_inst != NULL;<br>
+         }<br>
+         depth--;<br>
+         break;<br>
+      case BRW_OPCODE_ELSE:<br>
+         if (depth == 0) {<br>
+            *else_inst = (fs_inst *) block->end;<br>
+         }<br>
+         /* No change in depth */<br>
+         break;<br>
+      case BRW_OPCODE_IF:<br>
+      case BRW_OPCODE_DO:<br>
+         depth++;<br>
+         break;<br>
+      case BRW_OPCODE_WHILE:<br>
+         depth--;<br>
+         break;<br>
+      default:<br>
+         break;<br>
+      }<br>
+   }<br>
+<br>
+   return false;<br>
+}<br>
+<br>
+/**<br>
  * Scans backwards from an ENDIF counting MOV instructions with common<br>
  * destinations inside the "then" and "else" blocks of the if statement.<br>
  *<br>
@@ -136,6 +188,56 @@ match_movs_from_endif(fs_inst *then_mov[MAX_MOVS], fs_inst *else_mov[MAX_MOVS],<br>
 }<br>
<br>
 /**<br>
+ * Scans forward from an IF counting MOV instructions with common destinations<br>
+ * inside the "then" and "else" blocks of the if statement.<br>
+ *<br>
+ * A pointer to the fs_inst* for IF is passed as the <if_inst> argument. The<br>
+ * function stores pointers to the MOV instructions in the <then_mov> and<br>
+ * <else_mov> arrays.<br>
+ *<br>
+ * \return the number of MOVs to a common destination found in the two branches<br>
+ *<br>
+ * E.g.:<br>
+ *                  IF ...<br>
+ *    then_mov[0] = MOV g4, ...<br>
+ *    then_mov[1] = MOV g5, ...<br>
+ *    then_mov[2] = MOV g6, ...<br>
+ *                  ELSE ...<br>
+ *    else_mov[0] = MOV g4, ...<br>
+ *    else_mov[1] = MOV g5, ...<br>
+ *    else_mov[2] = MOV g7, ...<br>
+ *                  ENDIF<br>
+ *    returns 2 (since only the first two MOVs have a common destination)<br>
+ */<br>
+static int<br>
+match_movs_from_if(fs_inst *then_mov[MAX_MOVS], fs_inst *else_mov[MAX_MOVS],<br>
+                   fs_inst *if_inst, fs_inst *else_inst)<br>
+{<br>
+   fs_inst *m = if_inst;<br>
+<br>
+   assert(m->opcode == BRW_OPCODE_IF);<br>
+   m = (fs_inst *) m->next;<br>
+<br>
+   int then_movs = 0;<br>
+   while (then_movs < MAX_MOVS && m->opcode == BRW_OPCODE_MOV) {<br>
+      then_mov[then_movs] = m;<br>
+      m = (fs_inst *) m->next;<br>
+      then_movs++;<br>
+   }<br>
+<br>
+   m = (fs_inst *) else_inst->next;<br>
+<br>
+   int else_movs = 0;<br>
+   while (else_movs < MAX_MOVS && m->opcode == BRW_OPCODE_MOV) {<br>
+      else_mov[else_movs] = m;<br>
+      m = (fs_inst *) m->next;<br>
+      else_movs++;<br>
+   }<br>
+<br>
+   return MIN2(then_movs, else_movs);<br>
+}<br>
+<br>
+/**<br>
  * Try to replace IF/MOV+/ELSE/MOV+/ENDIF with SEL.<br>
  *<br>
  * Many GLSL shaders contain the following pattern:<br>
@@ -202,6 +304,18 @@ fs_visitor::opt_peephole_sel()<br>
<br>
          if (movs == 0)<br>
             continue;<br>
+      } else if (start->opcode == BRW_OPCODE_IF) {<br>
+         if_inst = start;<br>
+<br>
+         /* Find the associated ELSE and ENDIF instructions for our IF. */<br>
+         if (!find_else_endif_from_if(&cfg, b, &else_inst, &endif_inst))<br>
+            continue;<br>
+<br>
+         /* Find MOVs to a common destination. */<br>
+         movs = match_movs_from_if(then_mov, else_mov, start, else_inst);<br>
+<br>
+         if (movs == 0)<br>
+            continue;<br></blockquote><div><br></div><div>It seems like we're doing some redundant work between this newly added code and the block above it.<br><br>The block above it says "for each 'if', find the matching else and endif.  Then try to peephole SEL by walking forward through the then/else instructions."<br>

</div><div>The block below it says "for each 'endif', find the matching if and else.  Then try to peephole SEL by walking backward through the then/else instructions."<br><br></div><div>It seems like we could just as easily do:  "For each 'endif', find the matching if and else.  Then try to peephole SEL by walking forward through the then/else instructions.  Then try to peephole SEL by walking backward through the then/else instructions".<br>

<br>An advantage of this approach is that there's no ambiguity about whether we walk forward first or walk backward first.  And I think walking forward first is better, because it avoids the problem we talked about in patch 9 of needing to save and restore the flag register.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

       } else {<br>
          continue;<br>
       }<br>
@@ -269,8 +383,8 @@ fs_visitor::opt_peephole_sel()<br>
             cmp_inst = new(mem_ctx) fs_inst(m);<br>
          }<br>
<br>
-         /* Insert flag-writing instruction immediately after the ENDIF, and<br>
-          * SEL and MOV imm instructions after that.<br>
+         /* Insert flag-writing instruction immediately after the IF or ENDIF,<br>
+          * and SEL and MOV imm instructions after that.<br>
           */<br>
          if (start->opcode == BRW_OPCODE_ENDIF) {<br>
             endif_inst->insert_after(cmp_inst);<br>
@@ -286,6 +400,19 @@ fs_visitor::opt_peephole_sel()<br>
<br>
             /* Appending an instruction may have changed our bblock end. */<br>
             block->end = sel_inst[0];<br>
+         } else if (start->opcode == BRW_OPCODE_IF) {<br>
+            if_inst->insert_before(cmp_inst);<br></blockquote><div><br></div><div>With the exception of the gen6 if-with-cmod case, this line can be dropped, and then we don't have the problem with the cmp instruction getting moved to a place where it might have a different meaning. <br>

</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+            for (int i = 0; i < movs; i++) {<br>
+               cmp_inst->insert_after(sel_inst[i]);<br>
+               if (mov_imm_inst[i])<br>
+                  cmp_inst->insert_after(mov_imm_inst[i]);<br>
+<br>
+               then_mov[i]->remove();<br>
+               else_mov[i]->remove();<br>
+            }<br>
+<br>
+            /* No change to basic block boundaries possible. */<br>
          }<br>
       }<br>
       progress = progress || bb_progress;<br>
<span><font color="#888888">--<br>
1.8.3.2<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org" target="_blank">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div><br></div></div>