ir3: Readd support for translating NIR phi nodes
authorConnor Abbott <cwabbott0@gmail.com>
Wed, 10 Feb 2021 18:49:46 +0000 (19:49 +0100)
committerEmma Anholt <emma@anholt.net>
Thu, 10 Jun 2021 19:20:38 +0000 (12:20 -0700)
This is roughly based on the support removed a while ago, but it handles
sources better by associating each source with a predecessor block.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9842>

src/freedreno/ir3/ir3_compiler_nir.c
src/freedreno/ir3/ir3_sched.c
src/freedreno/ir3/ir3_validate.c

index 497761b..164f72d 100644 (file)
@@ -2749,6 +2749,60 @@ emit_tex_txs(struct ir3_context *ctx, nir_tex_instr *tex)
        ir3_put_dst(ctx, &tex->dest);
 }
 
+/* phi instructions are left partially constructed.  We don't resolve
+ * their srcs until the end of the shader, since (eg. loops) one of
+ * the phi's srcs might be defined after the phi due to back edges in
+ * the CFG.
+ */
+static void
+emit_phi(struct ir3_context *ctx, nir_phi_instr *nphi)
+{
+       struct ir3_instruction *phi, **dst;
+
+       /* NOTE: phi's should be lowered to scalar at this point */
+       compile_assert(ctx, nphi->dest.ssa.num_components == 1);
+
+       dst = ir3_get_dst(ctx, &nphi->dest, 1);
+
+       phi = ir3_instr_create(ctx->block, OPC_META_PHI,
+                       1 + exec_list_length(&nphi->srcs));
+       __ssa_dst(phi);
+       phi->phi.nphi = nphi;
+
+       dst[0] = phi;
+
+       ir3_put_dst(ctx, &nphi->dest);
+}
+
+static struct ir3_block *get_block(struct ir3_context *ctx, const nir_block *nblock);
+
+static void
+resolve_phis(struct ir3_context *ctx, struct ir3_block *block)
+{
+       foreach_instr (phi, &block->instr_list) {
+               if (phi->opc != OPC_META_PHI)
+                       break;
+
+               nir_phi_instr *nphi = phi->phi.nphi;
+
+               for (unsigned i = 0; i < block->predecessors_count; i++) {
+                       struct ir3_block *pred = block->predecessors[i];
+                       nir_foreach_phi_src(nsrc, nphi) {
+                               if (get_block(ctx, nsrc->pred) == pred) {
+                                       if (nsrc->src.ssa->parent_instr->type == nir_instr_type_ssa_undef) {
+                                               /* Create an ir3 undef */
+                                               ir3_reg_create(phi, INVALID_REG, phi->regs[0]->flags);
+                                       } else {
+                                               struct ir3_instruction *src = ir3_get_src(ctx, &nsrc->src)[0];
+                                               __ssa_src(phi, src, 0);
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
 static void
 emit_jump(struct ir3_context *ctx, nir_jump_instr *jump)
 {
@@ -2810,8 +2864,7 @@ emit_instr(struct ir3_context *ctx, nir_instr *instr)
                emit_jump(ctx, nir_instr_as_jump(instr));
                break;
        case nir_instr_type_phi:
-               /* we have converted phi webs to regs in NIR by now */
-               ir3_context_error(ctx, "Unexpected NIR instruction type: %d\n", instr->type);
+               emit_phi(ctx, nir_instr_as_phi(instr));
                break;
        case nir_instr_type_call:
        case nir_instr_type_parallel_copy:
@@ -3092,6 +3145,9 @@ emit_function(struct ir3_context *ctx, nir_function_impl *impl)
        }
 
        setup_predecessors(ctx->ir);
+       foreach_block (block, &ctx->ir->block_list) {
+               resolve_phis(ctx, block);
+       }
 }
 
 static void
index 6d4a76e..d496918 100644 (file)
@@ -1014,6 +1014,13 @@ is_output_only(struct ir3_instruction *instr)
 static void
 sched_node_add_deps(struct ir3_instruction *instr)
 {
+       /* There's nothing to do for phi nodes, since they always go first. And
+        * phi nodes can reference sources later in the same block, so handling
+        * sources is not only unnecessary but could cause problems.
+        */
+       if (instr->opc == OPC_META_PHI)
+               return;
+
        /* Since foreach_ssa_src() already handles false-dep's we can construct
         * the DAG easily in a single pass.
         */
@@ -1100,17 +1107,19 @@ sched_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
                        ctx->remaining_tex++;
        }
 
-       /* First schedule all meta:input instructions, followed by
-        * tex-prefetch.  We want all of the instructions that load
-        * values into registers before the shader starts to go
-        * before any other instructions.  But in particular we
-        * want inputs to come before prefetches.  This is because
-        * a FS's bary_ij input may not actually be live in the
-        * shader, but it should not be scheduled on top of any
-        * other input (but can be overwritten by a tex prefetch)
+       /* First schedule all meta:input and meta:phi instructions, followed by
+        * tex-prefetch.  We want all of the instructions that load values into
+        * registers before the shader starts to go before any other instructions.
+        * But in particular we want inputs to come before prefetches.  This is
+        * because a FS's bary_ij input may not actually be live in the shader,
+        * but it should not be scheduled on top of any other input (but can be
+        * overwritten by a tex prefetch)
+        *
+        * Note: Because the first block cannot have predecessors, meta:input and
+        * meta:phi cannot exist in the same block.
         */
        foreach_instr_safe (instr, &ctx->unscheduled_list)
-               if (instr->opc == OPC_META_INPUT)
+               if (instr->opc == OPC_META_INPUT || instr->opc == OPC_META_PHI)
                        schedule(ctx, instr);
 
        foreach_instr_safe (instr, &ctx->unscheduled_list)
index 85038ee..4220b3e 100644 (file)
@@ -72,6 +72,32 @@ validate_src(struct ir3_validate_ctx *ctx, struct ir3_register *reg)
        validate_assert(ctx, reg_class_flags(src->regs[0]) == reg_class_flags(reg));
 }
 
+/* phi sources are logically read at the end of the predecessor basic block,
+ * and we have to validate them then in order to correctly validate that the
+ * use comes after the definition for loop phis.
+ */
+static void
+validate_phi_src(struct ir3_validate_ctx *ctx, struct ir3_block *block, struct ir3_block *pred)
+{
+       unsigned pred_idx = ir3_block_get_pred_index(block, pred);
+
+       foreach_instr (phi, &block->instr_list) {
+               if (phi->opc != OPC_META_PHI)
+                       break;
+
+               ctx->current_instr = phi;
+               validate_assert(ctx, phi->regs_count == block->predecessors_count + 1);
+               validate_src(ctx, phi->regs[1 + pred_idx]);
+       }
+}
+
+static void
+validate_phi(struct ir3_validate_ctx *ctx, struct ir3_instruction *phi)
+{
+       _mesa_set_add(ctx->defs, phi);
+       validate_assert(ctx, writes_gpr(phi));
+}
+
 #define validate_reg_size(ctx, reg, type) \
        validate_assert(ctx, type_size(type) == (((reg)->flags & IR3_REG_HALF) ? 16 : 32))
 
@@ -215,9 +241,29 @@ ir3_validate(struct ir3 *ir)
        ctx->defs = _mesa_pointer_set_create(ctx);
 
        foreach_block (block, &ir->block_list) {
+               /* We require that the first block does not have any predecessors,
+                * which allows us to assume that phi nodes and meta:input's do not
+                * appear in the same basic block.
+                */
+               validate_assert(ctx,
+                               block != ir3_start_block(ir) || block->predecessors_count == 0);
+
+               struct ir3_instruction *prev = NULL;
                foreach_instr (instr, &block->instr_list) {
                        ctx->current_instr = instr;
-                       validate_instr(ctx, instr);
+                       if (instr->opc == OPC_META_PHI) {
+                               /* phis must be the first in the block */
+                               validate_assert(ctx, prev == NULL || prev->opc == OPC_META_PHI);
+                               validate_phi(ctx, instr);
+                       } else {
+                               validate_instr(ctx, instr);
+                       }
+                       prev = instr;
+               }
+
+               for (unsigned i = 0; i < 2; i++) {
+                       if (block->successors[i])
+                               validate_phi_src(ctx, block->successors[i], block);
                }
        }