[Mesa-dev] [PATCH v4 7/7] nir: add loop unroll support for complex wrapper loops

Jason Ekstrand jason at jlekstrand.net
Tue Aug 28 17:59:55 UTC 2018


On Mon, Aug 27, 2018 at 4:10 AM Timothy Arceri <tarceri at itsqueeze.com>
wrote:

> In GLSL IR we cheat with switch statements and simply convert them
> into loops with a single iteration. This allowed us to make use of
> the existing jump instruction handling provided by the loop handing
> code, it also allows dead code to be cleaned up once we have
> wrapped the code in a loop.
>
> However using loops in this way created previously unrollable loops
> which limits further optimisations. Here we provide a way to unroll
> loops that end in a break and have multiple other exits.
>
> All shader-db changes are from the dolphin uber shaders. There is a
> small amount of HURT shaders but in general the improvements far
> exceed the HURT.
>
> shader-db results IVB:
>
> total instructions in shared programs: 10018187 -> 10016468 (-0.02%)
> instructions in affected programs: 104080 -> 102361 (-1.65%)
> helped: 36
> HURT: 15
>
> total cycles in shared programs: 220065064 -> 154529655 (-29.78%)
> cycles in affected programs: 126063017 -> 60527608 (-51.99%)
> helped: 51
> HURT: 0
>
> total loops in shared programs: 2515 -> 2308 (-8.23%)
> loops in affected programs: 903 -> 696 (-22.92%)
> helped: 51
> HURT: 0
>
> total spills in shared programs: 4370 -> 4124 (-5.63%)
> spills in affected programs: 1397 -> 1151 (-17.61%)
> helped: 9
> HURT: 12
>
> total fills in shared programs: 4581 -> 4419 (-3.54%)
> fills in affected programs: 2201 -> 2039 (-7.36%)
> helped: 9
> HURT: 15
> ---
>  src/compiler/nir/nir_opt_loop_unroll.c | 113 +++++++++++++++++--------
>  1 file changed, 76 insertions(+), 37 deletions(-)
>
> diff --git a/src/compiler/nir/nir_opt_loop_unroll.c
> b/src/compiler/nir/nir_opt_loop_unroll.c
> index 9c33267cb72..fa60523aac7 100644
> --- a/src/compiler/nir/nir_opt_loop_unroll.c
> +++ b/src/compiler/nir/nir_opt_loop_unroll.c
> @@ -67,7 +67,6 @@ loop_prepare_for_unroll(nir_loop *loop)
>     /* Remove continue if its the last instruction in the loop */
>     nir_instr *last_instr =
> nir_block_last_instr(nir_loop_last_block(loop));
>     if (last_instr && last_instr->type == nir_instr_type_jump) {
> -      assert(nir_instr_as_jump(last_instr)->type == nir_jump_continue);
>        nir_instr_remove(last_instr);
>     }
>  }
> @@ -474,54 +473,91 @@ complex_unroll(nir_loop *loop, nir_loop_terminator
> *unlimit_term,
>  static bool
>  wrapper_unroll(nir_loop *loop)
>  {
> -   bool progress = false;
> -
> -   nir_block *blk_after_loop =
> -      nir_cursor_current_block(nir_after_cf_node(&loop->cf_node));
> -
> -   /* There may still be some single src phis following the loop that
> -    * have not yet been cleaned up by another pass. Tidy those up before
> -    * unrolling the loop.
> -    */
> -   nir_foreach_instr_safe(instr, blk_after_loop) {
> -      if (instr->type != nir_instr_type_phi)
> -         break;
> +   if (!list_empty(&loop->info->loop_terminator_list)) {
>
> -      nir_phi_instr *phi = nir_instr_as_phi(instr);
> -      assert(exec_list_length(&phi->srcs) == 1);
> +      /* Unrolling a loop with a large number of exits can result in a
> +       * large inrease in register pressure. For now we just skip
> +       * unrolling if we have more than 3 exits (not including the break
> +       * at the end of the loop).
> +       *
> +       * TODO: Most loops that fit this pattern are simply switch
> +       * statements that are converted to a loop to take advantage of
> +       * exiting jump instruction handling. In this case we could make
> +       * use of a binary seach pattern like we do in
> +       * nir_lower_indirect_derefs(), this should allow us to unroll the
> +       * loops in an optimal way and should also avoid some of the
> +       * register pressure that comes from simply nesting the
> +       * terminators one after the other.
> +       */
> +      if (list_length(&loop->info->loop_terminator_list) > 3)
> +         return false;
> +
> +      loop_prepare_for_unroll(loop);
> +
> +      nir_cursor cursor = nir_after_block(nir_loop_last_block(loop));
>

Maybe call this loop_end; that's less generic than "cursor".


> +      list_for_each_entry(nir_loop_terminator, terminator,
> +                          &loop->info->loop_terminator_list,
> +                          loop_terminator_link) {
>

I presume the terminator list is guaranteed to be sorted top to bottom?

Reviewed-by: Jason Ekstrand <jason at jlekstrand.net>


> +
> +         /* Remove break from the terminator */
> +         nir_instr *break_instr =
> +            nir_block_last_instr(terminator->break_block);
> +         nir_instr_remove(break_instr);
> +
> +         /* Pluck out the loop body. */
> +         nir_cf_list loop_body;
> +         nir_cf_extract(&loop_body,
> +                        nir_after_cf_node(&terminator->nif->cf_node),
> +                        cursor);
> +
> +         /* Reinsert loop body into continue from block */
> +         nir_cf_reinsert(&loop_body,
> +
>  nir_after_block(terminator->continue_from_block));
> +
> +         cursor = terminator->continue_from_then ?
> +           nir_after_block(nir_if_last_then_block(terminator->nif)) :
> +           nir_after_block(nir_if_last_else_block(terminator->nif));
> +      }
> +   } else {
> +      nir_block *blk_after_loop =
> +         nir_cursor_current_block(nir_after_cf_node(&loop->cf_node));
>
> -      nir_phi_src *phi_src = exec_node_data(nir_phi_src,
> -
> exec_list_get_head(&phi->srcs),
> -                                            node);
> +      /* There may still be some single src phis following the loop that
> +       * have not yet been cleaned up by another pass. Tidy those up
> +       * before unrolling the loop.
> +       */
> +      nir_foreach_instr_safe(instr, blk_after_loop) {
> +         if (instr->type != nir_instr_type_phi)
> +            break;
>
> -      nir_ssa_def_rewrite_uses(&phi->dest.ssa, phi_src->src);
> -      nir_instr_remove(instr);
> +         nir_phi_instr *phi = nir_instr_as_phi(instr);
> +         assert(exec_list_length(&phi->srcs) == 1);
>
> -      progress = true;
> -   }
> +         nir_phi_src *phi_src =
> +            exec_node_data(nir_phi_src, exec_list_get_head(&phi->srcs),
> node);
>
> -   nir_block *last_loop_blk = nir_loop_last_block(loop);
> -   if (nir_block_ends_in_break(last_loop_blk)) {
> +         nir_ssa_def_rewrite_uses(&phi->dest.ssa, phi_src->src);
> +         nir_instr_remove(instr);
> +      }
>
>        /* Remove break at end of the loop */
> +      nir_block *last_loop_blk = nir_loop_last_block(loop);
>        nir_instr *break_instr = nir_block_last_instr(last_loop_blk);
>        nir_instr_remove(break_instr);
> +   }
>
> -      /* Pluck out the loop body. */
> -      nir_cf_list loop_body;
> -      nir_cf_extract(&loop_body,
> nir_before_block(nir_loop_first_block(loop)),
> -                     nir_after_block(nir_loop_last_block(loop)));
> -
> -      /* Reinsert loop body after the loop */
> -      nir_cf_reinsert(&loop_body, nir_after_cf_node(&loop->cf_node));
> +   /* Pluck out the loop body. */
> +   nir_cf_list loop_body;
> +   nir_cf_extract(&loop_body,
> nir_before_block(nir_loop_first_block(loop)),
> +                  nir_after_block(nir_loop_last_block(loop)));
>
> -      /* The loop has been unrolled so remove it. */
> -      nir_cf_node_remove(&loop->cf_node);
> +   /* Reinsert loop body after the loop */
> +   nir_cf_reinsert(&loop_body, nir_after_cf_node(&loop->cf_node));
>
> -      progress = true;
> -   }
> +   /* The loop has been unrolled so remove it. */
> +   nir_cf_node_remove(&loop->cf_node);
>
> -   return progress;
> +   return true;
>  }
>
>  static bool
> @@ -585,9 +621,12 @@ process_loops(nir_shader *sh, nir_cf_node *cf_node,
> bool *has_nested_loop_out)
>         * statements in a loop like this.
>         */
>        if (loop->info->limiting_terminator == NULL &&
> -          list_empty(&loop->info->loop_terminator_list) &&
>            !loop->info->complex_loop) {
>
> +         nir_block *last_loop_blk = nir_loop_last_block(loop);
> +         if (!nir_block_ends_in_break(last_loop_blk))
> +            goto exit;
> +
>           progress = wrapper_unroll(loop);
>
>           goto exit;
> --
> 2.17.1
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20180828/2ee6831f/attachment.html>


More information about the mesa-dev mailing list