nir: remove redundant passes from nir_opt_if()

These are now covered by nir_opt_loop():
- opt_if_loop_last_continue()
- opt_merge_breaks()
- opt_if_loop_terminator()

Reviewed-by: Konstantin Seurer <konstantin.seurer@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24940>
This commit is contained in:
Daniel Schürmann
2023-08-08 16:48:49 +02:00
committed by Marge Bot
parent 5b1b5cd794
commit bdbf873b0f
13 changed files with 12 additions and 353 deletions

View File

@@ -210,8 +210,7 @@ radv_optimize_nir(struct nir_shader *shader, bool optimize_conservatively)
NIR_LOOP_PASS(progress, skip, shader, nir_opt_remove_phis);
NIR_LOOP_PASS(progress, skip, shader, nir_opt_dce);
}
NIR_LOOP_PASS_NOT_IDEMPOTENT(progress, skip, shader, nir_opt_if,
nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_LOOP_PASS_NOT_IDEMPOTENT(progress, skip, shader, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_LOOP_PASS(progress, skip, shader, nir_opt_dead_cf);
NIR_LOOP_PASS(progress, skip, shader, nir_opt_cse);
NIR_LOOP_PASS(progress, skip, shader, nir_opt_peephole_select, 8, true, true);

View File

@@ -17,7 +17,6 @@
#include "nir_builder.h"
#include "nir_serialize.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
@@ -214,7 +213,7 @@ compile(void *memctx, const uint32_t *spirv, size_t spirv_size)
*/
NIR_PASS_V(nir, nir_lower_convert_alu_types, NULL);
NIR_PASS_V(nir, nir_opt_if, nir_opt_if_aggressive_last_continue);
NIR_PASS_V(nir, nir_opt_if, 0);
NIR_PASS_V(nir, nir_opt_idiv_const, 16);
optimize(nir);

View File

@@ -6301,9 +6301,8 @@ bool nir_opt_gcm(nir_shader *shader, bool value_number);
bool nir_opt_idiv_const(nir_shader *shader, unsigned min_bit_size);
typedef enum {
nir_opt_if_aggressive_last_continue = (1 << 0),
nir_opt_if_optimize_phi_true_false = (1 << 1),
nir_opt_if_avoid_64bit_phis = (1 << 2),
nir_opt_if_optimize_phi_true_false = (1 << 0),
nir_opt_if_avoid_64bit_phis = (1 << 1),
} nir_opt_if_options;
bool nir_opt_if(nir_shader *shader, nir_opt_if_options options);

View File

@@ -697,125 +697,6 @@ is_block_empty(nir_block *block)
exec_list_is_empty(&block->instr_list);
}
static bool
nir_block_ends_in_continue(nir_block *block)
{
if (exec_list_is_empty(&block->instr_list))
return false;
nir_instr *instr = nir_block_last_instr(block);
return instr->type == nir_instr_type_jump &&
nir_instr_as_jump(instr)->type == nir_jump_continue;
}
/**
* This optimization turns:
*
* loop {
* ...
* if (cond) {
* do_work_1();
* continue;
* } else {
* }
* do_work_2();
* }
*
* into:
*
* loop {
* ...
* if (cond) {
* do_work_1();
* continue;
* } else {
* do_work_2();
* }
* }
*
* The continue should then be removed by nir_opt_trivial_continues() and the
* loop can potentially be unrolled.
*
* Note: Unless the function param aggressive_last_continue==true do_work_2()
* is only ever blocks and nested loops. We avoid nesting other if-statments
* in the branch as this can result in increased register pressure, and in
* the i965 driver it causes a large amount of spilling in shader-db.
* For RADV however nesting these if-statements allows further continues to be
* remove and provides a significant FPS boost in Doom, which is why we have
* opted for this special bool to enable more aggresive optimisations.
* TODO: The GCM pass solves most of the spilling regressions in i965, if it
* is ever enabled we should consider removing the aggressive_last_continue
* param.
*/
static bool
opt_if_loop_last_continue(nir_loop *loop, bool aggressive_last_continue)
{
nir_if *nif = NULL;
bool then_ends_in_continue = false;
bool else_ends_in_continue = false;
/* Scan the control flow of the loop from the last to the first node
* looking for an if-statement we can optimise.
*/
nir_block *last_block = nir_loop_last_block(loop);
nir_cf_node *if_node = nir_cf_node_prev(&last_block->cf_node);
while (if_node) {
if (if_node->type == nir_cf_node_if) {
nif = nir_cf_node_as_if(if_node);
nir_block *then_block = nir_if_last_then_block(nif);
nir_block *else_block = nir_if_last_else_block(nif);
then_ends_in_continue = nir_block_ends_in_continue(then_block);
else_ends_in_continue = nir_block_ends_in_continue(else_block);
/* If both branches end in a jump do nothing, this should be handled
* by nir_opt_dead_cf().
*/
if ((then_ends_in_continue || nir_block_ends_in_break(then_block)) &&
(else_ends_in_continue || nir_block_ends_in_break(else_block)))
return false;
/* If continue found stop scanning and attempt optimisation, or
*/
if (then_ends_in_continue || else_ends_in_continue ||
!aggressive_last_continue)
break;
}
if_node = nir_cf_node_prev(if_node);
}
/* If we didn't find an if to optimise return */
if (!nif || (!then_ends_in_continue && !else_ends_in_continue))
return false;
/* If there is nothing after the if-statement we bail */
if (&nif->cf_node == nir_cf_node_prev(&last_block->cf_node) &&
exec_list_is_empty(&last_block->instr_list))
return false;
/* If there are single-source phis in the last block,
* get rid of them first
*/
nir_opt_remove_phis_block(last_block);
/* Move the last block of the loop inside the last if-statement */
nir_cf_list tmp;
nir_cf_extract(&tmp, nir_after_cf_node(if_node),
nir_after_block(last_block));
if (then_ends_in_continue)
nir_cf_reinsert(&tmp, nir_after_cf_list(&nif->else_list));
else
nir_cf_reinsert(&tmp, nir_after_cf_list(&nif->then_list));
/* In order to avoid running nir_lower_reg_intrinsics_to_ssa_impl() every
* time an if opt makes progress we leave nir_opt_trivial_continues() to
* remove the continue now that the end of the loop has been simplified.
*/
return true;
}
/* Walk all the phis in the block immediately following the if statement and
* swap the blocks.
*/
@@ -947,210 +828,6 @@ opt_if_phi_is_condition(nir_builder *b, nir_if *nif)
return progress;
}
/**
* This optimization tries to merge two break statements into a single break.
* For this purpose, it checks if both branch legs end in a break or
* if one branch leg ends in a break, and the other one does so after the
* branch.
*
* This optimization turns
*
* loop {
* ...
* if (cond) {
* do_work_1();
* break;
* } else {
* do_work_2();
* break;
* }
* }
*
* into:
*
* loop {
* ...
* if (cond) {
* do_work_1();
* } else {
* do_work_2();
* }
* break;
* }
*
* but also situations like
*
* loop {
* ...
* if (cond1) {
* if (cond2) {
* do_work_1();
* break;
* } else {
* do_work_2();
* }
* do_work_3();
* break;
* } else {
* ...
* }
* }
*
* into:
*
* loop {
* ...
* if (cond1) {
* if (cond2) {
* do_work_1();
* } else {
* do_work_2();
* do_work_3();
* }
* break;
* } else {
* ...
* }
* }
*/
static bool
opt_merge_breaks(nir_if *nif)
{
nir_block *last_then = nir_if_last_then_block(nif);
nir_block *last_else = nir_if_last_else_block(nif);
bool then_break = nir_block_ends_in_break(last_then);
bool else_break = nir_block_ends_in_break(last_else);
/* If both branch legs end in a break, merge the break after the branch */
if (then_break && else_break) {
nir_block *after_if = nir_cf_node_cf_tree_next(&nif->cf_node);
/* Make sure that the successor is empty.
* If not we let nir_opt_dead_cf() clean it up first.
*/
if (!is_block_empty(after_if))
return false;
nir_lower_phis_to_regs_block(last_then->successors[0]);
nir_instr_remove_v(nir_block_last_instr(last_then));
nir_instr *jump = nir_block_last_instr(last_else);
nir_instr_remove_v(jump);
nir_instr_insert(nir_after_block(after_if), jump);
return true;
}
/* Single break: If there's a break after the branch and the non-breaking
* side of the if falls through to it, then hoist that code after up into
* the if and leave just a single break there.
*/
if (then_break || else_break) {
/* At least one branch leg must fall-through */
if (nir_block_ends_in_jump(last_then) && nir_block_ends_in_jump(last_else))
return false;
/* Check if there is a single break after the IF */
nir_cf_node *first = nir_cf_node_next(&nif->cf_node);
nir_cf_node *last = first;
while (!nir_cf_node_is_last(last)) {
if (contains_other_jump(last, NULL))
return false;
last = nir_cf_node_next(last);
}
assert(last->type == nir_cf_node_block);
if (!nir_block_ends_in_break(nir_cf_node_as_block(last)))
return false;
/* Hoist the code from after the IF into the falling-through branch leg */
nir_opt_remove_phis_block(nir_cf_node_as_block(first));
nir_block *break_block = then_break ? last_then : last_else;
nir_lower_phis_to_regs_block(break_block->successors[0]);
nir_cf_list tmp;
nir_cf_extract(&tmp, nir_before_cf_node(first),
nir_after_block_before_jump(nir_cf_node_as_block(last)));
if (then_break)
nir_cf_reinsert(&tmp, nir_after_block(last_else));
else
nir_cf_reinsert(&tmp, nir_after_block(last_then));
nir_instr_remove_v(nir_block_last_instr(break_block));
return true;
}
return false;
}
/**
* 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_block_ends_in_jump(continue_from_blk))
return false;
/* Even though this if statement has a jump on one side, we may still have
* phis afterwards. Single-source phis can be produced by loop unrolling
* or dead control-flow passes and are perfectly legal. Run a quick phi
* removal on the block after the if to clean up any such phis.
*/
nir_opt_remove_phis_block(nir_cf_node_as_block(nir_cf_node_next(&nif->cf_node)));
/* 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));
return true;
}
static bool
evaluate_if_condition(nir_if *nif, nir_cursor cursor, bool *value)
{
@@ -1535,7 +1212,6 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list,
options);
progress |= opt_if_cf_list(b, &nif->else_list,
options);
progress |= opt_if_loop_terminator(nif);
progress |= opt_if_merge(nif);
progress |= opt_if_simplification(b, nif);
if (options & nir_opt_if_optimize_phi_true_false)
@@ -1549,8 +1225,6 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list,
progress |= opt_if_cf_list(b, &loop->body,
options);
progress |= opt_simplify_bcsel_of_phi(b, loop);
progress |= opt_if_loop_last_continue(loop,
options & nir_opt_if_aggressive_last_continue);
break;
}
@@ -1579,13 +1253,6 @@ opt_if_regs_cf_list(struct exec_list *cf_list)
nir_if *nif = nir_cf_node_as_if(cf_node);
progress |= opt_if_regs_cf_list(&nif->then_list);
progress |= opt_if_regs_cf_list(&nif->else_list);
if (opt_merge_breaks(nif)) {
/* This optimization might move blocks
* from after the NIF into the NIF */
progress = true;
opt_if_regs_cf_list(&nif->then_list);
opt_if_regs_cf_list(&nif->else_list);
}
break;
}

