freedreno/ir3: sched fixes for addr register usage
authorRob Clark <robclark@freedesktop.org>
Thu, 2 Jul 2015 18:59:08 +0000 (14:59 -0400)
committerRob Clark <robclark@freedesktop.org>
Fri, 3 Jul 2015 12:56:09 +0000 (08:56 -0400)
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@freedesktop.org>
src/gallium/drivers/freedreno/ir3/ir3_sched.c

index 01d7230..414d14e 100644 (file)
@@ -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: