freedreno/ir3/sched: avoid getting stuck on addr conflicts
authorRob Clark <robclark@freedesktop.org>
Wed, 8 Apr 2015 15:04:37 +0000 (11:04 -0400)
committerRob Clark <robclark@freedesktop.org>
Sat, 11 Apr 2015 15:40:15 +0000 (11:40 -0400)
When we get in a scenario where we cannot schedule any more instructions
due to address register conflict, clone the instruction that writes the
address register, and switch the remaining unscheduled users for the
current address register over to the new clone.

This is simpler and more robust than the previous attempt (which tried
and sometimes failed to ensure all other dependencies of users of the
address register were scheduled first).. hint it would try to schedule
instructions that were not actually needed for any output value.

We probably need to do the same with predicate register, although so far
it isn't so heavily used so we aren't running into problems with it
(yet).

Signed-off-by: Rob Clark <robclark@freedesktop.org>
src/gallium/drivers/freedreno/ir3/ir3_sched.c

index 653f679fe1ef9f2ff98a9f95c76695fb1c6af5ee..a790cba129be06a0c32fba5b8ba1b102446dcc3a 100644 (file)
@@ -267,36 +267,6 @@ 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) || (src->address == 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:
@@ -390,8 +360,48 @@ static int block_sched_undelayed(struct ir3_sched_ctx *ctx,
        /* detect if we've gotten ourselves into an impossible situation
         * and bail if needed
         */
-       if (all_delayed && (attempted > 0))
-               ctx->error = true;
+       if (all_delayed && (attempted > 0)) {
+               if (pred_in_use) {
+                       /* TODO we probably need to keep a list of instructions
+                        * that reference predicate, similar to indirects
+                        */
+                       ctx->error = true;
+                       return DELAYED;
+               }
+               if (addr_in_use) {
+                       struct ir3 *ir = ctx->addr->block->shader;
+                       struct ir3_instruction *new_addr =
+                                       ir3_instr_clone(ctx->addr);
+                       unsigned i;
+
+                       /* original addr is scheduled, but new one isn't: */
+                       new_addr->flags &= ~IR3_INSTR_MARK;
+
+                       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)
+                                       continue;
+
+                               /* remap remaining instructions using current addr
+                                * to new addr:
+                                */
+                               if (indirect->address == ctx->addr)
+                                       indirect->address = new_addr;
+                       }
+
+                       /* all remaining indirects remapped to new addr: */
+                       ctx->addr = NULL;
+
+                       /* not really, but this will trigger us to go back to
+                        * main trysched() loop now that we've resolved the
+                        * conflict by duplicating the instr that writes to
+                        * the address register.
+                        */
+                       return SCHEDULED;
+               }
+       }
 
        return cnt;
 }