View File

@@ -3331,7 +3331,7 @@ ntt_optimize_nir(struct nir_shader *s, struct pipe_screen *screen,
NIR_PASS(progress, s, nir_opt_copy_prop_vars);
NIR_PASS(progress, s, nir_opt_dead_write_vars);
NIR_PASS(progress, s, nir_opt_if, nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_peephole_select,
control_flow_depth == 0 ? ~0 : 8, true, true);
NIR_PASS(progress, s, nir_opt_algebraic);

View File

@@ -205,9 +205,7 @@ i915_optimize_nir(struct nir_shader *s)
NIR_PASS(progress, s, nir_opt_dead_cf);
NIR_PASS(progress, s, nir_opt_cse);
NIR_PASS(progress, s, nir_opt_find_array_copies);
NIR_PASS(progress, s, nir_opt_if,
nir_opt_if_aggressive_last_continue |
nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_peephole_select, ~0 /* flatten all IFs. */,
true, true);
NIR_PASS(progress, s, nir_opt_algebraic);

View File

@@ -85,7 +85,7 @@ r300_optimize_nir(struct nir_shader *s, struct pipe_screen *screen)
NIR_PASS(progress, s, nir_opt_copy_prop_vars);
NIR_PASS(progress, s, nir_opt_dead_write_vars);
NIR_PASS(progress, s, nir_opt_if, nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, s, nir_opt_peephole_select, is_r500 ? 8 : ~0, true, true);
NIR_PASS(progress, s, nir_opt_algebraic);
NIR_PASS(progress, s, nir_opt_constant_folding);

View File

@@ -98,7 +98,6 @@ void si_nir_opts(struct si_screen *sscreen, struct nir_shader *nir, bool first)
NIR_PASS(progress, nir, nir_opt_dce);
/* nir_opt_if_optimize_phi_true_false is disabled on LLVM14 (#6976) */
NIR_PASS(lower_phis_to_scalar, nir, nir_opt_if,
nir_opt_if_aggressive_last_continue |
nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_dead_cf);

View File

@@ -247,7 +247,7 @@ optimize(nir_shader *nir)
NIR_PASS(progress, nir, nir_opt_dce);
NIR_PASS(progress, nir, nir_opt_remove_phis);
}
NIR_PASS(progress, nir, nir_opt_if, nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_dead_cf);
NIR_PASS(progress, nir, nir_opt_conditional_discard);
NIR_PASS(progress, nir, nir_opt_remove_phis);

