Module: Mesa
Branch: main
Commit: bf43af984af6ab7ccf13fea976d11cb6f21af020
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=bf43af984af6ab7ccf13fea976d11cb6f21af020

Author: Daniel Schürmann <dan...@schuermann.dev>
Date:   Fri Aug 25 14:32:53 2023 +0200

nir/opt_loop_cf: generalize removal of "trivial" continues

So that is also handles break statements and works in
arbitrarily nested control flow.

Totals from 905 (1.18% of 76636) affected shaders: (RADV, GFX11)
Instrs: 605164 -> 605548 (+0.06%); split: -0.01%, +0.08%
CodeSize: 3162036 -> 3163472 (+0.05%); split: -0.01%, +0.06%
Latency: 2045559 -> 1387622 (-32.16%)
InvThroughput: 352344 -> 231676 (-34.25%)
SClause: 16092 -> 16088 (-0.02%); split: -0.04%, +0.02%
Copies: 41286 -> 41297 (+0.03%); split: -0.02%, +0.05%
Branches: 19949 -> 19929 (-0.10%)
PreSGPRs: 33413 -> 33385 (-0.08%)
PreVGPRs: 19177 -> 19135 (-0.22%)

Reviewed-by: Konstantin Seurer <konstantin.seu...@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24940>

---

 src/compiler/nir/nir_opt_loop.c | 63 +++++++++++++++++++++++++----------------
 1 file changed, 39 insertions(+), 24 deletions(-)

diff --git a/src/compiler/nir/nir_opt_loop.c b/src/compiler/nir/nir_opt_loop.c
index 34a60fc3446..0af0993718d 100644
--- a/src/compiler/nir/nir_opt_loop.c
+++ b/src/compiler/nir/nir_opt_loop.c
@@ -13,6 +13,14 @@ is_block_empty(nir_block *block)
           exec_list_is_empty(&block->instr_list);
 }
 
+static bool
+is_block_singular(nir_block *block)
+{
+   return nir_cf_node_is_last(&block->cf_node) &&
+          (exec_list_is_empty(&block->instr_list) ||
+           (exec_list_is_singular(&block->instr_list) && 
nir_block_ends_in_jump(block)));
+}
+
 static bool
 nir_block_ends_in_continue(nir_block *block)
 {
@@ -196,34 +204,43 @@ opt_loop_terminator(nir_if *nif)
  *
  */
 static bool
-opt_loop_last_block(nir_block *block, bool is_loop_last_block)
+opt_loop_last_block(nir_block *block, bool is_trivial_continue, bool 
is_trivial_break)
 {
    /* If this block has no predecessors, let nir_opt_dead_cf() do the cleanup 
*/
    if (block->predecessors->entries == 0)
       return false;
 
-   const bool has_break = nir_block_ends_in_break(block);
-   const bool has_continue = nir_block_ends_in_continue(block) || 
is_loop_last_block;
-
-   /* This is already handled by opt_loop_last_block(is_loop_last_block=false) 
*/
-   if (is_loop_last_block && has_break)
-      return false;
-
-   if (!has_continue && !has_break)
-      return false;
-
    bool progress = false;
+   bool has_break = nir_block_ends_in_break(block);
+   bool has_continue = nir_block_ends_in_continue(block);
 
-   /* First remove any "trivial" continue, i.e. those that are at the tail
-    * of a loop where we can just delete the continue instruction and
-    * control-flow will naturally take us back to the top of the loop.
+   /* Remove any "trivial" break and continue, i.e. those that are at the tail
+    * of a CF-list where we can just delete the instruction and
+    * control-flow will naturally take us to the same target block.
     */
-   if (is_loop_last_block && nir_block_ends_in_continue(block)) {
+   if ((has_break && is_trivial_break) || (has_continue && 
is_trivial_continue)) {
       nir_lower_phis_to_regs_block(block->successors[0]);
       nir_instr_remove_v(nir_block_last_instr(block));
-      progress = true;
+      return true;
    }
 
+   if (!nir_block_ends_in_jump(block)) {
+      has_break = is_trivial_break;
+      has_continue = is_trivial_continue;
+   } else if (is_trivial_continue || is_trivial_break) {
+      /* This block ends in a jump that cannot be removed because the implicit
+       * fallthrough leads to a different target block.
+       *
+       * We already optimized this block's jump with the predecessors' when 
visiting
+       * this block with opt_loop_last_block(block, is_trivial_* = false, 
false).
+       */
+      return false;
+   }
+
+   /* Nothing to do. */
+   if (!has_continue && !has_break)
+      return false;
+
    /* Walk backwards and check for previous IF statements whether one of the
     * branch legs ends with an equal jump instruction as this block.
     */
@@ -257,10 +274,8 @@ opt_loop_last_block(nir_block *block, bool 
is_loop_last_block)
 
       if (merge_into_then) {
          nir_cf_reinsert(&tmp, nir_after_block(then_block));
-         nir_instr_remove_v(nir_block_last_instr(else_block));
       } else {
          nir_cf_reinsert(&tmp, nir_after_block(else_block));
-         nir_instr_remove_v(nir_block_last_instr(then_block));
       }
 
       /* Because we split the current block, the pointer is not valid anymore. 
*/
@@ -268,13 +283,13 @@ opt_loop_last_block(nir_block *block, bool 
is_loop_last_block)
       progress = true;
    }
 
-   /* Revisit these blocks with is_loop_last_block=true to optimize with the 
implicit continue. */
-   if (is_loop_last_block && is_block_empty(block)) {
+   /* Revisit the predecessor blocks in order to remove implicit jump 
instructions. */
+   if (is_block_singular(block)) {
       nir_cf_node *prev = nir_cf_node_prev(&block->cf_node);
       if (prev && prev->type == nir_cf_node_if) {
          nir_if *nif = nir_cf_node_as_if(prev);
-         progress |= opt_loop_last_block(nir_if_last_then_block(nif), true);
-         progress |= opt_loop_last_block(nir_if_last_else_block(nif), true);
+         progress |= opt_loop_last_block(nir_if_last_then_block(nif), 
has_continue, has_break);
+         progress |= opt_loop_last_block(nir_if_last_else_block(nif), 
has_continue, has_break);
       }
    }
 
@@ -289,7 +304,7 @@ opt_loop_cf_list(struct exec_list *cf_list)
       switch (cf_node->type) {
       case nir_cf_node_block: {
          nir_block *block = nir_cf_node_as_block(cf_node);
-         progress |= opt_loop_last_block(block, false);
+         progress |= opt_loop_last_block(block, false, false);
          break;
       }
 
@@ -306,7 +321,7 @@ opt_loop_cf_list(struct exec_list *cf_list)
          nir_loop *loop = nir_cf_node_as_loop(cf_node);
          assert(!nir_loop_has_continue_construct(loop));
          progress |= opt_loop_cf_list(&loop->body);
-         progress |= opt_loop_last_block(nir_loop_last_block(loop), true);
+         progress |= opt_loop_last_block(nir_loop_last_block(loop), true, 
false);
          break;
       }
 

Reply via email to