From 05bcd9dd960d5658801ab35d429ba9778f67cad0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 15 Mar 2016 13:55:28 -0700 Subject: [PATCH] vc4: Define a QIR branch instruction 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 | 38 +++++++++++++++++----- src/gallium/drivers/vc4/vc4_qir.h | 15 +++++++++ src/gallium/drivers/vc4/vc4_qir_schedule.c | 8 +++++ src/gallium/drivers/vc4/vc4_qpu_emit.c | 9 +++++ 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/gallium/drivers/vc4/vc4_qir.c b/src/gallium/drivers/vc4/vc4_qir.c index 4c81b567927..982e8298ae9 100644 --- a/src/gallium/drivers/vc4/vc4_qir.c +++ b/src/gallium/drivers/vc4/vc4_qir.c @@ -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); + } } } diff --git a/src/gallium/drivers/vc4/vc4_qir.h b/src/gallium/drivers/vc4/vc4_qir.h index 5099b7f1f1c..ad784bb987b 100644 --- a/src/gallium/drivers/vc4/vc4_qir.h +++ b/src/gallium/drivers/vc4/vc4_qir.h @@ -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) diff --git a/src/gallium/drivers/vc4/vc4_qir_schedule.c b/src/gallium/drivers/vc4/vc4_qir_schedule.c index fc918040a28..903c6108824 100644 --- a/src/gallium/drivers/vc4/vc4_qir_schedule.c +++ b/src/gallium/drivers/vc4/vc4_qir_schedule.c @@ -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; diff --git a/src/gallium/drivers/vc4/vc4_qpu_emit.c b/src/gallium/drivers/vc4/vc4_qpu_emit.c index 1fd151acac2..2257dcce83b 100644 --- a/src/gallium/drivers/vc4/vc4_qpu_emit.c +++ b/src/gallium/drivers/vc4/vc4_qpu_emit.c @@ -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 */ -- 2.30.2