View File

@@ -356,8 +356,7 @@ fn opt_nir(nir: &mut NirShader, dev: &Device) {
progress |= nir_pass!(
nir,
nir_opt_if,
nir_opt_if_options::nir_opt_if_aggressive_last_continue
| nir_opt_if_options::nir_opt_if_optimize_phi_true_false,
nir_opt_if_options::nir_opt_if_optimize_phi_true_false,
);
progress |= nir_pass!(nir, nir_opt_dead_cf);
progress |= nir_pass!(nir, nir_opt_remove_phis);

View File

@@ -749,7 +749,7 @@ clc_spirv_to_dxil(struct clc_libclc *lib,
NIR_PASS(progress, nir, nir_lower_var_copies);
NIR_PASS(progress, nir, nir_lower_vars_to_ssa);
NIR_PASS(progress, nir, nir_opt_algebraic);
NIR_PASS(progress, nir, nir_opt_if, nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_dead_cf);
NIR_PASS(progress, nir, nir_opt_remove_phis);
NIR_PASS(progress, nir, nir_opt_peephole_select, 8, true, true);

View File

@@ -6232,7 +6232,7 @@ optimize_nir(struct nir_shader *s, const struct nir_to_dxil_options *opts)
NIR_PASS(progress, s, nir_opt_remove_phis);
NIR_PASS(progress, s, nir_opt_dce);
NIR_PASS(progress, s, nir_opt_if,
nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false | nir_opt_if_avoid_64bit_phis);
nir_opt_if_optimize_phi_true_false | nir_opt_if_avoid_64bit_phis);
NIR_PASS(progress, s, nir_opt_dead_cf);
NIR_PASS(progress, s, nir_opt_cse);
NIR_PASS(progress, s, nir_opt_peephole_select, 8, true, true);

View File

@@ -155,8 +155,7 @@ nvk_cg_optimize_nir(nir_shader *nir)
NIR_PASS(progress, nir, nir_opt_remove_phis);
NIR_PASS(progress, nir, nir_opt_dce);
}
NIR_PASS(progress, nir, nir_opt_if,
nir_opt_if_aggressive_last_continue | nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_if, nir_opt_if_optimize_phi_true_false);
NIR_PASS(progress, nir, nir_opt_dead_cf);
NIR_PASS(progress, nir, nir_opt_cse);
/*