2024-04-16 11:32:46 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright 2024 Valve Corporation
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "nir.h"
|
|
|
|
|
|
2025-12-03 23:00:48 -05:00
|
|
|
typedef struct {
|
|
|
|
|
nir_loop *loop;
|
|
|
|
|
} licm_state;
|
|
|
|
|
|
2024-04-16 11:32:46 +02:00
|
|
|
static bool
|
2025-12-03 23:00:48 -05:00
|
|
|
defined_before_loop(nir_src *src, void *_state)
|
2024-04-16 11:32:46 +02:00
|
|
|
{
|
2025-12-03 23:00:48 -05:00
|
|
|
licm_state *state = (licm_state *)_state;
|
|
|
|
|
|
|
|
|
|
/* The current instruction is loop-invariant only if its sources are before
|
|
|
|
|
* the loop.
|
|
|
|
|
*/
|
|
|
|
|
return nir_def_block(src->ssa)->index <=
|
|
|
|
|
nir_loop_predecessor_block(state->loop)->index;
|
2024-04-16 11:32:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2025-12-03 23:00:48 -05:00
|
|
|
is_instr_loop_invariant(nir_instr *instr, licm_state *state)
|
2024-04-16 11:32:46 +02:00
|
|
|
{
|
|
|
|
|
switch (instr->type) {
|
|
|
|
|
case nir_instr_type_load_const:
|
|
|
|
|
case nir_instr_type_undef:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case nir_instr_type_intrinsic:
|
|
|
|
|
if (!nir_intrinsic_can_reorder(nir_instr_as_intrinsic(instr)))
|
|
|
|
|
return false;
|
|
|
|
|
FALLTHROUGH;
|
|
|
|
|
|
|
|
|
|
case nir_instr_type_alu:
|
|
|
|
|
case nir_instr_type_tex:
|
|
|
|
|
case nir_instr_type_deref:
|
2025-12-03 23:00:48 -05:00
|
|
|
return nir_foreach_src(instr, defined_before_loop, state);
|
2024-04-16 11:32:46 +02:00
|
|
|
|
|
|
|
|
case nir_instr_type_phi:
|
|
|
|
|
case nir_instr_type_call:
|
2026-02-13 21:30:15 -08:00
|
|
|
case nir_instr_type_cmat_call:
|
2024-04-16 11:32:46 +02:00
|
|
|
case nir_instr_type_jump:
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2025-12-03 23:00:48 -05:00
|
|
|
visit_block(nir_block *block, licm_state *state)
|
2024-04-16 11:32:46 +02:00
|
|
|
{
|
2025-12-03 23:00:48 -05:00
|
|
|
assert(state->loop);
|
|
|
|
|
|
2024-04-16 11:32:46 +02:00
|
|
|
bool progress = false;
|
|
|
|
|
nir_foreach_instr_safe(instr, block) {
|
2025-12-03 23:00:48 -05:00
|
|
|
if (is_instr_loop_invariant(instr, state)) {
|
2024-04-16 11:32:46 +02:00
|
|
|
nir_instr_remove(instr);
|
2025-12-03 23:00:48 -05:00
|
|
|
nir_instr_insert_after_block(nir_loop_predecessor_block(state->loop),
|
|
|
|
|
instr);
|
2024-04-16 11:32:46 +02:00
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
should_optimize_loop(nir_loop *loop)
|
|
|
|
|
{
|
|
|
|
|
/* Ignore loops without back-edge */
|
2026-02-18 10:20:25 +00:00
|
|
|
if (!nir_loop_has_back_edge(loop))
|
2024-04-16 11:32:46 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
nir_foreach_block_in_cf_node(block, &loop->cf_node) {
|
|
|
|
|
/* Check for an early exit inside the loop. */
|
|
|
|
|
nir_foreach_instr(instr, block) {
|
|
|
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
|
|
|
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
|
|
|
|
|
if (intrin->intrinsic == nir_intrinsic_terminate ||
|
|
|
|
|
intrin->intrinsic == nir_intrinsic_terminate_if)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The loop must not contains any return statement. */
|
|
|
|
|
if (nir_block_ends_in_return_or_halt(block))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2025-12-03 23:00:48 -05:00
|
|
|
visit_cf_list(struct exec_list *list, licm_state *state)
|
2024-04-16 11:32:46 +02:00
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
foreach_list_typed(nir_cf_node, node, node, list) {
|
|
|
|
|
switch (node->type) {
|
|
|
|
|
case nir_cf_node_block: {
|
2025-12-03 23:00:48 -05:00
|
|
|
/* By only visiting blocks which dominate the block after the loop,
|
|
|
|
|
* we ensure that we don't speculatively hoist any instructions
|
2024-04-16 11:32:46 +02:00
|
|
|
* which otherwise might not be executed.
|
|
|
|
|
*
|
|
|
|
|
* Note, that the proper check would be whether this block
|
2025-12-03 23:00:48 -05:00
|
|
|
* postdominates the block before the loop.
|
2024-04-16 11:32:46 +02:00
|
|
|
*/
|
|
|
|
|
nir_block *block = nir_cf_node_as_block(node);
|
2025-12-03 23:00:48 -05:00
|
|
|
if (state->loop &&
|
|
|
|
|
nir_block_dominates(block, nir_loop_successor_block(state->loop)))
|
|
|
|
|
progress |= visit_block(block, state);
|
2024-04-16 11:32:46 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case nir_cf_node_if: {
|
|
|
|
|
nir_if *nif = nir_cf_node_as_if(node);
|
2025-12-03 23:00:48 -05:00
|
|
|
progress |= visit_cf_list(&nif->then_list, state);
|
|
|
|
|
progress |= visit_cf_list(&nif->else_list, state);
|
2024-04-16 11:32:46 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case nir_cf_node_loop: {
|
2025-12-03 23:00:48 -05:00
|
|
|
nir_loop *inner_loop = nir_cf_node_as_loop(node);
|
|
|
|
|
nir_loop *outer_loop = state->loop;
|
|
|
|
|
|
|
|
|
|
/* If we don't optimize this loop, we treat it like a block, so we
|
|
|
|
|
* don't do LICM from it per se, but if this loop is nested inside
|
|
|
|
|
* another loop that's optimized, we still do LICM from this CF list
|
|
|
|
|
* for the outer loop.
|
|
|
|
|
*/
|
|
|
|
|
if (should_optimize_loop(inner_loop))
|
|
|
|
|
state->loop = inner_loop;
|
|
|
|
|
|
|
|
|
|
progress |= visit_cf_list(&inner_loop->body, state);
|
|
|
|
|
progress |= visit_cf_list(&inner_loop->continue_list, state);
|
|
|
|
|
state->loop = outer_loop;
|
2024-04-16 11:32:46 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case nir_cf_node_function:
|
2025-07-23 09:17:35 +02:00
|
|
|
UNREACHABLE("NIR LICM: Unsupported cf_node type.");
|
2024-04-16 11:32:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
nir_opt_licm(nir_shader *shader)
|
|
|
|
|
{
|
2025-12-03 23:00:48 -05:00
|
|
|
licm_state state = {0};
|
2024-04-16 11:32:46 +02:00
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
nir_foreach_function_impl(impl, shader) {
|
|
|
|
|
nir_metadata_require(impl, nir_metadata_block_index |
|
|
|
|
|
nir_metadata_dominance);
|
|
|
|
|
|
2025-12-03 23:00:48 -05:00
|
|
|
state.loop = NULL;
|
|
|
|
|
|
|
|
|
|
progress |= nir_progress(visit_cf_list(&impl->body, &state), impl,
|
treewide: Switch to nir_progress
Via the Coccinelle patch at the end of the commit message, followed by
sed -ie 's/progress = progress | /progress |=/g' $(git grep -l 'progress = prog')
ninja -C ~/mesa/build clang-format
cd ~/mesa/src/compiler/nir && clang-format -i *.c
agxfmt
@@
identifier prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
-return prog;
+return nir_progress(prog, impl, metadata);
@@
expression prog_expr, impl, metadata;
@@
-if (prog_expr) {
-nir_metadata_preserve(impl, metadata);
-return true;
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-return false;
-}
+bool progress = prog_expr;
+return nir_progress(progress, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-nir_metadata_preserve(impl, prog ? (metadata) : nir_metadata_all);
-return prog;
+return nir_progress(prog, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-nir_metadata_preserve(impl, prog ? (metadata) : nir_metadata_all);
+nir_progress(prog, impl, metadata);
@@
expression impl, metadata;
@@
-nir_metadata_preserve(impl, metadata);
-return true;
+return nir_progress(true, impl, metadata);
@@
expression impl;
@@
-nir_metadata_preserve(impl, nir_metadata_all);
-return false;
+return nir_no_progress(impl);
@@
identifier other_prog, prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
-other_prog |= prog;
+other_prog = other_prog | nir_progress(prog, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+nir_progress(prog, impl, metadata);
@@
identifier other_prog, prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-other_prog = true;
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+other_prog = other_prog | nir_progress(prog, impl, metadata);
@@
expression prog_expr, impl, metadata;
identifier prog;
@@
-if (prog_expr) {
-nir_metadata_preserve(impl, metadata);
-prog = true;
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+bool impl_progress = prog_expr;
+prog = prog | nir_progress(impl_progress, impl, metadata);
@@
identifier other_prog, prog;
expression impl, metadata;
@@
-if (prog) {
-other_prog = true;
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+other_prog = other_prog | nir_progress(prog, impl, metadata);
@@
expression prog_expr, impl, metadata;
identifier prog;
@@
-if (prog_expr) {
-prog = true;
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+bool impl_progress = prog_expr;
+prog = prog | nir_progress(impl_progress, impl, metadata);
@@
expression prog_expr, impl, metadata;
@@
-if (prog_expr) {
-nir_metadata_preserve(impl, metadata);
-} else {
-nir_metadata_preserve(impl, nir_metadata_all);
-}
+bool impl_progress = prog_expr;
+nir_progress(impl_progress, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-nir_metadata_preserve(impl, metadata);
-prog = true;
+prog = nir_progress(true, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-}
-return prog;
+return nir_progress(prog, impl, metadata);
@@
identifier prog;
expression impl, metadata;
@@
-if (prog) {
-nir_metadata_preserve(impl, metadata);
-}
+nir_progress(prog, impl, metadata);
@@
expression impl;
@@
-nir_metadata_preserve(impl, nir_metadata_all);
+nir_no_progress(impl);
@@
expression impl, metadata;
@@
-nir_metadata_preserve(impl, metadata);
+nir_progress(true, impl, metadata);
squashme! sed -ie 's/progress = progress | /progress |=/g' $(git grep -l 'progress = prog')
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Georg Lehmann <dadschoorse@gmail.com>
Acked-by: Faith Ekstrand <faith.ekstrand@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33722>
2025-02-24 15:10:33 -05:00
|
|
|
nir_metadata_block_index | nir_metadata_dominance);
|
2024-04-16 11:32:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|