2014-11-11 16:11:34 -08:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2014 Intel Corporation
|
2025-11-11 16:20:24 -05:00
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Common Subexpression Elimination
|
|
|
|
|
*
|
|
|
|
|
* This implementation behaves more like Global Value Numbering (GVN) than
|
|
|
|
|
* traditional CSE. While traditional CSE eliminates redundant instructions
|
|
|
|
|
* that have identical representations, GVN eliminates redundant instructions
|
|
|
|
|
* that have identical behavior.
|
|
|
|
|
*
|
|
|
|
|
* The pass walks the shader and adds instructions into a set whose equality
|
|
|
|
|
* function returns whether the behavior of 2 instructions is identical.
|
|
|
|
|
* When we encounter an instruction that is already in the set, the instruction
|
|
|
|
|
* is eliminated if the instruction in the set dominates it, else
|
|
|
|
|
* the instruction replaces the instruction in the set (see example 4).
|
|
|
|
|
*
|
|
|
|
|
* Non-reorderable intrinsics are ignored with the exception of certain
|
|
|
|
|
* non-reorderable subgroups ops and intrinsics like demote and terminate that
|
|
|
|
|
* are CSE'd.
|
|
|
|
|
*
|
|
|
|
|
* Example 1. Identical instructions:
|
|
|
|
|
* %2 = iadd %0, %1
|
|
|
|
|
* control_flow {
|
|
|
|
|
* %3 = iadd %0, %1 // eliminated
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* Example 2. Commutative instructions:
|
|
|
|
|
* %3 = ffma %0, %1, %2
|
|
|
|
|
* %4 = ffma %1, %0, %2 // eliminated
|
2014-11-11 16:11:34 -08:00
|
|
|
*
|
2025-11-11 16:20:24 -05:00
|
|
|
* Example 3. Non-matching ALU flags are merged:
|
|
|
|
|
* %2 = fmul %0, %1 (fp_fast_math) // exact added here
|
|
|
|
|
* %3 = fmul %0, %1 (exact) // eliminated
|
2014-11-11 16:11:34 -08:00
|
|
|
*
|
2025-11-11 16:20:24 -05:00
|
|
|
* Example 4. Non-dominating situation:
|
|
|
|
|
* if {
|
|
|
|
|
* %2 = iadd %0, %1
|
|
|
|
|
* } else {
|
|
|
|
|
* %3 = iadd %0, %1 // keep, but replace %2 in the set
|
|
|
|
|
* %4 = iadd %0, %1 // eliminated
|
|
|
|
|
* }
|
|
|
|
|
* TODO: We could move %2 before "if" in this pass instead. It would also
|
|
|
|
|
* reduce register usage when %0 and %1 are no longer live in
|
|
|
|
|
* the range between "if" and %3, while only %2 would be live in that
|
|
|
|
|
* range.
|
2014-11-11 16:11:34 -08:00
|
|
|
*
|
2025-11-11 16:20:24 -05:00
|
|
|
* TODO - everything below is not implemented:
|
|
|
|
|
*
|
|
|
|
|
* Implementing the following cases could eliminate most of nir_opt_copy_prop:
|
|
|
|
|
*
|
|
|
|
|
* Case 1. Copy propagation of movs without swizzles:
|
|
|
|
|
* 32x4 %2 = (any instruction)
|
2025-12-11 16:33:54 -05:00
|
|
|
* 32x4 %3 = mov %2.xyzw // eliminated since it's equal to %2
|
|
|
|
|
* OR
|
|
|
|
|
* 32x4 %3 = vec4 %2.x, %2.y, %2.z, %2.w // eliminated since it's equal to %2
|
2025-11-11 16:20:24 -05:00
|
|
|
*
|
|
|
|
|
* Case 2. Copy propagation of movs with swizzles:
|
|
|
|
|
* 32x2 %2 = (any instruction)
|
2025-12-11 16:33:54 -05:00
|
|
|
* 32x3 %3 = mov %2.yxx // eliminated conditionally
|
|
|
|
|
* OR
|
|
|
|
|
* 32x3 %3 = vec3 %2.y, %2.x, %2.x // eliminated conditionally
|
2025-11-11 16:20:24 -05:00
|
|
|
* All %3 uses that are ALU will absorb the swizzle and are changed
|
2025-12-11 16:33:54 -05:00
|
|
|
* to use %2, and those uses that are not ALU will keep vecN or replace
|
|
|
|
|
* mov with equivalent vecN while eliminating components not used by
|
|
|
|
|
* the remaining uses (nir_opt_copy_prop always does that).
|
2014-11-11 16:11:34 -08:00
|
|
|
*/
|
|
|
|
|
|
2025-02-07 00:58:16 +01:00
|
|
|
#include "nir.h"
|
2025-02-24 15:09:24 -05:00
|
|
|
#include "nir_instr_set.h"
|
2014-11-11 16:11:34 -08:00
|
|
|
|
|
|
|
|
static bool
|
2020-08-17 19:56:16 +01:00
|
|
|
dominates(const nir_instr *old_instr, const nir_instr *new_instr)
|
2014-11-11 16:11:34 -08:00
|
|
|
{
|
2020-08-17 19:56:16 +01:00
|
|
|
return nir_block_dominates(old_instr->block, new_instr->block);
|
2014-11-11 16:11:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
nir_opt_cse_impl(nir_function_impl *impl)
|
|
|
|
|
{
|
2025-08-09 17:32:30 -04:00
|
|
|
struct set instr_set;
|
|
|
|
|
nir_instr_set_init(&instr_set, NULL);
|
2014-11-11 16:11:34 -08:00
|
|
|
|
2025-08-09 17:32:30 -04:00
|
|
|
_mesa_set_resize(&instr_set, impl->ssa_alloc);
|
2020-08-17 20:22:23 +01:00
|
|
|
|
2014-11-11 16:11:34 -08:00
|
|
|
nir_metadata_require(impl, nir_metadata_dominance);
|
|
|
|
|
|
2020-08-17 19:56:16 +01:00
|
|
|
bool progress = false;
|
|
|
|
|
nir_foreach_block(block, impl) {
|
2024-06-24 07:16:43 -04:00
|
|
|
nir_foreach_instr_safe(instr, block) {
|
2025-08-09 17:32:30 -04:00
|
|
|
if (nir_instr_set_add_or_rewrite(&instr_set, instr, dominates)) {
|
2024-06-24 07:16:43 -04:00
|
|
|
progress = true;
|
2024-06-24 07:20:19 -04:00
|
|
|
nir_instr_remove(instr);
|
2024-06-24 07:16:43 -04:00
|
|
|
}
|
|
|
|
|
}
|
2020-08-17 19:56:16 +01:00
|
|
|
}
|
2014-11-11 16:11:34 -08:00
|
|
|
|
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_progress(progress, impl, nir_metadata_control_flow);
|
2014-11-11 16:11:34 -08:00
|
|
|
|
2025-08-09 17:32:30 -04:00
|
|
|
nir_instr_set_fini(&instr_set);
|
2015-05-22 00:41:45 -04:00
|
|
|
return progress;
|
2014-11-11 16:11:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
nir_opt_cse(nir_shader *shader)
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
2023-06-22 13:27:59 -04:00
|
|
|
nir_foreach_function_impl(impl, shader) {
|
|
|
|
|
progress |= nir_opt_cse_impl(impl);
|
2014-11-11 16:11:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|