101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2016 Broadcom
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2101e04c3fSmrg * IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg#include <string.h>
2501e04c3fSmrg#include <stdio.h>
2601e04c3fSmrg#include "util/ralloc.h"
2701e04c3fSmrg
2801e04c3fSmrg#include "broadcom/common/v3d_device_info.h"
2901e04c3fSmrg#include "qpu_instr.h"
3001e04c3fSmrg#include "qpu_disasm.h"
3101e04c3fSmrg
3201e04c3fSmrgstruct disasm_state {
3301e04c3fSmrg        const struct v3d_device_info *devinfo;
3401e04c3fSmrg        char *string;
3501e04c3fSmrg        size_t offset;
3601e04c3fSmrg};
3701e04c3fSmrg
3801e04c3fSmrgstatic void
3901e04c3fSmrgappend(struct disasm_state *disasm, const char *fmt, ...)
4001e04c3fSmrg{
4101e04c3fSmrg        va_list args;
4201e04c3fSmrg        va_start(args, fmt);
4301e04c3fSmrg        ralloc_vasprintf_rewrite_tail(&disasm->string,
4401e04c3fSmrg                                      &disasm->offset,
4501e04c3fSmrg                                      fmt, args);
4601e04c3fSmrg        va_end(args);
4701e04c3fSmrg}
4801e04c3fSmrg
4901e04c3fSmrgstatic void
5001e04c3fSmrgpad_to(struct disasm_state *disasm, int n)
5101e04c3fSmrg{
5201e04c3fSmrg        /* FIXME: Do a single append somehow. */
5301e04c3fSmrg        while (disasm->offset < n)
5401e04c3fSmrg                append(disasm, " ");
5501e04c3fSmrg}
5601e04c3fSmrg
5701e04c3fSmrg
5801e04c3fSmrgstatic void
5901e04c3fSmrgv3d_qpu_disasm_raddr(struct disasm_state *disasm,
6001e04c3fSmrg                     const struct v3d_qpu_instr *instr, uint8_t mux)
6101e04c3fSmrg{
6201e04c3fSmrg        if (mux == V3D_QPU_MUX_A) {
6301e04c3fSmrg                append(disasm, "rf%d", instr->raddr_a);
6401e04c3fSmrg        } else if (mux == V3D_QPU_MUX_B) {
6501e04c3fSmrg                if (instr->sig.small_imm) {
6601e04c3fSmrg                        uint32_t val;
677ec681f3Smrg                        ASSERTED bool ok =
6801e04c3fSmrg                                v3d_qpu_small_imm_unpack(disasm->devinfo,
6901e04c3fSmrg                                                         instr->raddr_b,
7001e04c3fSmrg                                                         &val);
7101e04c3fSmrg
7201e04c3fSmrg                        if ((int)val >= -16 && (int)val <= 15)
7301e04c3fSmrg                                append(disasm, "%d", val);
7401e04c3fSmrg                        else
7501e04c3fSmrg                                append(disasm, "0x%08x", val);
7601e04c3fSmrg                        assert(ok);
7701e04c3fSmrg                } else {
7801e04c3fSmrg                        append(disasm, "rf%d", instr->raddr_b);
7901e04c3fSmrg                }
8001e04c3fSmrg        } else {
8101e04c3fSmrg                append(disasm, "r%d", mux);
8201e04c3fSmrg        }
8301e04c3fSmrg}
8401e04c3fSmrg
8501e04c3fSmrgstatic void
8601e04c3fSmrgv3d_qpu_disasm_waddr(struct disasm_state *disasm, uint32_t waddr, bool magic)
8701e04c3fSmrg{
8801e04c3fSmrg        if (!magic) {
8901e04c3fSmrg                append(disasm, "rf%d", waddr);
9001e04c3fSmrg                return;
9101e04c3fSmrg        }
9201e04c3fSmrg
937ec681f3Smrg        const char *name = v3d_qpu_magic_waddr_name(disasm->devinfo, waddr);
9401e04c3fSmrg        if (name)
9501e04c3fSmrg                append(disasm, "%s", name);
9601e04c3fSmrg        else
9701e04c3fSmrg                append(disasm, "waddr UNKNOWN %d", waddr);
9801e04c3fSmrg}
9901e04c3fSmrg
10001e04c3fSmrgstatic void
10101e04c3fSmrgv3d_qpu_disasm_add(struct disasm_state *disasm,
10201e04c3fSmrg                   const struct v3d_qpu_instr *instr)
10301e04c3fSmrg{
10401e04c3fSmrg        bool has_dst = v3d_qpu_add_op_has_dst(instr->alu.add.op);
10501e04c3fSmrg        int num_src = v3d_qpu_add_op_num_src(instr->alu.add.op);
10601e04c3fSmrg
10701e04c3fSmrg        append(disasm, "%s", v3d_qpu_add_op_name(instr->alu.add.op));
10801e04c3fSmrg        if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
10901e04c3fSmrg                append(disasm, "%s", v3d_qpu_cond_name(instr->flags.ac));
11001e04c3fSmrg        append(disasm, "%s", v3d_qpu_pf_name(instr->flags.apf));
11101e04c3fSmrg        append(disasm, "%s", v3d_qpu_uf_name(instr->flags.auf));
11201e04c3fSmrg
11301e04c3fSmrg        append(disasm, "  ");
11401e04c3fSmrg
11501e04c3fSmrg        if (has_dst) {
11601e04c3fSmrg                v3d_qpu_disasm_waddr(disasm, instr->alu.add.waddr,
11701e04c3fSmrg                                     instr->alu.add.magic_write);
11801e04c3fSmrg                append(disasm, v3d_qpu_pack_name(instr->alu.add.output_pack));
11901e04c3fSmrg        }
12001e04c3fSmrg
12101e04c3fSmrg        if (num_src >= 1) {
12201e04c3fSmrg                if (has_dst)
12301e04c3fSmrg                        append(disasm, ", ");
12401e04c3fSmrg                v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.a);
12501e04c3fSmrg                append(disasm, "%s",
12601e04c3fSmrg                       v3d_qpu_unpack_name(instr->alu.add.a_unpack));
12701e04c3fSmrg        }
12801e04c3fSmrg
12901e04c3fSmrg        if (num_src >= 2) {
13001e04c3fSmrg                append(disasm, ", ");
13101e04c3fSmrg                v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.b);
13201e04c3fSmrg                append(disasm, "%s",
13301e04c3fSmrg                       v3d_qpu_unpack_name(instr->alu.add.b_unpack));
13401e04c3fSmrg        }
13501e04c3fSmrg}
13601e04c3fSmrg
13701e04c3fSmrgstatic void
13801e04c3fSmrgv3d_qpu_disasm_mul(struct disasm_state *disasm,
13901e04c3fSmrg                   const struct v3d_qpu_instr *instr)
14001e04c3fSmrg{
14101e04c3fSmrg        bool has_dst = v3d_qpu_mul_op_has_dst(instr->alu.mul.op);
14201e04c3fSmrg        int num_src = v3d_qpu_mul_op_num_src(instr->alu.mul.op);
14301e04c3fSmrg
14401e04c3fSmrg        pad_to(disasm, 21);
14501e04c3fSmrg        append(disasm, "; ");
14601e04c3fSmrg
14701e04c3fSmrg        append(disasm, "%s", v3d_qpu_mul_op_name(instr->alu.mul.op));
14801e04c3fSmrg        if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
14901e04c3fSmrg                append(disasm, "%s", v3d_qpu_cond_name(instr->flags.mc));
15001e04c3fSmrg        append(disasm, "%s", v3d_qpu_pf_name(instr->flags.mpf));
15101e04c3fSmrg        append(disasm, "%s", v3d_qpu_uf_name(instr->flags.muf));
15201e04c3fSmrg
15301e04c3fSmrg        if (instr->alu.mul.op == V3D_QPU_M_NOP)
15401e04c3fSmrg                return;
15501e04c3fSmrg
15601e04c3fSmrg        append(disasm, "  ");
15701e04c3fSmrg
15801e04c3fSmrg        if (has_dst) {
15901e04c3fSmrg                v3d_qpu_disasm_waddr(disasm, instr->alu.mul.waddr,
16001e04c3fSmrg                                     instr->alu.mul.magic_write);
16101e04c3fSmrg                append(disasm, v3d_qpu_pack_name(instr->alu.mul.output_pack));
16201e04c3fSmrg        }
16301e04c3fSmrg
16401e04c3fSmrg        if (num_src >= 1) {
16501e04c3fSmrg                if (has_dst)
16601e04c3fSmrg                        append(disasm, ", ");
16701e04c3fSmrg                v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.a);
16801e04c3fSmrg                append(disasm, "%s",
16901e04c3fSmrg                       v3d_qpu_unpack_name(instr->alu.mul.a_unpack));
17001e04c3fSmrg        }
17101e04c3fSmrg
17201e04c3fSmrg        if (num_src >= 2) {
17301e04c3fSmrg                append(disasm, ", ");
17401e04c3fSmrg                v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.b);
17501e04c3fSmrg                append(disasm, "%s",
17601e04c3fSmrg                       v3d_qpu_unpack_name(instr->alu.mul.b_unpack));
17701e04c3fSmrg        }
17801e04c3fSmrg}
17901e04c3fSmrg
18001e04c3fSmrgstatic void
18101e04c3fSmrgv3d_qpu_disasm_sig_addr(struct disasm_state *disasm,
18201e04c3fSmrg                        const struct v3d_qpu_instr *instr)
18301e04c3fSmrg{
18401e04c3fSmrg        if (disasm->devinfo->ver < 41)
18501e04c3fSmrg                return;
18601e04c3fSmrg
18701e04c3fSmrg        if (!instr->sig_magic)
18801e04c3fSmrg                append(disasm, ".rf%d", instr->sig_addr);
18901e04c3fSmrg        else {
1907ec681f3Smrg                const char *name =
1917ec681f3Smrg                        v3d_qpu_magic_waddr_name(disasm->devinfo,
1927ec681f3Smrg                                                 instr->sig_addr);
19301e04c3fSmrg                if (name)
19401e04c3fSmrg                        append(disasm, ".%s", name);
19501e04c3fSmrg                else
19601e04c3fSmrg                        append(disasm, ".UNKNOWN%d", instr->sig_addr);
19701e04c3fSmrg        }
19801e04c3fSmrg}
19901e04c3fSmrg
20001e04c3fSmrgstatic void
20101e04c3fSmrgv3d_qpu_disasm_sig(struct disasm_state *disasm,
20201e04c3fSmrg                   const struct v3d_qpu_instr *instr)
20301e04c3fSmrg{
20401e04c3fSmrg        const struct v3d_qpu_sig *sig = &instr->sig;
20501e04c3fSmrg
20601e04c3fSmrg        if (!sig->thrsw &&
20701e04c3fSmrg            !sig->ldvary &&
20801e04c3fSmrg            !sig->ldvpm &&
20901e04c3fSmrg            !sig->ldtmu &&
2107ec681f3Smrg            !sig->ldtlb &&
2117ec681f3Smrg            !sig->ldtlbu &&
21201e04c3fSmrg            !sig->ldunif &&
21301e04c3fSmrg            !sig->ldunifrf &&
21401e04c3fSmrg            !sig->ldunifa &&
21501e04c3fSmrg            !sig->ldunifarf &&
21601e04c3fSmrg            !sig->wrtmuc) {
21701e04c3fSmrg                return;
21801e04c3fSmrg        }
21901e04c3fSmrg
22001e04c3fSmrg        pad_to(disasm, 41);
22101e04c3fSmrg
22201e04c3fSmrg        if (sig->thrsw)
22301e04c3fSmrg                append(disasm, "; thrsw");
22401e04c3fSmrg        if (sig->ldvary) {
22501e04c3fSmrg                append(disasm, "; ldvary");
22601e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
22701e04c3fSmrg        }
22801e04c3fSmrg        if (sig->ldvpm)
22901e04c3fSmrg                append(disasm, "; ldvpm");
23001e04c3fSmrg        if (sig->ldtmu) {
23101e04c3fSmrg                append(disasm, "; ldtmu");
23201e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
23301e04c3fSmrg        }
23401e04c3fSmrg        if (sig->ldtlb) {
23501e04c3fSmrg                append(disasm, "; ldtlb");
23601e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
23701e04c3fSmrg        }
23801e04c3fSmrg        if (sig->ldtlbu) {
23901e04c3fSmrg                append(disasm, "; ldtlbu");
24001e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
24101e04c3fSmrg        }
24201e04c3fSmrg        if (sig->ldunif)
24301e04c3fSmrg                append(disasm, "; ldunif");
24401e04c3fSmrg        if (sig->ldunifrf) {
24501e04c3fSmrg                append(disasm, "; ldunifrf");
24601e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
24701e04c3fSmrg        }
24801e04c3fSmrg        if (sig->ldunifa)
24901e04c3fSmrg                append(disasm, "; ldunifa");
25001e04c3fSmrg        if (sig->ldunifarf) {
25101e04c3fSmrg                append(disasm, "; ldunifarf");
25201e04c3fSmrg                v3d_qpu_disasm_sig_addr(disasm, instr);
25301e04c3fSmrg        }
25401e04c3fSmrg        if (sig->wrtmuc)
25501e04c3fSmrg                append(disasm, "; wrtmuc");
25601e04c3fSmrg}
25701e04c3fSmrg
25801e04c3fSmrgstatic void
25901e04c3fSmrgv3d_qpu_disasm_alu(struct disasm_state *disasm,
26001e04c3fSmrg                   const struct v3d_qpu_instr *instr)
26101e04c3fSmrg{
26201e04c3fSmrg        v3d_qpu_disasm_add(disasm, instr);
26301e04c3fSmrg        v3d_qpu_disasm_mul(disasm, instr);
26401e04c3fSmrg        v3d_qpu_disasm_sig(disasm, instr);
26501e04c3fSmrg}
26601e04c3fSmrg
26701e04c3fSmrgstatic void
26801e04c3fSmrgv3d_qpu_disasm_branch(struct disasm_state *disasm,
26901e04c3fSmrg                      const struct v3d_qpu_instr *instr)
27001e04c3fSmrg{
27101e04c3fSmrg        append(disasm, "b");
27201e04c3fSmrg        if (instr->branch.ub)
27301e04c3fSmrg                append(disasm, "u");
27401e04c3fSmrg        append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond));
27501e04c3fSmrg        append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign));
27601e04c3fSmrg
27701e04c3fSmrg        switch (instr->branch.bdi) {
27801e04c3fSmrg        case V3D_QPU_BRANCH_DEST_ABS:
27901e04c3fSmrg                append(disasm, "  zero_addr+0x%08x", instr->branch.offset);
28001e04c3fSmrg                break;
28101e04c3fSmrg
28201e04c3fSmrg        case V3D_QPU_BRANCH_DEST_REL:
28301e04c3fSmrg                append(disasm, "  %d", instr->branch.offset);
28401e04c3fSmrg                break;
28501e04c3fSmrg
28601e04c3fSmrg        case V3D_QPU_BRANCH_DEST_LINK_REG:
28701e04c3fSmrg                append(disasm, "  lri");
28801e04c3fSmrg                break;
28901e04c3fSmrg
29001e04c3fSmrg        case V3D_QPU_BRANCH_DEST_REGFILE:
29101e04c3fSmrg                append(disasm, "  rf%d", instr->branch.raddr_a);
29201e04c3fSmrg                break;
29301e04c3fSmrg        }
29401e04c3fSmrg
29501e04c3fSmrg        if (instr->branch.ub) {
29601e04c3fSmrg                switch (instr->branch.bdu) {
29701e04c3fSmrg                case V3D_QPU_BRANCH_DEST_ABS:
29801e04c3fSmrg                        append(disasm, ", a:unif");
29901e04c3fSmrg                        break;
30001e04c3fSmrg
30101e04c3fSmrg                case V3D_QPU_BRANCH_DEST_REL:
30201e04c3fSmrg                        append(disasm, ", r:unif");
30301e04c3fSmrg                        break;
30401e04c3fSmrg
30501e04c3fSmrg                case V3D_QPU_BRANCH_DEST_LINK_REG:
30601e04c3fSmrg                        append(disasm, ", lri");
30701e04c3fSmrg                        break;
30801e04c3fSmrg
30901e04c3fSmrg                case V3D_QPU_BRANCH_DEST_REGFILE:
31001e04c3fSmrg                        append(disasm, ", rf%d", instr->branch.raddr_a);
31101e04c3fSmrg                        break;
31201e04c3fSmrg                }
31301e04c3fSmrg        }
31401e04c3fSmrg}
31501e04c3fSmrg
31601e04c3fSmrgconst char *
31701e04c3fSmrgv3d_qpu_decode(const struct v3d_device_info *devinfo,
31801e04c3fSmrg               const struct v3d_qpu_instr *instr)
31901e04c3fSmrg{
32001e04c3fSmrg        struct disasm_state disasm = {
32101e04c3fSmrg                .string = rzalloc_size(NULL, 1),
32201e04c3fSmrg                .offset = 0,
32301e04c3fSmrg                .devinfo = devinfo,
32401e04c3fSmrg        };
32501e04c3fSmrg
32601e04c3fSmrg        switch (instr->type) {
32701e04c3fSmrg        case V3D_QPU_INSTR_TYPE_ALU:
32801e04c3fSmrg                v3d_qpu_disasm_alu(&disasm, instr);
32901e04c3fSmrg                break;
33001e04c3fSmrg
33101e04c3fSmrg        case V3D_QPU_INSTR_TYPE_BRANCH:
33201e04c3fSmrg                v3d_qpu_disasm_branch(&disasm, instr);
33301e04c3fSmrg                break;
33401e04c3fSmrg        }
33501e04c3fSmrg
33601e04c3fSmrg        return disasm.string;
33701e04c3fSmrg}
33801e04c3fSmrg
33901e04c3fSmrg/**
34001e04c3fSmrg * Returns a string containing the disassembled representation of the QPU
34101e04c3fSmrg * instruction.  It is the caller's responsibility to free the return value
34201e04c3fSmrg * with ralloc_free().
34301e04c3fSmrg */
34401e04c3fSmrgconst char *
34501e04c3fSmrgv3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst)
34601e04c3fSmrg{
34701e04c3fSmrg        struct v3d_qpu_instr instr;
34801e04c3fSmrg        bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr);
34901e04c3fSmrg        assert(ok); (void)ok;
35001e04c3fSmrg
35101e04c3fSmrg        return v3d_qpu_decode(devinfo, &instr);
35201e04c3fSmrg}
35301e04c3fSmrg
35401e04c3fSmrgvoid
35501e04c3fSmrgv3d_qpu_dump(const struct v3d_device_info *devinfo,
35601e04c3fSmrg             const struct v3d_qpu_instr *instr)
35701e04c3fSmrg{
35801e04c3fSmrg        const char *decoded = v3d_qpu_decode(devinfo, instr);
35901e04c3fSmrg        fprintf(stderr, "%s", decoded);
36001e04c3fSmrg        ralloc_free((char *)decoded);
36101e04c3fSmrg}
362