lima: ppir: fix regalloc bugs

Currently regalloc doesn't mark write destinations in the single
instructions as conflicting, as a result regalloc may assign the same
register to a multiple write destinations.

Before we started scheduling multiple root nodes into a single instruction
it was pretty much hidden. Fix it by marking destination registers as
conflicting if instruction has multiple writes.

Also stop handling a special case for output registers in regalloc and just
mark them as live in the last instruction of "stop" block(s)

Reviewed-by: Erico Nunes <nunes.erico@gmail.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33636>
This commit is contained in:
Vasily Khoruzhick 2025-02-18 22:10:23 -08:00 committed by Marge Bot
parent c58655b999
commit 8905ee3a03
2 changed files with 47 additions and 11 deletions

View file

@ -123,6 +123,7 @@ ppir_liveness_instr_srcs(ppir_compiler *comp, ppir_instr *instr)
static void
ppir_liveness_instr_dest(ppir_compiler *comp, ppir_instr *instr, ppir_instr *last)
{
int dest_count = 0;
for (int i = PPIR_INSTR_SLOT_NUM-1; i >= 0; i--) {
ppir_node *node = instr->slots[i];
if (!node)
@ -143,21 +144,14 @@ ppir_liveness_instr_dest(ppir_compiler *comp, ppir_instr *instr, ppir_instr *las
if (!reg || reg->undef)
continue;
dest_count++;
unsigned int index = reg->regalloc_index;
bool live = BITSET_TEST(instr->live_set, index);
/* If it's an out reg, it's alive till the end of the block, so add it
* to live_set of the last instruction */
if (!live && reg->out_reg && (instr != last)) {
BITSET_SET(last->live_set, index);
BITSET_CLEAR(instr->live_set, index);
continue;
}
/* If a register is written but wasn't read in a later instruction, it is
* either an output register in last instruction, dead code or a bug.
* For now, assign an interference to it to ensure it doesn't get assigned
* a live register and overwrites it. */
* likely consumed in the current instruction, dead code (which should have
* been eliminated by DCE pass) or a bug. Assign an interference to it to
* ensure it doesn't get assigned a live register and overwrites it. */
if (!live) {
BITSET_SET(instr->live_internal, index);
continue;
@ -182,6 +176,35 @@ ppir_liveness_instr_dest(ppir_compiler *comp, ppir_instr *instr, ppir_instr *las
}
}
}
if (dest_count < 2)
return;
/* If we have more than one write, mark all write dests as conflicting,
* otherwise regalloc may assign the same register to them
*/
for (int i = PPIR_INSTR_SLOT_NUM-1; i >= 0; i--) {
ppir_node *node = instr->slots[i];
if (!node)
continue;
switch(node->op) {
case ppir_op_const:
case ppir_op_undef:
continue;
default:
break;
}
ppir_dest *dest = ppir_node_get_dest(node);
if (!dest || dest->type == ppir_target_pipeline)
continue;
ppir_reg *reg = ppir_dest_get_reg(dest);
if (!reg || reg->undef)
continue;
unsigned int index = reg->regalloc_index;
BITSET_SET(instr->live_internal, index);
}
}
/* Main loop, iterate blocks/instructions/ops backwards, propagate

View file

@ -500,6 +500,8 @@ static void ppir_regalloc_reset_liveness_info(ppir_compiler *comp)
}
list_for_each_entry(ppir_block, block, &comp->block_list, list) {
if (list_is_empty(&block->instr_list))
continue;
list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
if (instr->live_mask)
@ -515,6 +517,17 @@ static void ppir_regalloc_reset_liveness_info(ppir_compiler *comp)
ralloc_free(instr->live_internal);
instr->live_internal = rzalloc_array(comp, BITSET_WORD, comp->reg_num);
}
/* Mark out regs as live for the last instruction of stop block */
if (block->stop) {
ppir_instr *last = list_last_entry(&block->instr_list, ppir_instr, list);
list_for_each_entry(ppir_reg, reg, &comp->reg_list, list) {
if (reg->out_reg) {
BITSET_SET(last->live_set, reg->regalloc_index);
set_reg_mask(last->live_mask, reg->regalloc_index, 0xf);
}
}
}
}
}