ir3: make shpe a terminator

shpe is a bit of a special instruction: it's not really a terminator
(i.e., it does not perform a jump) but it does have to stay at the end
of its block. Up to now, we tried to enforce this by creating const
write barriers on shpe; the assumption being that everything that
happens in the preamble ends in a write to the const file so shpe stays
at the end. Alas, it turns out this is not true: things like sampler
prefetches do not write the const file and nothing was preventing those
from being scheduled after shpe.

Instead of trying to create even more barrier dependencies, fix this by
making shpe a terminator. Both sched and postsched treat terminators
specially to make sure they always stay at the end of their block.

Signed-off-by: Job Noorman <jnoorman@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34290>
This commit is contained in:
Job Noorman 2025-03-31 11:50:34 +02:00 committed by Marge Bot
parent f5019ee0d4
commit dd1ba74777
4 changed files with 13 additions and 8 deletions

View file

@ -604,7 +604,7 @@ ir3_find_shpe(struct ir3 *ir)
}
foreach_block (block, &ir->block_list) {
struct ir3_instruction *last = ir3_block_get_last_non_terminator(block);
struct ir3_instruction *last = ir3_block_get_terminator(block);
if (last && last->opc == OPC_SHPE) {
return last;
@ -666,9 +666,6 @@ ir3_create_empty_preamble(struct ir3 *ir)
b.cursor = ir3_after_block(body_block);
struct ir3_instruction *shpe = ir3_SHPE(&b);
shpe->barrier_class = shpe->barrier_conflict = IR3_BARRIER_CONST_W;
array_insert(body_block, body_block->keeps, shpe);
ir3_JUMP(&b);
body_block->successors[0] = main_start_block;
ir3_block_add_predecessor(main_start_block, body_block);
ir3_block_link_physical(body_block, main_start_block);

View file

@ -1009,6 +1009,7 @@ is_terminator(struct ir3_instruction *instr)
case OPC_BRAA:
case OPC_BRAO:
case OPC_SHPS:
case OPC_SHPE:
case OPC_GETONE:
case OPC_GETLAST:
case OPC_PREDT:

View file

@ -3363,9 +3363,7 @@ emit_intrinsic(struct ir3_context *ctx, nir_intrinsic_instr *intr)
break;
case nir_intrinsic_preamble_end_ir3: {
struct ir3_instruction *instr = ir3_SHPE(b);
instr->barrier_class = instr->barrier_conflict = IR3_BARRIER_CONST_W;
array_insert(ctx->block, ctx->block->keeps, instr);
ir3_SHPE(b);
break;
}
case nir_intrinsic_store_const_ir3: {

View file

@ -1230,7 +1230,16 @@ block_sched(struct ir3 *ir)
*/
assert(terminator);
assert(terminator->opc == OPC_JUMP || terminator->opc == OPC_PREDT ||
terminator->opc == OPC_PREDF);
terminator->opc == OPC_PREDF || terminator->opc == OPC_SHPE);
/* shpe is not a branch (we just treat it as a terminator to make sure
* it stays at the end of its block) so add one now.
*/
if (terminator->opc == OPC_SHPE) {
struct ir3_builder b = ir3_builder_at(ir3_after_instr(terminator));
terminator = ir3_JUMP(&b);
}
terminator->cat0.target = block->successors[0];
}
}