Files
mesa/src/compiler/nir/nir_lower_terminate_to_demote.c
T
Faith Ekstrand b593de2c49 nir: Delete the rest of the CF list when adding a halt
In nir_lower_terminate_to_demote(), we were deleting the rest of the
block contents when we added a halt instruction but left any subsequent
CF nodes in the list.  While this may be technically okay, that much
dead code makes the rest of NIR pretty grumpy.  It's better to delete
everything to the end of the CF list, not just everything to the end of
the block.

Fixes: 75861c64b8 ("nir: Add a lower_terminate_to_demote pass")
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28456>
2024-03-29 23:08:50 +00:00

118 lines
3.4 KiB
C

/*
* Copyright © 2024 Collabora, Ltd.
* SPDX-License-Identifier: MIT
*/
#include "nir.h"
#include "nir_builder.h"
static bool
nir_lower_terminate_cf_list(nir_builder *b, struct exec_list *cf_list)
{
bool progress = false;
foreach_list_typed_safe(nir_cf_node, node, node, cf_list) {
switch (node->type) {
case nir_cf_node_block: {
nir_block *block = nir_cf_node_as_block(node);
nir_foreach_instr_safe(instr, block) {
if (instr->type != nir_instr_type_intrinsic)
continue;
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
switch (intrin->intrinsic) {
case nir_intrinsic_terminate: {
/* Everything after the terminate is dead */
nir_cf_list dead_cf;
nir_cf_extract(&dead_cf, nir_after_instr(&intrin->instr),
nir_after_cf_list(cf_list));
nir_cf_delete(&dead_cf);
intrin->intrinsic = nir_intrinsic_demote;
b->cursor = nir_after_instr(&intrin->instr);
nir_jump(b, nir_jump_halt);
/* We just removed the remainder of this list of CF nodes.
* It's not safe to continue iterating.
*/
return true;
}
case nir_intrinsic_terminate_if:
b->cursor = nir_before_instr(&intrin->instr);
nir_push_if(b, intrin->src[0].ssa);
{
nir_demote(b);
nir_jump(b, nir_jump_halt);
}
nir_instr_remove(&intrin->instr);
progress = true;
break;
default:
break;
}
}
break;
}
case nir_cf_node_if: {
nir_if *nif = nir_cf_node_as_if(node);
progress |= nir_lower_terminate_cf_list(b, &nif->then_list);
progress |= nir_lower_terminate_cf_list(b, &nif->else_list);
break;
}
case nir_cf_node_loop: {
nir_loop *loop = nir_cf_node_as_loop(node);
progress |= nir_lower_terminate_cf_list(b, &loop->body);
progress |= nir_lower_terminate_cf_list(b, &loop->continue_list);
break;
}
default:
unreachable("Unknown CF node type");
}
}
return progress;
}
static bool
nir_lower_terminate_impl(nir_function_impl *impl)
{
nir_builder b = nir_builder_create(impl);
bool progress = nir_lower_terminate_cf_list(&b, &impl->body);
if (progress) {
nir_metadata_preserve(impl, nir_metadata_none);
} else {
nir_metadata_preserve(impl, nir_metadata_all);
}
return progress;
}
/** Lowers nir_intrinsic_terminate to demote + halt
*
* The semantics of nir_intrinsic_terminate require that threads immediately
* exit. In SPIR-V, terminate is branch instruction even though it's only an
* intrinsic in NIR. This pass lowers terminate to demote + halt. Since halt
* is a jump instruction in NIR, this restores those semantics and NIR can
* reason about dead threads after a halt. It allows lets back-ends to only
* implement nir_intrinsic_demote as long as they also implement nir_jump_halt.
*/
bool
nir_lower_terminate_to_demote(nir_shader *nir)
{
bool progress = false;
nir_foreach_function_impl(impl, nir) {
if (nir_lower_terminate_impl(impl))
progress = true;
}
return progress;
}