nir: add opt_if_loop_terminator()
authorTimothy Arceri <tarceri@itsqueeze.com>
Fri, 1 Jun 2018 05:37:28 +0000 (15:37 +1000)
committerTimothy Arceri <tarceri@itsqueeze.com>
Thu, 7 Jun 2018 01:33:04 +0000 (11:33 +1000)
This pass detects potential loop terminators and moves intructions
from the non breaking branch after the if-statement.

This enables both the new opt_if_simplification() pass and loop
unrolling to potentially progress further.

Unexpectedly this change speed up shader-db run times by ~3%

Ivy Bridge shader-db results (all changes in dolphin/ubershaders):

total instructions in shared programs: 9995662 -> 9995338 (-0.00%)
instructions in affected programs: 87845 -> 87521 (-0.37%)
helped: 27
HURT: 0

total cycles in shared programs: 230931495 -> 230925015 (-0.00%)
cycles in affected programs: 56391385 -> 56384905 (-0.01%)
helped: 27
HURT: 0

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
src/compiler/nir/nir_opt_if.c

index b03657a42446593c6ed6dadaee250eceb9c0a388..863ca630fbd0c6284adc6c4bfd75730699c488ac 100644 (file)
@@ -24,6 +24,7 @@
 #include "nir.h"
 #include "nir/nir_builder.h"
 #include "nir_control_flow.h"
+#include "nir_loop_analyze.h"
 
 /**
  * This optimization detects if statements at the tops of loops where the
@@ -283,6 +284,72 @@ opt_if_simplification(nir_builder *b, nir_if *nif)
    return true;
 }
 
+/**
+ * This optimization simplifies potential loop terminators which then allows
+ * other passes such as opt_if_simplification() and loop unrolling to progress
+ * further:
+ *
+ *     if (cond) {
+ *        ... then block instructions ...
+ *     } else {
+ *         ...
+ *        break;
+ *     }
+ *
+ * into:
+ *
+ *     if (cond) {
+ *     } else {
+ *         ...
+ *        break;
+ *     }
+ *     ... then block instructions ...
+ */
+static bool
+opt_if_loop_terminator(nir_if *nif)
+{
+   nir_block *break_blk = NULL;
+   nir_block *continue_from_blk = NULL;
+   bool continue_from_then = true;
+
+   nir_block *last_then = nir_if_last_then_block(nif);
+   nir_block *last_else = nir_if_last_else_block(nif);
+
+   if (nir_block_ends_in_break(last_then)) {
+      break_blk = last_then;
+      continue_from_blk = last_else;
+      continue_from_then = false;
+   } else if (nir_block_ends_in_break(last_else)) {
+      break_blk = last_else;
+      continue_from_blk = last_then;
+   }
+
+   /* Continue if the if-statement contained no jumps at all */
+   if (!break_blk)
+      return false;
+
+   /* If the continue from block is empty then return as there is nothing to
+    * move.
+    */
+   nir_block *first_continue_from_blk = continue_from_then ?
+      nir_if_first_then_block(nif) :
+      nir_if_first_else_block(nif);
+   if (is_block_empty(first_continue_from_blk))
+      return false;
+
+   if (!nir_is_trivial_loop_if(nif, break_blk))
+      return false;
+
+   /* Finally, move the continue from branch after the if-statement. */
+   nir_cf_list tmp;
+   nir_cf_extract(&tmp, nir_before_block(first_continue_from_blk),
+                        nir_after_block(continue_from_blk));
+   nir_cf_reinsert(&tmp, nir_after_cf_node(&nif->cf_node));
+   nir_cf_delete(&tmp);
+
+   return true;
+}
+
 static bool
 opt_if_cf_list(nir_builder *b, struct exec_list *cf_list)
 {
@@ -296,6 +363,7 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list)
          nir_if *nif = nir_cf_node_as_if(cf_node);
          progress |= opt_if_cf_list(b, &nif->then_list);
          progress |= opt_if_cf_list(b, &nif->else_list);
+         progress |= opt_if_loop_terminator(nif);
          progress |= opt_if_simplification(b, nif);
          break;
       }