[Mesa-dev] [PATCH] nir: update opt_dead_cf to merge some consecutive ifs

Timothy Arceri timothy.arceri at collabora.com
Thu Dec 8 02:25:00 UTC 2016


This handles two cases. The first is where the second ifs
condition is a phi with constant srcs origination from the first if.
The second is where both ifs have the same condtion.

A limitation to merging is that currently this code will only merge
the ifs if there is only a single phi instruction in the block
between them. It may make sense to expand this in the future but
more research would be needed to see if its worth while.

All of the changes in shader-db are from the second use case. The
first use case is used to stop regression in the following patch
when switching to the NIR return lowering pass.

total instructions in shared programs: 8673389 -> 8673075 (-0.00%)
instructions in affected programs: 30364 -> 30050 (-1.03%)
helped: 90
HURT: 0

total cycles in shared programs: 73195178 -> 73258862 (0.09%)
cycles in affected programs: 642244 -> 705928 (9.92%)
helped: 6
HURT: 84
---
 src/compiler/nir/nir_opt_dead_cf.c | 146 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 142 insertions(+), 4 deletions(-)

diff --git a/src/compiler/nir/nir_opt_dead_cf.c b/src/compiler/nir/nir_opt_dead_cf.c
index 53c92ec..bc4d5b6 100644
--- a/src/compiler/nir/nir_opt_dead_cf.c
+++ b/src/compiler/nir/nir_opt_dead_cf.c
@@ -215,6 +215,48 @@ loop_is_dead(nir_loop *loop)
    return true;
 }
 
