ir3: Fix infinite loop in scheduler when splitting

When we go to split e.g. a p0.x producer, the only other instructions
ready to schedule are often only p0.x producers. It could happen that
they all have a lower priority than the split instruction. Then we would
immediately schedule the split instruction again, then again try to
schedule one of the other producers, be blocked, and split it, around
and around again, leading to an infinite loop. The following commit
triggered this with
dEQP-GLES3.functional.shaders.discard.dynamic_loop_always on a3xx.

Fixes: d2f4d33 ("freedreno/ir3: new pre-RA scheduler")
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6752>
(cherry picked from commit bb3212dd4d)
This commit is contained in:
Connor Abbott 2021-06-28 18:41:41 +02:00 committed by Eric Engestrom
parent 442df9d8f4
commit 43ade99dcc
2 changed files with 20 additions and 1 deletions

View file

@ -1534,7 +1534,7 @@
"description": "ir3: Fix infinite loop in scheduler when splitting",
"nominated": true,
"nomination_type": 1,
"resolution": 0,
"resolution": 1,
"main_sha": null,
"because_sha": "d2f4d332dbb552af62fe5caabe67664d98f32229"
},

View file

@ -92,6 +92,8 @@ struct ir3_sched_ctx {
struct ir3_instruction *addr1; /* current a1.x user, if any */
struct ir3_instruction *pred; /* current p0.x user, if any */
struct ir3_instruction *split; /* most-recently-split a0/a1/p0 producer */
int remaining_kills;
int remaining_tex;
@ -336,6 +338,13 @@ check_instr(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
{
debug_assert(!is_scheduled(instr));
if (instr == ctx->split) {
/* Don't schedule instructions created by splitting a a0.x/a1.x/p0.x
* write until another "normal" instruction has been scheduled.
*/
return false;
}
if (ctx->remaining_kills && (is_tex(instr) || is_mem(instr))) {
/* avoid texture/memory access if we have unscheduled kills
* that could make the expensive operation unnecessary. By
@ -1127,6 +1136,11 @@ sched_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
}
schedule(ctx, instr);
/* Since we've scheduled a "real" instruction, we can now
* schedule any split instruction created by the scheduler again.
*/
ctx->split = NULL;
} else {
struct ir3_instruction *new_instr = NULL;
struct ir3 *ir = block->shader;
@ -1157,6 +1171,11 @@ sched_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
list_delinit(&new_instr->node);
list_addtail(&new_instr->node, &ctx->unscheduled_list);
}
/* If we produced a new instruction, do not schedule it next to
* guarantee progress.
*/
ctx->split = new_instr;
}
}