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:
committed by
Marge Bot
parent
0e9b90dcf4
commit
c45f02461c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user