ir3: make RA aware of repeat groups

Create merge sets for the sources and defs of the instructions in repeat
groups. This way, RA will try to allocate consecutive registers for
them. This will not be forced though because we prefer to split-up
repeat groups over creating movs to reorder registers.

When choosing a register for a repeat group's merge set, if its merge
set is unique (i.e., only used for these repeated instructions), try to
first allocate one of their sources (for the same reason as for ALU/SFU
instructions). This also prevents us from allocating a new register
range for this merge set when the one from a source could be reused.

Signed-off-by: Job Noorman <jnoorman@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28341>
This commit is contained in:
Job Noorman
2024-08-15 08:46:35 +02:00
committed by Marge Bot
parent a5b03fc316
commit 4fcee235a6
2 changed files with 103 additions and 13 deletions
+39
View File
@@ -401,6 +401,39 @@ aggressive_coalesce_collect(struct ir3_liveness *live,
}
}
static void
aggressive_coalesce_rpt(struct ir3_liveness *live,
struct ir3_instruction *instr)
{
if (!ir3_instr_is_first_rpt(instr))
return;
struct ir3_register *def = instr->dsts[0];
unsigned def_offset = 0;
unsigned src_offsets[instr->srcs_count];
memset(src_offsets, 0, sizeof(unsigned) * instr->srcs_count);
foreach_instr_rpt_excl (rpt, instr) {
if (!(rpt->dsts[0]->flags & IR3_REG_SSA))
continue;
def_offset += reg_elem_size(def);
try_merge_defs(live, def, rpt->dsts[0], def_offset);
foreach_src_n (src, src_n, instr) {
struct ir3_register *rpt_src = rpt->srcs[src_n];
if (!(src->flags & IR3_REG_SSA) || !(rpt_src->flags & IR3_REG_SSA))
continue;
if (src->def == rpt_src->def)
continue;
src_offsets[src_n] += reg_elem_size(src->def);
try_merge_defs(live, src->def, rpt_src->def, src_offsets[src_n]);
}
}
}
static void
create_parallel_copy(struct ir3_block *block)
{
@@ -594,6 +627,12 @@ ir3_merge_regs(struct ir3_liveness *live, struct ir3 *ir)
}
}
foreach_block (block, &ir->block_list) {
foreach_instr (instr, &block->instr_list) {
aggressive_coalesce_rpt(live, instr);
}
}
index_merge_sets(live, ir);
if (ir3_shader_debug & IR3_DBG_RAMSGS)
+64 -13
View File
@@ -1370,6 +1370,55 @@ find_best_gap(struct ra_ctx *ctx, struct ra_file *file,
return (physreg_t)~0;
}
static physreg_t
try_allocate_src(struct ra_ctx *ctx, struct ra_file *file,
struct ir3_register *reg)
{
unsigned file_size = reg_file_size(file, reg);
unsigned size = reg_size(reg);
for (unsigned i = 0; i < reg->instr->srcs_count; i++) {
struct ir3_register *src = reg->instr->srcs[i];
if (!ra_reg_is_src(src))
continue;
if (ra_get_file(ctx, src) == file && reg_size(src) >= size) {
struct ra_interval *src_interval = &ctx->intervals[src->def->name];
physreg_t src_physreg = ra_interval_get_physreg(src_interval);
if (src_physreg % reg_elem_size(reg) == 0 &&
src_physreg + size <= file_size &&
get_reg_specified(ctx, file, reg, src_physreg, false))
return src_physreg;
}
}
return ~0;
}
static bool
rpt_has_unique_merge_set(struct ir3_instruction *instr)
{
assert(ir3_instr_is_rpt(instr));
if (!instr->dsts[0]->merge_set)
return false;
struct ir3_instruction *first = ir3_instr_first_rpt(instr);
struct ir3_register *def = first->dsts[0];
if (def->merge_set != instr->dsts[0]->merge_set ||
def->merge_set->regs_count != ir3_instr_rpt_length(first)) {
return false;
}
unsigned i = 0;
foreach_instr_rpt (rpt, first) {
if (rpt->dsts[0] != def->merge_set->regs[i++])
return false;
}
return true;
}
/* This is the main entrypoint for picking a register. Pick a free register
* for "reg", shuffling around sources if necessary. In the normal case where
* "is_source" is false, this register can overlap with killed sources
@@ -1392,6 +1441,18 @@ get_reg(struct ra_ctx *ctx, struct ra_file *file, struct ir3_register *reg)
return preferred_reg;
}
/* For repeated instructions whose merge set is unique (i.e., only used for
* these repeated instructions), try to first allocate one of their sources
* (for the same reason as for ALU/SFU instructions explained below). This
* also prevents us from allocating a new register range for this merge set
* when the one from a source could be reused.
*/
if (ir3_instr_is_rpt(reg->instr) && rpt_has_unique_merge_set(reg->instr)) {
physreg_t src_reg = try_allocate_src(ctx, file, reg);
if (src_reg != (physreg_t)~0)
return src_reg;
}
/* If this register is a subset of a merge set which we have not picked a
* register for, first try to allocate enough space for the entire merge
* set.
@@ -1414,19 +1475,9 @@ get_reg(struct ra_ctx *ctx, struct ra_file *file, struct ir3_register *reg)
* SFU instructions:
*/
if (is_sfu(reg->instr) || is_alu(reg->instr)) {
for (unsigned i = 0; i < reg->instr->srcs_count; i++) {
struct ir3_register *src = reg->instr->srcs[i];
if (!ra_reg_is_src(src))
continue;
if (ra_get_file(ctx, src) == file && reg_size(src) >= size) {
struct ra_interval *src_interval = &ctx->intervals[src->def->name];
physreg_t src_physreg = ra_interval_get_physreg(src_interval);
if (src_physreg % reg_elem_size(reg) == 0 &&
src_physreg + size <= file_size &&
get_reg_specified(ctx, file, reg, src_physreg, false))
return src_physreg;
}
}
physreg_t src_reg = try_allocate_src(ctx, file, reg);
if (src_reg != (physreg_t)~0)
return src_reg;
}
physreg_t best_reg =