diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index 250d47f79b4..4238cec4868 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -3477,6 +3477,26 @@ nir_shader_get_entrypoint(nir_shader *shader) return func->impl; } +typedef struct nir_liveness_bounds { + uint32_t start; + uint32_t end; +} nir_liveness_bounds; + +typedef struct nir_instr_liveness { + /** + * nir_instr->index for the start and end of a single live interval for SSA + * defs. ssa values last used by a nir_if condition will have an interval + * ending at the first instruction after the last one before the if + * condition. + * + * Indexed by def->index (impl->ssa_alloc elements). + */ + struct nir_liveness_bounds *defs; +} nir_instr_liveness; + +nir_instr_liveness * +nir_live_ssa_defs_per_instr(nir_function_impl *impl); + nir_shader *nir_shader_create(void *mem_ctx, gl_shader_stage stage, const nir_shader_compiler_options *options, diff --git a/src/compiler/nir/nir_liveness.c b/src/compiler/nir/nir_liveness.c index accb418e6e4..8ed6d844ba1 100644 --- a/src/compiler/nir/nir_liveness.c +++ b/src/compiler/nir/nir_liveness.c @@ -286,3 +286,78 @@ nir_ssa_defs_interfere(nir_ssa_def *a, nir_ssa_def *b) return nir_ssa_def_is_live_at(b, a->parent_instr); } } + +/* Takes an SSA def's defs and uses and expands the live interval to cover + * that range. Control flow effects are handled separately. + */ +static bool def_cb(nir_ssa_def *def, void *state) +{ + nir_instr_liveness *liveness = state; + nir_instr *instr = def->parent_instr; + int index = def->index; + + liveness->defs[index].start = MIN2(liveness->defs[index].start, instr->index); + + nir_foreach_use(src, def) { + liveness->defs[index].end = MAX2(liveness->defs[index].end, + src->parent_instr->index); + } + + return true; +} + +nir_instr_liveness * +nir_live_ssa_defs_per_instr(nir_function_impl *impl) +{ + /* We'll use block-level live_ssa_defs to expand our per-instr ranges for + * control flow. + */ + nir_metadata_require(impl, + nir_metadata_block_index | + nir_metadata_instr_index | + nir_metadata_live_ssa_defs); + + /* Make our struct. */ + nir_instr_liveness *liveness = ralloc(NULL, nir_instr_liveness); + liveness->defs = rzalloc_array(liveness, nir_liveness_bounds, + impl->ssa_alloc); + + /* Set our starts so we can use MIN2() as we accumulate bounds. */ + for (int i = 0; i < impl->ssa_alloc; i++) + liveness->defs->start = ~0; + + unsigned last_instr = 0; + nir_foreach_block(block, impl) { + unsigned index; + BITSET_FOREACH_SET(index, block->live_in, impl->ssa_alloc) { + liveness->defs[index].start = MIN2(liveness->defs[index].start, + last_instr); + } + + nir_foreach_instr(instr, block) { + nir_foreach_ssa_def(instr, def_cb, liveness); + + last_instr = instr->index; + }; + + /* track an if src's use. We need to make sure that our value is live + * across the if reference, where we don't have an instr->index + * representing the use. Mark it as live through the next real + * instruction. + */ + nir_if *nif = nir_block_get_following_if(block); + if (nif) { + if (nif->condition.is_ssa) { + liveness->defs[nif->condition.ssa->index].end = MAX2( + liveness->defs[nif->condition.ssa->index].end, + last_instr + 1); + } + } + + BITSET_FOREACH_SET(index, block->live_out, impl->ssa_alloc) { + liveness->defs[index].end = MAX2(liveness->defs[index].end, last_instr); + } + } + + return liveness; +}