vc4: Define a QIR branch instruction
authorEric Anholt <eric@anholt.net>
Tue, 15 Mar 2016 20:55:28 +0000 (13:55 -0700)
committerEric Anholt <eric@anholt.net>
Wed, 13 Jul 2016 00:42:40 +0000 (17:42 -0700)
This uses the branch condition code in inst->cond to jump to either
successor[0] (condition matches) or successor[0] (condition doesn't
match).

src/gallium/drivers/vc4/vc4_qir.c
src/gallium/drivers/vc4/vc4_qir.h
src/gallium/drivers/vc4/vc4_qir_schedule.c
src/gallium/drivers/vc4/vc4_qpu_emit.c

index 4c81b567927e932e38efb33513cf0c952ecceb19..982e8298ae90b3c3b6cad55fb6713e5a85c0fe23 100644 (file)
@@ -83,6 +83,8 @@ static const struct qir_op_info qir_op_info[] = {
         [QOP_TEX_RESULT] = { "tex_result", 1, 0, true },
 
         [QOP_LOAD_IMM] = { "load_imm", 0, 1 },
+
+        [QOP_BRANCH] = { "branch", 0, 0, true },
 };
 
 static const char *
@@ -204,8 +206,12 @@ qir_is_tex(struct qinst *inst)
 bool
 qir_depends_on_flags(struct qinst *inst)
 {
-        return (inst->cond != QPU_COND_ALWAYS &&
-                inst->cond != QPU_COND_NEVER);
+        if (inst->op == QOP_BRANCH) {
+                return inst->cond != QPU_COND_BRANCH_ALWAYS;
+        } else {
+                return (inst->cond != QPU_COND_ALWAYS &&
+                        inst->cond != QPU_COND_NEVER);
+        }
 }
 
 bool
@@ -337,20 +343,26 @@ void
 qir_dump_inst(struct vc4_compile *c, struct qinst *inst)
 {
         fprintf(stderr, "%s", qir_get_op_name(inst->op));
-        vc4_qpu_disasm_cond(stderr, inst->cond);
+        if (inst->op == QOP_BRANCH)
+                vc4_qpu_disasm_cond_branch(stderr, inst->cond);
+        else
+                vc4_qpu_disasm_cond(stderr, inst->cond);
         if (inst->sf)
                 fprintf(stderr, ".sf");
         fprintf(stderr, " ");
 
-        qir_print_reg(c, inst->dst, true);
-        if (inst->dst.pack) {
+        if (inst->op != QOP_BRANCH) {
+                qir_print_reg(c, inst->dst, true);
                 if (inst->dst.pack) {
-                        if (qir_is_mul(inst))
-                                vc4_qpu_disasm_pack_mul(stderr, inst->dst.pack);
-                        else
-                                vc4_qpu_disasm_pack_a(stderr, inst->dst.pack);
+                        if (inst->dst.pack) {
+                                if (qir_is_mul(inst))
+                                        vc4_qpu_disasm_pack_mul(stderr, inst->dst.pack);
+                                else
+                                        vc4_qpu_disasm_pack_a(stderr, inst->dst.pack);
+                        }
                 }
         }
+
         for (int i = 0; i < qir_get_op_nsrc(inst->op); i++) {
                 fprintf(stderr, ", ");
                 qir_print_reg(c, inst->src[i], false);
@@ -412,6 +424,14 @@ qir_dump(struct vc4_compile *c)
                         fprintf(stderr, "\n");
                         ip++;
                 }
+                if (block->successors[1]) {
+                        fprintf(stderr, "-> BLOCK %d, %d\n",
+                                block->successors[0]->index,
+                                block->successors[1]->index);
+                } else if (block->successors[0]) {
+                        fprintf(stderr, "-> BLOCK %d\n",
+                                block->successors[0]->index);
+                }
         }
 }
 
index 5099b7f1f1c908d3c7fa071922b6122dc6cd470e..ad784bb987bb3df0115eabf5822035dc2ad4aef7 100644 (file)
@@ -156,6 +156,12 @@ enum qop {
         QOP_TEX_RESULT,
 
         QOP_LOAD_IMM,
+
+        /* Jumps to block->successor[0] if the qinst->cond (as a
+         * QPU_COND_BRANCH_*) passes, or block->successor[1] if not.  Note
+         * that block->successor[1] may be unset if the condition is ALWAYS.
+         */
+        QOP_BRANCH,
 };
 
 struct queued_qpu_inst {
@@ -754,6 +760,15 @@ qir_LOAD_IMM(struct vc4_compile *c, uint32_t val)
                                         qir_reg(QFILE_LOAD_IMM, val), c->undef));
 }
 
+static inline struct qinst *
+qir_BRANCH(struct vc4_compile *c, uint8_t cond)
+{
+        struct qinst *inst = qir_inst(QOP_BRANCH, c->undef, c->undef, c->undef);
+        inst->cond = cond;
+        qir_emit_nondef(c, inst);
+        return inst;
+}
+
 #define qir_for_each_block(block, c)                                    \
         list_for_each_entry(struct qblock, block, &c->blocks, link)
 
index fc918040a286de6550d68bf0acfcdb0605e6c0c4..903c6108824032cb7280cf4bddfc83a73ea459f3 100644 (file)
@@ -388,6 +388,14 @@ choose_instruction(struct schedule_state *state)
         struct schedule_node *chosen = NULL;
 
         list_for_each_entry(struct schedule_node, n, &state->worklist, link) {
+                /* The branches aren't being tracked as dependencies.  Make
+                 * sure that they stay scheduled as the last instruction of
+                 * the block, which is to say the first one we choose to
+                 * schedule.
+                 */
+                if (n->inst->op == QOP_BRANCH)
+                        return n;
+
                 if (!chosen) {
                         chosen = n;
                         continue;
index 1fd151acac281f811b25b7c2107d573e11af7be1..2257dcce83be3f70c180c612abbe815d75f6fb2f 100644 (file)
@@ -446,6 +446,15 @@ vc4_generate_code(struct vc4_context *vc4, struct vc4_compile *c)
                         handle_r4_qpu_write(c, qinst, dst);
                         break;
 
+                case QOP_BRANCH:
+                        /* The branch target will be updated at QPU scheduling
+                         * time.
+                         */
+                        queue(c, (qpu_branch(qinst->cond, 0) |
+                                  QPU_BRANCH_REL));
+                        handled_qinst_cond = true;
+                        break;
+
                 default:
                         assert(qinst->op < ARRAY_SIZE(translate));
                         assert(translate[qinst->op].op != 0); /* NOPs */