Mesa (master): freedreno/ir3: sched fixes for addr register usage
Rob Clark
robclark at kemper.freedesktop.org
Fri Jul 3 12:57:13 UTC 2015
Module: Mesa
Branch: master
Commit: 2215ff2a5d5f1df5791399e1ff78b56bf06e9102
URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=2215ff2a5d5f1df5791399e1ff78b56bf06e9102
Author: Rob Clark <robclark at freedesktop.org>
Date: Thu Jul 2 14:59:08 2015 -0400
freedreno/ir3: sched fixes for addr register usage
A handful of fixes and cleanups:
1) If we split addr/pred, we need the newly created instruction to
end up in the unscheduled_list
2) Avoid scheduling a write to the address register if there is no
instruction using the address register that is otherwise ready
to schedule. Note that I currently don't bother with the same
logic for predicate register, since the only instructions using
predicate (br/kill) don't take any other src registers, so this
situation should not arise.
3) few other cosmetic cleanups
Signed-off-by: Rob Clark <robclark at freedesktop.org>
---
src/gallium/drivers/freedreno/ir3/ir3_sched.c | 77 +++++++++++++++++++++----
1 file changed, 65 insertions(+), 12 deletions(-)
diff --git a/src/gallium/drivers/freedreno/ir3/ir3_sched.c b/src/gallium/drivers/freedreno/ir3/ir3_sched.c
index 01d7230..414d14e 100644
--- a/src/gallium/drivers/freedreno/ir3/ir3_sched.c
+++ b/src/gallium/drivers/freedreno/ir3/ir3_sched.c
@@ -80,12 +80,12 @@ schedule(struct ir3_sched_ctx *ctx, struct ir3_instruction *instr)
list_delinit(&instr->node);
if (writes_addr(instr)) {
- assert(ctx->addr == NULL);
+ debug_assert(ctx->addr == NULL);
ctx->addr = instr;
}
if (writes_pred(instr)) {
- assert(ctx->pred == NULL);
+ debug_assert(ctx->pred == NULL);
ctx->pred = instr;
}
@@ -180,13 +180,13 @@ check_conflict(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
* free:
*/
if (writes_addr(instr) && ctx->addr) {
- assert(ctx->addr != instr);
+ debug_assert(ctx->addr != instr);
notes->addr_conflict = true;
return true;
}
if (writes_pred(instr) && ctx->pred) {
- assert(ctx->pred != instr);
+ debug_assert(ctx->pred != instr);
notes->pred_conflict = true;
return true;
}
@@ -261,6 +261,20 @@ instr_eligibility(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
return 0;
}
+/* could an instruction be scheduled if specified ssa src was scheduled? */
+static bool
+could_sched(struct ir3_instruction *instr, struct ir3_instruction *src)
+{
+ struct ir3_instruction *other_src;
+ foreach_ssa_src(other_src, instr) {
+ /* if dependency not scheduled, we aren't ready yet: */
+ if ((src != other_src) && !is_scheduled(other_src)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/* move eligible instructions to the priority list: */
static unsigned
add_eligible_instrs(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
@@ -272,6 +286,29 @@ add_eligible_instrs(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
int e = instr_eligibility(ctx, notes, instr);
if (e < 0)
continue;
+
+ /* For instructions that write address register we need to
+ * make sure there is at least one instruction that uses the
+ * addr value which is otherwise ready.
+ *
+ * TODO if any instructions use pred register and have other
+ * src args, we would need to do the same for writes_pred()..
+ */
+ if (unlikely(writes_addr(instr))) {
+ struct ir3 *ir = instr->block->shader;
+ bool ready = false;
+ for (unsigned i = 0; (i < ir->indirects_count) && !ready; i++) {
+ struct ir3_instruction *indirect = ir->indirects[i];
+ if (indirect->address != instr)
+ continue;
+ ready = could_sched(indirect, instr);
+ }
+
+ /* nothing could be scheduled, so keep looking: */
+ if (!ready)
+ continue;
+ }
+
min_delay = MIN2(min_delay, e);
if (e == 0) {
/* remove from unscheduled list and into priority queue: */
@@ -287,20 +324,22 @@ add_eligible_instrs(struct ir3_sched_ctx *ctx, struct ir3_sched_notes *notes,
* instructions which depend on the current address register
* to a clone of the instruction which wrote the address reg.
*/
-static void
+static struct ir3_instruction *
split_addr(struct ir3_sched_ctx *ctx)
{
- struct ir3 *ir = ctx->addr->block->shader;
+ struct ir3 *ir;
struct ir3_instruction *new_addr = NULL;
unsigned i;
debug_assert(ctx->addr);
+ ir = ctx->addr->block->shader;
+
for (i = 0; i < ir->indirects_count; i++) {
struct ir3_instruction *indirect = ir->indirects[i];
/* skip instructions already scheduled: */
- if (indirect->flags & IR3_INSTR_MARK)
+ if (is_scheduled(indirect))
continue;
/* remap remaining instructions using current addr
@@ -318,26 +357,30 @@ split_addr(struct ir3_sched_ctx *ctx)
/* all remaining indirects remapped to new addr: */
ctx->addr = NULL;
+
+ return new_addr;
}
/* "spill" the predicate register by remapping any unscheduled
* instructions which depend on the current predicate register
* to a clone of the instruction which wrote the address reg.
*/
-static void
+static struct ir3_instruction *
split_pred(struct ir3_sched_ctx *ctx)
{
- struct ir3 *ir = ctx->pred->block->shader;
+ struct ir3 *ir;
struct ir3_instruction *new_pred = NULL;
unsigned i;
debug_assert(ctx->pred);
+ ir = ctx->pred->block->shader;
+
for (i = 0; i < ir->predicates_count; i++) {
struct ir3_instruction *predicated = ir->predicates[i];
/* skip instructions already scheduled: */
- if (predicated->flags & IR3_INSTR_MARK)
+ if (is_scheduled(predicated))
continue;
/* remap remaining instructions using current pred
@@ -358,6 +401,8 @@ split_pred(struct ir3_sched_ctx *ctx)
/* all remaining predicated remapped to new pred: */
ctx->pred = NULL;
+
+ return new_pred;
}
static void
@@ -407,20 +452,28 @@ sched_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
schedule(ctx, instr);
} else if (delay == ~0) {
+ struct ir3_instruction *new_instr = NULL;
+
/* nothing available to schedule.. if we are blocked on
* address/predicate register conflict, then break the
* deadlock by cloning the instruction that wrote that
* reg:
*/
if (notes.addr_conflict) {
- split_addr(ctx);
+ new_instr = split_addr(ctx);
} else if (notes.pred_conflict) {
- split_pred(ctx);
+ new_instr = split_pred(ctx);
} else {
debug_assert(0);
ctx->error = true;
return;
}
+
+ if (new_instr) {
+ list_del(&new_instr->node);
+ list_addtail(&new_instr->node, &unscheduled_list);
+ }
+
} else {
/* and if we run out of instructions that can be scheduled,
* then it is time for nop's:
More information about the mesa-commit
mailing list