uint64_t qpu_branch(uint32_t cond, uint32_t target) { uint64_t inst = 0; inst |= qpu_a_dst(qpu_ra(QPU_W_NOP)); inst |= qpu_m_dst(qpu_rb(QPU_W_NOP)); inst |= QPU_SET_FIELD(cond, QPU_BRANCH_COND); inst |= QPU_SET_FIELD(QPU_SIG_BRANCH, QPU_SIG); inst |= QPU_SET_FIELD(target, QPU_BRANCH_TARGET); return inst; }
uint64_t qpu_load_imm_ui(struct qpu_reg dst, uint32_t val) { uint64_t inst = 0; inst |= qpu_a_dst(dst); inst |= QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_MUL); inst |= QPU_SET_FIELD(QPU_COND_ALWAYS, QPU_COND_ADD); inst |= QPU_SET_FIELD(QPU_COND_ALWAYS, QPU_COND_MUL); inst |= QPU_SET_FIELD(QPU_SIG_LOAD_IMM, QPU_SIG); inst |= val; return inst; }
static uint64_t qpu_m_dst(struct qpu_reg dst) { uint64_t inst = 0; if (dst.mux <= QPU_MUX_R5) { /* Translate the mux to the ACCn values. */ inst |= QPU_SET_FIELD(32 + dst.mux, QPU_WADDR_MUL); } else { inst |= QPU_SET_FIELD(dst.addr, QPU_WADDR_MUL); if (dst.mux == QPU_MUX_A) inst |= QPU_WS; } return inst; }
static uint64_t set_src_raddr(uint64_t inst, struct qpu_reg src) { if (src.mux == QPU_MUX_A) { assert(QPU_GET_FIELD(inst, QPU_RADDR_A) == QPU_R_NOP || QPU_GET_FIELD(inst, QPU_RADDR_A) == src.addr); return QPU_UPDATE_FIELD(inst, src.addr, QPU_RADDR_A); } if (src.mux == QPU_MUX_B) { assert((QPU_GET_FIELD(inst, QPU_RADDR_B) == QPU_R_NOP || QPU_GET_FIELD(inst, QPU_RADDR_B) == src.addr) && QPU_GET_FIELD(inst, QPU_SIG) != QPU_SIG_SMALL_IMM); return QPU_UPDATE_FIELD(inst, src.addr, QPU_RADDR_B); } if (src.mux == QPU_MUX_SMALL_IMM) { if (QPU_GET_FIELD(inst, QPU_SIG) == QPU_SIG_SMALL_IMM) { assert(QPU_GET_FIELD(inst, QPU_RADDR_B) == src.addr); } else { inst = qpu_set_sig(inst, QPU_SIG_SMALL_IMM); assert(QPU_GET_FIELD(inst, QPU_RADDR_B) == QPU_R_NOP); } return ((inst & ~QPU_RADDR_B_MASK) | QPU_SET_FIELD(src.addr, QPU_RADDR_B)); } return inst; }
static bool try_swap_ra_file(uint64_t *merge, uint64_t *a, uint64_t *b) { uint32_t raddr_a_a = QPU_GET_FIELD(*a, QPU_RADDR_A); uint32_t raddr_a_b = QPU_GET_FIELD(*a, QPU_RADDR_B); uint32_t raddr_b_a = QPU_GET_FIELD(*b, QPU_RADDR_A); uint32_t raddr_b_b = QPU_GET_FIELD(*b, QPU_RADDR_B); if (raddr_a_b != QPU_R_NOP) return false; switch (raddr_a_a) { case QPU_R_UNIF: case QPU_R_VARY: break; default: return false; } if (!(*merge & QPU_PM) && QPU_GET_FIELD(*merge, QPU_UNPACK) != QPU_UNPACK_NOP) { return false; } if (raddr_b_b != QPU_R_NOP && raddr_b_b != raddr_a_a) return false; /* Move raddr A to B in instruction a. */ *a = (*a & ~QPU_RADDR_A_MASK) | QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_A); *a = (*a & ~QPU_RADDR_B_MASK) | QPU_SET_FIELD(raddr_a_a, QPU_RADDR_B); *merge = QPU_UPDATE_FIELD(*merge, raddr_b_a, QPU_RADDR_A); *merge = QPU_UPDATE_FIELD(*merge, raddr_a_a, QPU_RADDR_B); swap_ra_file_mux_helper(merge, a, QPU_ADD_A_SHIFT); swap_ra_file_mux_helper(merge, a, QPU_ADD_B_SHIFT); swap_ra_file_mux_helper(merge, a, QPU_MUL_A_SHIFT); swap_ra_file_mux_helper(merge, a, QPU_MUL_B_SHIFT); return true; }
uint64_t qpu_NOP() { uint64_t inst = 0; inst |= QPU_SET_FIELD(QPU_A_NOP, QPU_OP_ADD); inst |= QPU_SET_FIELD(QPU_M_NOP, QPU_OP_MUL); /* Note: These field values are actually non-zero */ inst |= QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_ADD); inst |= QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_MUL); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_A); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_B); inst |= QPU_SET_FIELD(QPU_SIG_NONE, QPU_SIG); return inst; }
uint64_t qpu_m_MOV(struct qpu_reg dst, struct qpu_reg src) { uint64_t inst = 0; inst |= QPU_SET_FIELD(QPU_SIG_NONE, QPU_SIG); inst |= QPU_SET_FIELD(QPU_M_V8MIN, QPU_OP_MUL); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_A); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_B); inst |= qpu_m_dst(dst); inst |= QPU_SET_FIELD(QPU_COND_ALWAYS, QPU_COND_MUL); inst |= QPU_MUX(src.mux, QPU_MUL_A); inst |= QPU_MUX(src.mux, QPU_MUL_B); inst = set_src_raddr(inst, src); inst |= QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_ADD); return inst; }
uint64_t qpu_m_alu2(enum qpu_op_mul op, struct qpu_reg dst, struct qpu_reg src0, struct qpu_reg src1) { uint64_t inst = 0; inst |= QPU_SET_FIELD(QPU_SIG_NONE, QPU_SIG); inst |= QPU_SET_FIELD(op, QPU_OP_MUL); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_A); inst |= QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_B); inst |= qpu_m_dst(dst); inst |= QPU_SET_FIELD(QPU_COND_ALWAYS, QPU_COND_MUL); inst |= QPU_MUX(src0.mux, QPU_MUL_A); inst = set_src_raddr(inst, src0); inst |= QPU_MUX(src1.mux, QPU_MUL_B); inst = set_src_raddr(inst, src1); inst |= QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_ADD); return inst; }
void vc4_generate_code(struct vc4_context *vc4, struct vc4_compile *c) { struct qpu_reg *temp_registers = vc4_register_allocate(vc4, c); bool discard = false; uint32_t inputs_remaining = c->num_inputs; uint32_t vpm_read_fifo_count = 0; uint32_t vpm_read_offset = 0; int last_vpm_read_index = -1; /* Map from the QIR ops enum order to QPU unpack bits. */ static const uint32_t unpack_map[] = { QPU_UNPACK_8A, QPU_UNPACK_8B, QPU_UNPACK_8C, QPU_UNPACK_8D, QPU_UNPACK_16A_TO_F32, QPU_UNPACK_16B_TO_F32, }; list_inithead(&c->qpu_inst_list); switch (c->stage) { case QSTAGE_VERT: case QSTAGE_COORD: /* There's a 4-entry FIFO for VPMVCD reads, each of which can * load up to 16 dwords (4 vec4s) per vertex. */ while (inputs_remaining) { uint32_t num_entries = MIN2(inputs_remaining, 16); queue(c, qpu_load_imm_ui(qpu_vrsetup(), vpm_read_offset | 0x00001a00 | ((num_entries & 0xf) << 20))); inputs_remaining -= num_entries; vpm_read_offset += num_entries; vpm_read_fifo_count++; } assert(vpm_read_fifo_count <= 4); queue(c, qpu_load_imm_ui(qpu_vwsetup(), 0x00001a00)); break; case QSTAGE_FRAG: break; } list_for_each_entry(struct qinst, qinst, &c->instructions, link) { #if 0 fprintf(stderr, "translating qinst to qpu: "); qir_dump_inst(qinst); fprintf(stderr, "\n"); #endif static const struct { uint32_t op; } translate[] = { #define A(name) [QOP_##name] = {QPU_A_##name} #define M(name) [QOP_##name] = {QPU_M_##name} A(FADD), A(FSUB), A(FMIN), A(FMAX), A(FMINABS), A(FMAXABS), A(FTOI), A(ITOF), A(ADD), A(SUB), A(SHL), A(SHR), A(ASR), A(MIN), A(MAX), A(AND), A(OR), A(XOR), A(NOT), M(FMUL), M(MUL24), }; struct qpu_reg src[4]; for (int i = 0; i < qir_get_op_nsrc(qinst->op); i++) { int index = qinst->src[i].index; switch (qinst->src[i].file) { case QFILE_NULL: src[i] = qpu_rn(0); break; case QFILE_TEMP: src[i] = temp_registers[index]; break; case QFILE_UNIF: src[i] = qpu_unif(); break; case QFILE_VARY: src[i] = qpu_vary(); break; case QFILE_SMALL_IMM: src[i].mux = QPU_MUX_SMALL_IMM; src[i].addr = qpu_encode_small_immediate(qinst->src[i].index); /* This should only have returned a valid * small immediate field, not ~0 for failure. */ assert(src[i].addr <= 47); break; case QFILE_VPM: assert((int)qinst->src[i].index >= last_vpm_read_index); (void)last_vpm_read_index; last_vpm_read_index = qinst->src[i].index; src[i] = qpu_ra(QPU_R_VPM); break; } } struct qpu_reg dst; switch (qinst->dst.file) { case QFILE_NULL: dst = qpu_ra(QPU_W_NOP); break; case QFILE_TEMP: dst = temp_registers[qinst->dst.index]; break; case QFILE_VPM: dst = qpu_ra(QPU_W_VPM); break; case QFILE_VARY: case QFILE_UNIF: case QFILE_SMALL_IMM: assert(!"not reached"); break; } switch (qinst->op) { case QOP_MOV: /* Skip emitting the MOV if it's a no-op. */ if (dst.mux == QPU_MUX_A || dst.mux == QPU_MUX_B || dst.mux != src[0].mux || dst.addr != src[0].addr) { queue(c, qpu_a_MOV(dst, src[0])); } break; case QOP_SEL_X_0_ZS: case QOP_SEL_X_0_ZC: case QOP_SEL_X_0_NS: case QOP_SEL_X_0_NC: case QOP_SEL_X_0_CS: case QOP_SEL_X_0_CC: queue(c, qpu_a_MOV(dst, src[0])); set_last_cond_add(c, qinst->op - QOP_SEL_X_0_ZS + QPU_COND_ZS); queue(c, qpu_a_XOR(dst, qpu_r0(), qpu_r0())); set_last_cond_add(c, ((qinst->op - QOP_SEL_X_0_ZS) ^ 1) + QPU_COND_ZS); break; case QOP_SEL_X_Y_ZS: case QOP_SEL_X_Y_ZC: case QOP_SEL_X_Y_NS: case QOP_SEL_X_Y_NC: case QOP_SEL_X_Y_CS: case QOP_SEL_X_Y_CC: queue(c, qpu_a_MOV(dst, src[0])); set_last_cond_add(c, qinst->op - QOP_SEL_X_Y_ZS + QPU_COND_ZS); queue(c, qpu_a_MOV(dst, src[1])); set_last_cond_add(c, ((qinst->op - QOP_SEL_X_Y_ZS) ^ 1) + QPU_COND_ZS); break; case QOP_RCP: case QOP_RSQ: case QOP_EXP2: case QOP_LOG2: switch (qinst->op) { case QOP_RCP: queue(c, qpu_a_MOV(qpu_rb(QPU_W_SFU_RECIP), src[0])); break; case QOP_RSQ: queue(c, qpu_a_MOV(qpu_rb(QPU_W_SFU_RECIPSQRT), src[0])); break; case QOP_EXP2: queue(c, qpu_a_MOV(qpu_rb(QPU_W_SFU_EXP), src[0])); break; case QOP_LOG2: queue(c, qpu_a_MOV(qpu_rb(QPU_W_SFU_LOG), src[0])); break; default: abort(); } if (dst.mux != QPU_MUX_R4) queue(c, qpu_a_MOV(dst, qpu_r4())); break; case QOP_PACK_8888_F: queue(c, qpu_m_MOV(dst, src[0])); *last_inst(c) |= QPU_PM; *last_inst(c) |= QPU_SET_FIELD(QPU_PACK_MUL_8888, QPU_PACK); break; case QOP_PACK_8A_F: case QOP_PACK_8B_F: case QOP_PACK_8C_F: case QOP_PACK_8D_F: queue(c, qpu_m_MOV(dst, src[0]) | QPU_PM | QPU_SET_FIELD(QPU_PACK_MUL_8A + qinst->op - QOP_PACK_8A_F, QPU_PACK)); break; case QOP_FRAG_X: queue(c, qpu_a_ITOF(dst, qpu_ra(QPU_R_XY_PIXEL_COORD))); break; case QOP_FRAG_Y: queue(c, qpu_a_ITOF(dst, qpu_rb(QPU_R_XY_PIXEL_COORD))); break; case QOP_FRAG_REV_FLAG: queue(c, qpu_a_ITOF(dst, qpu_rb(QPU_R_MS_REV_FLAGS))); break; case QOP_FRAG_Z: case QOP_FRAG_W: /* QOP_FRAG_Z/W don't emit instructions, just allocate * the register to the Z/W payload. */ break; case QOP_TLB_DISCARD_SETUP: discard = true; queue(c, qpu_a_MOV(src[0], src[0])); *last_inst(c) |= QPU_SF; break; case QOP_TLB_STENCIL_SETUP: queue(c, qpu_a_MOV(qpu_ra(QPU_W_TLB_STENCIL_SETUP), src[0])); break; case QOP_TLB_Z_WRITE: queue(c, qpu_a_MOV(qpu_ra(QPU_W_TLB_Z), src[0])); if (discard) { set_last_cond_add(c, QPU_COND_ZS); } break; case QOP_TLB_COLOR_READ: queue(c, qpu_NOP()); *last_inst(c) = qpu_set_sig(*last_inst(c), QPU_SIG_COLOR_LOAD); if (dst.mux != QPU_MUX_R4) queue(c, qpu_a_MOV(dst, qpu_r4())); break; case QOP_TLB_COLOR_WRITE: queue(c, qpu_a_MOV(qpu_tlbc(), src[0])); if (discard) { set_last_cond_add(c, QPU_COND_ZS); } break; case QOP_VARY_ADD_C: queue(c, qpu_a_FADD(dst, src[0], qpu_r5())); break; case QOP_TEX_S: case QOP_TEX_T: case QOP_TEX_R: case QOP_TEX_B: queue(c, qpu_a_MOV(qpu_rb(QPU_W_TMU0_S + (qinst->op - QOP_TEX_S)), src[0])); break; case QOP_TEX_DIRECT: fixup_raddr_conflict(c, dst, &src[0], &src[1]); queue(c, qpu_a_ADD(qpu_rb(QPU_W_TMU0_S), src[0], src[1])); break; case QOP_TEX_RESULT: queue(c, qpu_NOP()); *last_inst(c) = qpu_set_sig(*last_inst(c), QPU_SIG_LOAD_TMU0); if (dst.mux != QPU_MUX_R4) queue(c, qpu_a_MOV(dst, qpu_r4())); break; case QOP_UNPACK_8A_F: case QOP_UNPACK_8B_F: case QOP_UNPACK_8C_F: case QOP_UNPACK_8D_F: case QOP_UNPACK_16A_F: case QOP_UNPACK_16B_F: { if (src[0].mux == QPU_MUX_R4) { queue(c, qpu_a_MOV(dst, src[0])); *last_inst(c) |= QPU_PM; *last_inst(c) |= QPU_SET_FIELD(QPU_UNPACK_8A + (qinst->op - QOP_UNPACK_8A_F), QPU_UNPACK); } else { assert(src[0].mux == QPU_MUX_A); /* Since we're setting the pack bits, if the * destination is in A it would get re-packed. */ queue(c, qpu_a_FMAX((dst.mux == QPU_MUX_A ? qpu_rb(31) : dst), src[0], src[0])); *last_inst(c) |= QPU_SET_FIELD(unpack_map[qinst->op - QOP_UNPACK_8A_F], QPU_UNPACK); if (dst.mux == QPU_MUX_A) { queue(c, qpu_a_MOV(dst, qpu_rb(31))); } } } break; case QOP_UNPACK_8A_I: case QOP_UNPACK_8B_I: case QOP_UNPACK_8C_I: case QOP_UNPACK_8D_I: case QOP_UNPACK_16A_I: case QOP_UNPACK_16B_I: { assert(src[0].mux == QPU_MUX_A); /* Since we're setting the pack bits, if the * destination is in A it would get re-packed. */ queue(c, qpu_a_MOV((dst.mux == QPU_MUX_A ? qpu_rb(31) : dst), src[0])); *last_inst(c) |= QPU_SET_FIELD(unpack_map[qinst->op - QOP_UNPACK_8A_I], QPU_UNPACK); if (dst.mux == QPU_MUX_A) { queue(c, qpu_a_MOV(dst, qpu_rb(31))); } } break; default: assert(qinst->op < ARRAY_SIZE(translate)); assert(translate[qinst->op].op != 0); /* NOPs */ /* If we have only one source, put it in the second * argument slot as well so that we don't take up * another raddr just to get unused data. */ if (qir_get_op_nsrc(qinst->op) == 1) src[1] = src[0]; fixup_raddr_conflict(c, dst, &src[0], &src[1]); if (qir_is_mul(qinst)) { queue(c, qpu_m_alu2(translate[qinst->op].op, dst, src[0], src[1])); if (qinst->dst.pack) { *last_inst(c) |= QPU_PM; *last_inst(c) |= QPU_SET_FIELD(qinst->dst.pack, QPU_PACK); } } else { queue(c, qpu_a_alu2(translate[qinst->op].op, dst, src[0], src[1])); if (qinst->dst.pack) { assert(dst.mux == QPU_MUX_A); *last_inst(c) |= QPU_SET_FIELD(qinst->dst.pack, QPU_PACK); } } break; } if (qinst->sf) { assert(!qir_is_multi_instruction(qinst)); *last_inst(c) |= QPU_SF; } } qpu_schedule_instructions(c); /* thread end can't have VPM write or read */ if (QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_WADDR_ADD) == QPU_W_VPM || QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_WADDR_MUL) == QPU_W_VPM || QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_RADDR_A) == QPU_R_VPM || QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_RADDR_B) == QPU_R_VPM) { qpu_serialize_one_inst(c, qpu_NOP()); } /* thread end can't have uniform read */ if (QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_RADDR_A) == QPU_R_UNIF || QPU_GET_FIELD(c->qpu_insts[c->qpu_inst_count - 1], QPU_RADDR_B) == QPU_R_UNIF) { qpu_serialize_one_inst(c, qpu_NOP()); } /* thread end can't have TLB operations */ if (qpu_inst_is_tlb(c->qpu_insts[c->qpu_inst_count - 1])) qpu_serialize_one_inst(c, qpu_NOP()); c->qpu_insts[c->qpu_inst_count - 1] = qpu_set_sig(c->qpu_insts[c->qpu_inst_count - 1], QPU_SIG_PROG_END); qpu_serialize_one_inst(c, qpu_NOP()); qpu_serialize_one_inst(c, qpu_NOP()); switch (c->stage) { case QSTAGE_VERT: case QSTAGE_COORD: break; case QSTAGE_FRAG: c->qpu_insts[c->qpu_inst_count - 1] = qpu_set_sig(c->qpu_insts[c->qpu_inst_count - 1], QPU_SIG_SCOREBOARD_UNLOCK); break; } if (vc4_debug & VC4_DEBUG_QPU) vc4_dump_program(c); vc4_qpu_validate(c->qpu_insts, c->qpu_inst_count); free(temp_registers); }
uint64_t qpu_merge_inst(uint64_t a, uint64_t b) { uint64_t merge = a | b; bool ok = true; uint32_t a_sig = QPU_GET_FIELD(a, QPU_SIG); uint32_t b_sig = QPU_GET_FIELD(b, QPU_SIG); if (QPU_GET_FIELD(a, QPU_OP_ADD) != QPU_A_NOP && QPU_GET_FIELD(b, QPU_OP_ADD) != QPU_A_NOP) { if (QPU_GET_FIELD(a, QPU_OP_MUL) != QPU_M_NOP || QPU_GET_FIELD(b, QPU_OP_MUL) != QPU_M_NOP || !(convert_mov(&a) || convert_mov(&b))) { return 0; } else { merge = a | b; } } if (QPU_GET_FIELD(a, QPU_OP_MUL) != QPU_M_NOP && QPU_GET_FIELD(b, QPU_OP_MUL) != QPU_M_NOP) return 0; if (qpu_num_sf_accesses(a) && qpu_num_sf_accesses(b)) return 0; if (a_sig == QPU_SIG_LOAD_IMM || b_sig == QPU_SIG_LOAD_IMM || a_sig == QPU_SIG_SMALL_IMM || b_sig == QPU_SIG_SMALL_IMM || a_sig == QPU_SIG_BRANCH || b_sig == QPU_SIG_BRANCH) { return 0; } ok = ok && merge_fields(&merge, a, b, QPU_SIG_MASK, QPU_SET_FIELD(QPU_SIG_NONE, QPU_SIG)); /* Misc fields that have to match exactly. */ ok = ok && merge_fields(&merge, a, b, QPU_SF, ~0); if (!merge_fields(&merge, a, b, QPU_RADDR_A_MASK, QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_A))) { /* Since we tend to use regfile A by default both for register * allocation and for our special values (uniforms and * varyings), try swapping uniforms and varyings to regfile B * to resolve raddr A conflicts. */ if (!try_swap_ra_file(&merge, &a, &b) && !try_swap_ra_file(&merge, &b, &a)) { return 0; } } ok = ok && merge_fields(&merge, a, b, QPU_RADDR_B_MASK, QPU_SET_FIELD(QPU_R_NOP, QPU_RADDR_B)); ok = ok && merge_fields(&merge, a, b, QPU_WADDR_ADD_MASK, QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_ADD)); ok = ok && merge_fields(&merge, a, b, QPU_WADDR_MUL_MASK, QPU_SET_FIELD(QPU_W_NOP, QPU_WADDR_MUL)); /* Allow disagreement on WS (swapping A vs B physical reg file as the * destination for ADD/MUL) if one of the original instructions * ignores it (probably because it's just writing to accumulators). */ if (qpu_waddr_ignores_ws(QPU_GET_FIELD(a, QPU_WADDR_ADD)) && qpu_waddr_ignores_ws(QPU_GET_FIELD(a, QPU_WADDR_MUL))) { merge = (merge & ~QPU_WS) | (b & QPU_WS); } else if (qpu_waddr_ignores_ws(QPU_GET_FIELD(b, QPU_WADDR_ADD)) && qpu_waddr_ignores_ws(QPU_GET_FIELD(b, QPU_WADDR_MUL))) { merge = (merge & ~QPU_WS) | (a & QPU_WS); } else { if ((a & QPU_WS) != (b & QPU_WS)) return 0; } if (!merge_fields(&merge, a, b, QPU_PM, ~0)) { /* If one instruction has PM bit set and the other not, the * one without PM shouldn't do packing/unpacking, and we * have to make sure non-NOP packing/unpacking from PM * instruction aren't added to it. */ uint64_t temp; /* Let a be the one with PM bit */ if (!(a & QPU_PM)) { temp = a; a = b; b = temp; } if ((b & (QPU_PACK_MASK | QPU_UNPACK_MASK)) != 0) return 0; if ((a & QPU_PACK_MASK) != 0 && QPU_GET_FIELD(b, QPU_OP_MUL) != QPU_M_NOP) return 0; if ((a & QPU_UNPACK_MASK) != 0 && reads_r4(b)) return 0; } else { /* packing: Make sure that non-NOP packs agree, then deal with * special-case failing of adding a non-NOP pack to something * with a NOP pack. */ if (!merge_fields(&merge, a, b, QPU_PACK_MASK, 0)) return 0; bool new_a_pack = (QPU_GET_FIELD(a, QPU_PACK) != QPU_GET_FIELD(merge, QPU_PACK)); bool new_b_pack = (QPU_GET_FIELD(b, QPU_PACK) != QPU_GET_FIELD(merge, QPU_PACK)); if (!(merge & QPU_PM)) { /* Make sure we're not going to be putting a new * a-file packing on either half. */ if (new_a_pack && writes_a_file(a)) return 0; if (new_b_pack && writes_a_file(b)) return 0; } else { /* Make sure we're not going to be putting new MUL * packing oneither half. */ if (new_a_pack && QPU_GET_FIELD(a, QPU_OP_MUL) != QPU_M_NOP) return 0; if (new_b_pack && QPU_GET_FIELD(b, QPU_OP_MUL) != QPU_M_NOP) return 0; } /* unpacking: Make sure that non-NOP unpacks agree, then deal * with special-case failing of adding a non-NOP unpack to * something with a NOP unpack. */ if (!merge_fields(&merge, a, b, QPU_UNPACK_MASK, 0)) return 0; bool new_a_unpack = (QPU_GET_FIELD(a, QPU_UNPACK) != QPU_GET_FIELD(merge, QPU_UNPACK)); bool new_b_unpack = (QPU_GET_FIELD(b, QPU_UNPACK) != QPU_GET_FIELD(merge, QPU_UNPACK)); if (!(merge & QPU_PM)) { /* Make sure we're not going to be putting a new * a-file packing on either half. */ if (new_a_unpack && QPU_GET_FIELD(a, QPU_RADDR_A) != QPU_R_NOP) return 0; if (new_b_unpack && QPU_GET_FIELD(b, QPU_RADDR_A) != QPU_R_NOP) return 0; } else { /* Make sure we're not going to be putting new r4 * unpack on either half. */ if (new_a_unpack && reads_r4(a)) return 0; if (new_b_unpack && reads_r4(b)) return 0; } } if (ok) return merge; else return 0; }
uint64_t qpu_load_imm_i2(struct qpu_reg dst, uint32_t val) { return qpu_load_imm_ui(dst, val) | QPU_SET_FIELD(QPU_LOAD_IMM_MODE_I2, QPU_LOAD_IMM_MODE); }