nir/dce: perform DCE for unlooped instructions in a single pass
It's unnecessary to iterate twice for instructions outside loops. Compile-time (nir_opt_dce): Difference at 95.0% confidence -630.64 +/- 6.18761 -27.0751% +/- 0.223134% (Student's t, pooled s = 7.30785) Compile-time (entire run): Difference at 95.0% confidence -749.54 +/- 48.8272 -1.82644% +/- 0.117838% (Student's t, pooled s = 57.6672) Signed-off-by: Rhys Perry <pendingchaos02@gmail.com> Reviewed-by: Daniel Schürmann <daniel@schuermann.dev> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7691>
This commit is contained in:
@@ -106,14 +106,14 @@ struct loop_state {
|
||||
nir_block *preheader;
|
||||
};
|
||||
|
||||
static void
|
||||
mark_block(nir_block *block, BITSET_WORD *defs_live, struct loop_state *loop)
|
||||
static bool
|
||||
dce_block(nir_block *block, BITSET_WORD *defs_live, struct loop_state *loop)
|
||||
{
|
||||
bool progress = false;
|
||||
bool phis_changed = false;
|
||||
nir_foreach_instr_reverse(instr, block) {
|
||||
if (is_live(defs_live, instr)) {
|
||||
instr->pass_flags = 1;
|
||||
|
||||
nir_foreach_instr_reverse_safe(instr, block) {
|
||||
bool live = is_live(defs_live, instr);
|
||||
if (live) {
|
||||
if (instr->type == nir_instr_type_phi) {
|
||||
nir_foreach_phi_src(src, nir_instr_as_phi(instr)) {
|
||||
phis_changed |= mark_src_live(&src->src, defs_live) &&
|
||||
@@ -122,8 +122,16 @@ mark_block(nir_block *block, BITSET_WORD *defs_live, struct loop_state *loop)
|
||||
} else {
|
||||
nir_foreach_src(instr, mark_live_cb, defs_live);
|
||||
}
|
||||
} else {
|
||||
instr->pass_flags = 0;
|
||||
}
|
||||
|
||||
/* If we're not in a loop, remove it now if it's dead. If we are in a
|
||||
* loop, leave instructions to be removed later if they're still dead.
|
||||
*/
|
||||
if (loop->preheader) {
|
||||
instr->pass_flags = live;
|
||||
} else if (!live) {
|
||||
nir_instr_remove(instr);
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,29 +140,40 @@ mark_block(nir_block *block, BITSET_WORD *defs_live, struct loop_state *loop)
|
||||
* setting header_phis_changed.
|
||||
*/
|
||||
loop->header_phis_changed = phis_changed;
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
static void
|
||||
mark_cf_list(struct exec_list *cf_list, BITSET_WORD *defs_live,
|
||||
struct loop_state *parent_loop)
|
||||
static bool
|
||||
dce_cf_list(struct exec_list *cf_list, BITSET_WORD *defs_live,
|
||||
struct loop_state *parent_loop)
|
||||
{
|
||||
bool progress = false;
|
||||
foreach_list_typed_reverse(nir_cf_node, cf_node, node, cf_list) {
|
||||
switch (cf_node->type) {
|
||||
case nir_cf_node_block: {
|
||||
nir_block *block = nir_cf_node_as_block(cf_node);
|
||||
mark_block(block, defs_live, parent_loop);
|
||||
progress |= dce_block(block, defs_live, parent_loop);
|
||||
break;
|
||||
}
|
||||
case nir_cf_node_if: {
|
||||
nir_if *nif = nir_cf_node_as_if(cf_node);
|
||||
mark_cf_list(&nif->else_list, defs_live, parent_loop);
|
||||
mark_cf_list(&nif->then_list, defs_live, parent_loop);
|
||||
progress |= dce_cf_list(&nif->else_list, defs_live, parent_loop);
|
||||
progress |= dce_cf_list(&nif->then_list, defs_live, parent_loop);
|
||||
mark_src_live(&nif->condition, defs_live);
|
||||
break;
|
||||
}
|
||||
case nir_cf_node_loop: {
|
||||
nir_loop *loop = nir_cf_node_as_loop(cf_node);
|
||||
|
||||
/* Fast path if the loop has no continues: we can remove instructions
|
||||
* as we mark the others live.
|
||||
*/
|
||||
if (nir_loop_first_block(loop)->predecessors->entries == 1) {
|
||||
progress |= dce_cf_list(&loop->body, defs_live, parent_loop);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mark instructions as live until there is no more progress. */
|
||||
struct loop_state inner_state;
|
||||
inner_state.preheader = nir_cf_node_as_block(nir_cf_node_prev(cf_node));
|
||||
@@ -163,14 +182,33 @@ mark_cf_list(struct exec_list *cf_list, BITSET_WORD *defs_live,
|
||||
/* dce_cf_list() resets inner_state.header_phis_changed itself, so
|
||||
* it doesn't have to be done here.
|
||||
*/
|
||||
mark_cf_list(&loop->body, defs_live, &inner_state);
|
||||
dce_cf_list(&loop->body, defs_live, &inner_state);
|
||||
} while (inner_state.header_phis_changed);
|
||||
|
||||
/* We don't know how many times mark_cf_list() will repeat, so
|
||||
* remove instructions separately.
|
||||
*
|
||||
* By checking parent_loop->preheader, we ensure that we only do this
|
||||
* walk for the outer-most loops so it only happens once.
|
||||
*/
|
||||
if (!parent_loop->preheader) {
|
||||
nir_foreach_block_in_cf_node(block, cf_node) {
|
||||
nir_foreach_instr_safe(instr, block) {
|
||||
if (!instr->pass_flags) {
|
||||
nir_instr_remove(instr);
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nir_cf_node_function:
|
||||
unreachable("Invalid cf type");
|
||||
}
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -183,18 +221,7 @@ nir_opt_dce_impl(nir_function_impl *impl)
|
||||
|
||||
struct loop_state loop;
|
||||
loop.preheader = NULL;
|
||||
mark_cf_list(&impl->body, defs_live, &loop);
|
||||
|
||||
bool progress = false;
|
||||
|
||||
nir_foreach_block(block, impl) {
|
||||
nir_foreach_instr_safe(instr, block) {
|
||||
if (!instr->pass_flags) {
|
||||
nir_instr_remove(instr);
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool progress = dce_cf_list(&impl->body, defs_live, &loop);
|
||||
|
||||
ralloc_free(defs_live);
|
||||
|
||||
|
Reference in New Issue
Block a user