agx: Detect conditional breaks

Search for code like

   if ... {
      break
   }

and replace with a break_if pseudo-instruction for optimized handling, since the
break_if lowering is better than the original code.

   total instructions in shared programs: 1764596 -> 1764350 (-0.01%)
   instructions in affected programs: 24540 -> 24294 (-1.00%)
   helped: 78
   HURT: 0
   Instructions are helped.

   total bytes in shared programs: 11611196 -> 11610212 (<.01%)
   bytes in affected programs: 166458 -> 165474 (-0.59%)
   helped: 78
   HURT: 0
   Bytes are helped.

shader-db probably understates the benefit here, since this optimizes the body
of loops.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25052>
This commit is contained in:
Alyssa Rosenzweig
2023-08-29 17:01:54 -04:00
committed by Marge Bot
parent a009f39fca
commit 6e0ae2c316
4 changed files with 84 additions and 0 deletions
+1
View File
@@ -2551,6 +2551,7 @@ agx_compile_function_nir(nir_shader *nir, nir_function_impl *impl,
agx_insert_waits(ctx);
agx_opt_empty_else(ctx);
agx_opt_break_if(ctx);
agx_lower_pseudo(ctx);
if (agx_should_dump(nir, AGX_DBG_SHADERS))
+1
View File
@@ -801,6 +801,7 @@ void agx_ra(agx_context *ctx);
void agx_lower_64bit_postra(agx_context *ctx);
void agx_insert_waits(agx_context *ctx);
void agx_opt_empty_else(agx_context *ctx);
void agx_opt_break_if(agx_context *ctx);
void agx_pack_binary(agx_context *ctx, struct util_dynarray *emission);
#ifndef NDEBUG
+81
View File
@@ -0,0 +1,81 @@
/*
* Copyright 2023 Valve Corporation
* SPDX-License-Identifier: MIT
*/
#include "util/list.h"
#include "agx_builder.h"
#include "agx_compiler.h"
/*
* Replaces conditional breaks:
*
* if_cmp x, y, n=1 {
* break #value
* }
* pop_exec n=1
*
* with break_if_*cmp pseudo-instructions for further optimization. This
* assumes agx_opt_empty_else has already run.
*/
static void
match_block(agx_context *ctx, agx_block *block)
{
agx_instr *if_ = agx_last_instr(block);
if (!if_ ||
(if_->op != AGX_OPCODE_IF_ICMP && if_->op != AGX_OPCODE_IF_FCMP) ||
if_->nest != 1)
return;
/* If's fallthrough to the then */
agx_block *then_block = agx_next_block(block);
assert(block->successors[0] == then_block && "successors for if");
/* We're searching for a single block then, so the next block is else */
agx_block *else_block = agx_next_block(then_block);
if (block->successors[1] != else_block)
return;
if (!list_is_singular(&then_block->instructions) ||
!list_is_singular(&else_block->instructions))
return;
agx_instr *break_ = agx_last_instr(then_block);
agx_instr *pop = agx_last_instr(else_block);
if (break_->op != AGX_OPCODE_BREAK || pop->op != AGX_OPCODE_POP_EXEC ||
pop->nest != 1)
return;
/* Find the successor of the if */
agx_block *after = else_block->successors[0];
assert(else_block->successors[1] == NULL && "single successor");
/* We are dropping one level of nesting (the if) when rewriting */
assert(break_->nest >= 1 && "must at least break out of the if");
unsigned new_nest = break_->nest - 1;
/* Rewrite */
agx_builder b = agx_init_builder(ctx, agx_before_block(after));
if (if_->op == AGX_OPCODE_IF_FCMP) {
agx_break_if_fcmp(&b, if_->src[0], if_->src[1], new_nest,
if_->invert_cond, if_->fcond);
} else {
agx_break_if_icmp(&b, if_->src[0], if_->src[1], new_nest,
if_->invert_cond, if_->icond);
}
agx_remove_instruction(if_);
agx_remove_instruction(break_);
agx_remove_instruction(pop);
}
void
agx_opt_break_if(agx_context *ctx)
{
agx_foreach_block(ctx, block) {
match_block(ctx, block);
}
}
+1
View File
@@ -27,6 +27,7 @@ libasahi_agx_files = files(
'agx_pressure_schedule.c',
'agx_ir.c',
'agx_opt_cse.c',
'agx_opt_break_if.c',
'agx_opt_empty_else.c',
'agx_optimizer.c',
'agx_register_allocate.c',