+static void
+opt_merge_if(nir_if *dest_if, nir_if *src_if, bool dest_if_then,
+             bool src_if_then, nir_phi_instr *phi, nir_phi_src *phi_src)
+{
+   /* First rewrite the src for any instructions in the second if that use
+    * the phi from the first if as a src.
+    */
+   nir_block *first_if_blk = src_if_then ? nir_if_first_then_block(src_if)
+                                         : nir_if_first_else_block(src_if);
+
+   nir_block *last_if_blk = src_if_then ? nir_if_last_then_block(src_if)
+                                        : nir_if_last_else_block(src_if);
+
+   nir_foreach_use_safe(use_src, &phi->dest.ssa) {
+      unsigned use_blk_idx = use_src->parent_instr->block->index;
+      if (first_if_blk->index <= use_blk_idx &&
+          last_if_blk->index >= use_blk_idx)
+         nir_instr_rewrite_src(use_src->parent_instr, use_src,
+                               nir_src_for_ssa(phi_src->src.ssa));
+   }
+
+   nir_foreach_if_use_safe(use_src, &phi->dest.ssa) {
+      unsigned use_blk_idx = use_src->parent_instr->block->index;
+      if (first_if_blk->index <= use_blk_idx &&
+          last_if_blk->index >= use_blk_idx)
+         nir_instr_rewrite_src(use_src->parent_instr, use_src,
+                               nir_src_for_ssa(phi_src->src.ssa));
+   }
+
+   /* Now merge the if branch */
+   nir_block *dest_blk = dest_if_then ? nir_if_last_then_block(dest_if)
+                                      : nir_if_last_else_block(dest_if);
+
+   struct exec_list *list = src_if_then ? &src_if->then_list
+                                        : &src_if->else_list;
+
+   nir_cf_list if_cf_list;
+   nir_cf_extract(&if_cf_list, nir_before_cf_list(list),
+                  nir_after_cf_list(list));
+   nir_cf_reinsert(&if_cf_list, nir_after_block(dest_blk));
+}
+
 static bool
 dead_cf_block(nir_block *block)
 {
@@ -223,11 +265,105 @@ dead_cf_block(nir_block *block)
       nir_const_value *const_value =
          nir_src_as_const_value(following_if->condition);
 
-      if (!const_value)
-         return false;
+      if (const_value) {
+         opt_constant_if(following_if, const_value->u32[0] != 0);
+         return true;
+      }
 
-      opt_constant_if(following_if, const_value->u32[0] != 0);
-      return true;
+      nir_block *next_blk = nir_cf_node_cf_tree_next(&following_if->cf_node);
+      if (next_blk && following_if->condition.is_ssa) {
+
+         nir_if *next_if = nir_block_get_following_if(next_blk);
+         if (next_if && next_if->condition.is_ssa &&
+             exec_list_length(&next_blk->instr_list) == 1) {
+
+            nir_instr *blk_instr = nir_block_last_instr(next_blk);
+            if (blk_instr->type == nir_instr_type_phi) {
+               nir_phi_instr *phi = nir_instr_as_phi(blk_instr);
+
+               if (blk_instr == next_if->condition.ssa->parent_instr) {
+                  /* Here we merge two consecutive ifs where the second ifs
+                   * condition is a phi with constant srcs originating from
+                   * the first if e.g:
+                   *
+                   *   if ssa_1 {
+                   *      block block_3:
+                   *      vec1 32 ssa_2 = load_const (0x00000000)
+                   *      ...
+                   *   } else {
+                   *      block block_4:
+                   *      vec1 32 ssa_3 = load_const (0xffffffff)
+                   *      ...
+                   *   }
+                   *   vec1 32 ssa_4 = phi block_3: ssa_2, block_4: ssa_3
+                   *   if ssa_4 {
+                   *      ...
+                   *   } else {
+                   *      ...
+                   *   }
+                   */
+
+                  unsigned i = 0;
+                  nir_const_value *cond[2];
+                  nir_phi_src *phi_src[2];
+                  nir_foreach_phi_src(src, phi) {
+                     phi_src[i] = src;
+                     cond[i] = nir_src_as_const_value(src->src);
+                     i++;
+                  }
+
+                  if (cond[0] && cond[1]) {
+                     const bool const1_eq_0 = cond[0]->u32[0] == 0;
+                     const bool const2_eq_0 = cond[1]->u32[0] == 0;
+                     if ((const1_eq_0 && !const2_eq_0) ||
+                         (!const1_eq_0 && const2_eq_0)) {
+                         bool dest_if_then_blk =
+                            phi_src[0]->pred->index < phi_src[1]->pred->index;
+                         opt_merge_if(following_if, next_if, dest_if_then_blk,
+                                      const2_eq_0, phi, phi_src[0]);
+                         opt_merge_if(following_if, next_if, !dest_if_then_blk,
+                                      const1_eq_0, phi, phi_src[1]);
+                     }
+                  }
+               } else if (following_if->condition.ssa ==
+                          next_if->condition.ssa) {
+                  /* Here we merge two consecutive ifs that have the same
+                   * condition e.g:
+                   *
+                   *   if ssa_12 {
+                   *      ...
+                   *   } else {
+                   *      ...
+                   *   }
+                   *   vec1 32 ssa_22 = phi block_3: ssa_19, block_4: ssa_20
+                   *   if ssa_12 {
+                   *      ...
+                   *   } else {
+                   *      ...
+                   *   }
+                   */
+
+                  unsigned i = 0;
+                  nir_phi_src *phi_src[2];
+                  nir_foreach_phi_src(src, phi) {
+                     phi_src[i] = src;
+                     i++;
+                  }
+
+                  bool if_then = false;
+                  if (phi_src[0]->pred->index < phi_src[1]->pred->index)
+                     if_then = true;
+
+                  opt_merge_if(following_if, next_if, if_then, if_then, phi,
+                               phi_src[0]);
+                  opt_merge_if(following_if, next_if, !if_then, !if_then, phi,
+                               phi_src[1]);
+               }
+            }
+         }
+      }
+
+      return false;
    }
 
    nir_loop *following_loop = nir_block_get_following_loop(block);
@@ -334,6 +470,8 @@ dead_cf_list(struct exec_list *list, bool *list_ends_in_jump)
 static bool
 opt_dead_cf_impl(nir_function_impl *impl)
 {
+   nir_metadata_require(impl, nir_metadata_block_index);
+
    bool dummy;
    bool progress = dead_cf_list(&impl->body, &dummy);
 
-- 
2.7.4



More information about the mesa-dev mailing list