diff --git a/src/gallium/drivers/freedreno/ir3/ir3.h b/src/gallium/drivers/freedreno/ir3/ir3.h index fa866a29850..14369ce772c 100644 --- a/src/gallium/drivers/freedreno/ir3/ir3.h +++ b/src/gallium/drivers/freedreno/ir3/ir3.h @@ -321,6 +321,19 @@ struct ir3 { unsigned baryfs_count, baryfs_sz; struct ir3_instruction **baryfs; + /* Track all indirect instructions (read and write). To avoid + * deadlock scenario where an address register gets scheduled, + * but other dependent src instructions cannot be scheduled due + * to dependency on a *different* address register value, the + * scheduler needs to ensure that all dependencies other than + * the instruction other than the address register are scheduled + * before the one that writes the address register. Having a + * convenient list of instructions that reference some address + * register simplifies this. + */ + unsigned indirects_count, indirects_sz; + struct ir3_instruction **indirects; + struct ir3_block *block; unsigned heap_idx; struct ir3_heap_chunk *chunk; diff --git a/src/gallium/drivers/freedreno/ir3/ir3_compiler.c b/src/gallium/drivers/freedreno/ir3/ir3_compiler.c index 8ffa37cf34f..f6bdc06130e 100644 --- a/src/gallium/drivers/freedreno/ir3/ir3_compiler.c +++ b/src/gallium/drivers/freedreno/ir3/ir3_compiler.c @@ -771,6 +771,7 @@ add_dst_reg_wrmask(struct ir3_compile_context *ctx, compile_assert(ctx, ctx->block->address == instr->address); instr->address = ctx->block->address; + array_insert(ctx->ir->indirects, instr); } reg = ir3_reg_create(instr, regid(num, chan), flags); @@ -901,6 +902,7 @@ add_src_reg_wrmask(struct ir3_compile_context *ctx, compile_assert(ctx, ctx->block->address == instr->address); instr->address = ctx->block->address; + array_insert(ctx->ir->indirects, instr); } reg = ir3_reg_create(instr, regid(num, chan), flags); diff --git a/src/gallium/drivers/freedreno/ir3/ir3_sched.c b/src/gallium/drivers/freedreno/ir3/ir3_sched.c index 7938c1f9bea..c1921d04e4e 100644 --- a/src/gallium/drivers/freedreno/ir3/ir3_sched.c +++ b/src/gallium/drivers/freedreno/ir3/ir3_sched.c @@ -262,6 +262,36 @@ static int trysched(struct ir3_sched_ctx *ctx, } } + /* if instruction writes address register, we need to ensure + * that the instructions which use the address register value + * have all their other dependencies scheduled. + * TODO we may possibly need to do the same thing with predicate + * register usage, but for now we get by without since the + * predicate usage patterns are more simple + */ + if (writes_addr(instr)) { + struct ir3 *ir = instr->block->shader; + unsigned i; + + for (i = 0; i < ir->indirects_count; i++) { + struct ir3_instruction *indirect = ir->indirects[i]; + if (indirect->depth == DEPTH_UNUSED) + continue; + if (indirect->address != instr) + continue; + /* NOTE: avoid recursively scheduling the dependency + * on ourself (ie. avoid infinite recursion): + */ + foreach_ssa_src(src, indirect) { + if (src == instr) + continue; + delay = trysched(ctx, src); + if (delay) + return delay; + } + } + } + /* if this is a write to address/predicate register, and that * register is currently in use, we need to defer until it is * free: