r300: don't allocate fs registers when translating from NIR

So we can get the ssa-like form right away and we don't have to generate
it again with the register rename pass later.

Signed-off-by: Pavel Ondračka <pavel.ondracka@gmail.com>
Reviewed-by: Filip Gawin <None>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33066>
This commit is contained in:
Pavel Ondračka
2024-12-10 08:49:24 +01:00
committed by Marge Bot
parent 0e9b90dcf4
commit c45f02461c
+1 -261
View File
@@ -226,263 +226,6 @@ struct ntr_live_reg_state {
nir_block_worklist worklist;
};
static void
ntr_live_reg_mark_use(struct ntr_compile *c, struct ntr_live_reg_block_state *bs, int ip,
unsigned index, unsigned used_mask)
{
bs->use[index] |= used_mask & ~bs->def[index];
c->liveness[index].start = MIN2(c->liveness[index].start, ip);
c->liveness[index].end = MAX2(c->liveness[index].end, ip);
}
static void
ntr_live_reg_setup_def_use(struct ntr_compile *c, nir_function_impl *impl,
struct ntr_live_reg_state *state)
{
for (int i = 0; i < impl->num_blocks; i++) {
state->blocks[i].def = rzalloc_array(state->blocks, uint8_t, c->num_temps);
state->blocks[i].defin = rzalloc_array(state->blocks, uint8_t, c->num_temps);
state->blocks[i].defout = rzalloc_array(state->blocks, uint8_t, c->num_temps);
state->blocks[i].use = rzalloc_array(state->blocks, uint8_t, c->num_temps);
state->blocks[i].livein = rzalloc_array(state->blocks, uint8_t, c->num_temps);
state->blocks[i].liveout = rzalloc_array(state->blocks, uint8_t, c->num_temps);
}
int ip = 0;
nir_foreach_block (block, impl) {
struct ntr_live_reg_block_state *bs = &state->blocks[block->index];
struct ntr_block *ntr_block = ntr_block_from_nir(c, block);
ntr_block->start_ip = ip;
util_dynarray_foreach (&ntr_block->insns, struct ntr_insn, insn) {
const struct tgsi_opcode_info *opcode_info = tgsi_get_opcode_info(insn->opcode);
/* Set up use[] for the srcs.
*
* Uses are the channels of the reg read in the block that don't have a
* preceding def to screen them off. Note that we don't do per-element
* tracking of array regs, so they're never screened off.
*/
for (int i = 0; i < opcode_info->num_src; i++) {
if (insn->src[i].File != TGSI_FILE_TEMPORARY)
continue;
int index = insn->src[i].Index;
uint32_t used_mask = tgsi_util_get_src_usage_mask(
insn->opcode, i, insn->dst->WriteMask, insn->src[i].SwizzleX, insn->src[i].SwizzleY,
insn->src[i].SwizzleZ, insn->src[i].SwizzleW, insn->tex_target, insn->tex_target);
assert(!insn->src[i].Indirect || index < c->first_non_array_temp);
ntr_live_reg_mark_use(c, bs, ip, index, used_mask);
}
if (insn->is_tex) {
for (int i = 0; i < ARRAY_SIZE(insn->tex_offset); i++) {
if (insn->tex_offset[i].File == TGSI_FILE_TEMPORARY)
ntr_live_reg_mark_use(c, bs, ip, insn->tex_offset[i].Index, 0xf);
}
}
/* Set up def[] for the srcs.
*
* Defs are the unconditionally-written (not R/M/W) channels of the reg in
* the block that don't have a preceding use.
*/
for (int i = 0; i < opcode_info->num_dst; i++) {
if (insn->dst[i].File != TGSI_FILE_TEMPORARY)
continue;
int index = insn->dst[i].Index;
uint32_t writemask = insn->dst[i].WriteMask;
bs->def[index] |= writemask & ~bs->use[index];
bs->defout[index] |= writemask;
assert(!insn->dst[i].Indirect || index < c->first_non_array_temp);
c->liveness[index].start = MIN2(c->liveness[index].start, ip);
c->liveness[index].end = MAX2(c->liveness[index].end, ip);
}
ip++;
}
ntr_block->end_ip = ip;
}
}
static void
ntr_live_regs(struct ntr_compile *c, nir_function_impl *impl)
{
nir_metadata_require(impl, nir_metadata_block_index);
c->liveness = rzalloc_array(c, struct ntr_reg_interval, c->num_temps);
struct ntr_live_reg_state state = {
.blocks = rzalloc_array(impl, struct ntr_live_reg_block_state, impl->num_blocks),
};
/* The intervals start out with start > end (indicating unused) */
for (int i = 0; i < c->num_temps; i++)
c->liveness[i].start = ~0;
ntr_live_reg_setup_def_use(c, impl, &state);
/* Make a forward-order worklist of all the blocks. */
nir_block_worklist_init(&state.worklist, impl->num_blocks, NULL);
nir_foreach_block (block, impl) {
nir_block_worklist_push_tail(&state.worklist, block);
}
/* Propagate defin/defout down the CFG to calculate the live variables
* potentially defined along any possible control flow path. We'll use this
* to keep things like conditional defs of the reg (or array regs where we
* don't track defs!) from making the reg's live range extend back to the
* start of the program.
*/
while (!nir_block_worklist_is_empty(&state.worklist)) {
nir_block *block = nir_block_worklist_pop_head(&state.worklist);
for (int j = 0; j < ARRAY_SIZE(block->successors); j++) {
nir_block *succ = block->successors[j];
if (!succ || succ->index == impl->num_blocks)
continue;
for (int i = 0; i < c->num_temps; i++) {
uint8_t new_def =
state.blocks[block->index].defout[i] & ~state.blocks[succ->index].defin[i];
if (new_def) {
state.blocks[succ->index].defin[i] |= new_def;
state.blocks[succ->index].defout[i] |= new_def;
nir_block_worklist_push_tail(&state.worklist, succ);
}
}
}
}
/* Make a reverse-order worklist of all the blocks. */
nir_foreach_block (block, impl) {
nir_block_worklist_push_head(&state.worklist, block);
}
/* We're now ready to work through the worklist and update the liveness sets
* of each of the blocks. As long as we keep the worklist up-to-date as we
* go, everything will get covered.
*/
while (!nir_block_worklist_is_empty(&state.worklist)) {
/* We pop them off in the reverse order we pushed them on. This way
* the first walk of the instructions is backwards so we only walk
* once in the case of no control flow.
*/
nir_block *block = nir_block_worklist_pop_head(&state.worklist);
struct ntr_block *ntr_block = ntr_block_from_nir(c, block);
struct ntr_live_reg_block_state *bs = &state.blocks[block->index];
for (int i = 0; i < c->num_temps; i++) {
/* Collect livein from our successors to include in our liveout. */
for (int j = 0; j < ARRAY_SIZE(block->successors); j++) {
nir_block *succ = block->successors[j];
if (!succ || succ->index == impl->num_blocks)
continue;
struct ntr_live_reg_block_state *sbs = &state.blocks[succ->index];
uint8_t new_liveout = sbs->livein[i] & ~bs->liveout[i];
if (new_liveout) {
if (state.blocks[block->index].defout[i])
c->liveness[i].end = MAX2(c->liveness[i].end, ntr_block->end_ip);
bs->liveout[i] |= sbs->livein[i];
}
}
/* Propagate use requests from either our block's uses or our
* non-screened-off liveout up to our predecessors.
*/
uint8_t new_livein = ((bs->use[i] | (bs->liveout[i] & ~bs->def[i])) & ~bs->livein[i]);
if (new_livein) {
bs->livein[i] |= new_livein;
set_foreach (block->predecessors, entry) {
nir_block *pred = (void *)entry->key;
nir_block_worklist_push_tail(&state.worklist, pred);
}
if (new_livein & state.blocks[block->index].defin[i])
c->liveness[i].start = MIN2(c->liveness[i].start, ntr_block->start_ip);
}
}
}
ralloc_free(state.blocks);
nir_block_worklist_fini(&state.worklist);
}
static void
ntr_ra_check(struct ntr_compile *c, unsigned *ra_map, BITSET_WORD *released, int ip, unsigned index)
{
if (index < c->first_non_array_temp)
return;
if (c->liveness[index].start == ip && ra_map[index] == ~0)
ra_map[index] = ureg_DECL_temporary(c->ureg).Index;
if (c->liveness[index].end == ip && !BITSET_TEST(released, index)) {
ureg_release_temporary(c->ureg, ureg_dst_register(TGSI_FILE_TEMPORARY, ra_map[index]));
BITSET_SET(released, index);
}
}
static void
ntr_allocate_regs(struct ntr_compile *c, nir_function_impl *impl)
{
ntr_live_regs(c, impl);
unsigned *ra_map = ralloc_array(c, unsigned, c->num_temps);
unsigned *released = rzalloc_array(c, BITSET_WORD, BITSET_WORDS(c->num_temps));
/* No RA on NIR array regs */
for (int i = 0; i < c->first_non_array_temp; i++)
ra_map[i] = i;
for (int i = c->first_non_array_temp; i < c->num_temps; i++)
ra_map[i] = ~0;
int ip = 0;
nir_foreach_block (block, impl) {
struct ntr_block *ntr_block = ntr_block_from_nir(c, block);
for (int i = 0; i < c->num_temps; i++)
ntr_ra_check(c, ra_map, released, ip, i);
util_dynarray_foreach (&ntr_block->insns, struct ntr_insn, insn) {
const struct tgsi_opcode_info *opcode_info = tgsi_get_opcode_info(insn->opcode);
for (int i = 0; i < opcode_info->num_src; i++) {
if (insn->src[i].File == TGSI_FILE_TEMPORARY) {
ntr_ra_check(c, ra_map, released, ip, insn->src[i].Index);
insn->src[i].Index = ra_map[insn->src[i].Index];
}
}
if (insn->is_tex) {
for (int i = 0; i < ARRAY_SIZE(insn->tex_offset); i++) {
if (insn->tex_offset[i].File == TGSI_FILE_TEMPORARY) {
ntr_ra_check(c, ra_map, released, ip, insn->tex_offset[i].Index);
insn->tex_offset[i].Index = ra_map[insn->tex_offset[i].Index];
}
}
}
for (int i = 0; i < opcode_info->num_dst; i++) {
if (insn->dst[i].File == TGSI_FILE_TEMPORARY) {
ntr_ra_check(c, ra_map, released, ip, insn->dst[i].Index);
insn->dst[i].Index = ra_map[insn->dst[i].Index];
}
}
ip++;
}
for (int i = 0; i < c->num_temps; i++)
ntr_ra_check(c, ra_map, released, ip, i);
}
}
static void
ntr_allocate_regs_unoptimized(struct ntr_compile *c, nir_function_impl *impl)
{
@@ -2035,10 +1778,7 @@ ntr_emit_impl(struct ntr_compile *c, nir_function_impl *impl)
/* Emit the ntr insns */
ntr_emit_cf_list(c, &impl->body);
if (c->s->info.stage == MESA_SHADER_FRAGMENT)
ntr_allocate_regs(c, impl);
else
ntr_allocate_regs_unoptimized(c, impl);
ntr_allocate_regs_unoptimized(c, impl);
/* Turn the ntr insns into actual TGSI tokens */
ntr_emit_cf_list_ureg(c, &impl->body);