freedreno/ir3: sync disasm changes from envytools
[mesa.git] / src / freedreno / ir3 / disasm-a3xx.c
index 4cf45ce922753f625e0e11b275339db117994560..1cd82876edb16f51e7185405d67e59f94a411762 100644 (file)
@@ -36,6 +36,7 @@
 enum debug_t {
        PRINT_RAW      = 0x1,    /* dump raw hexdump */
        PRINT_VERBOSE  = 0x2,
+       EXPAND_REPEAT  = 0x4,
 };
 
 static enum debug_t debug;
@@ -77,9 +78,12 @@ static const char *type[] = {
 struct disasm_ctx {
        FILE *out;
        int level;
+       unsigned gpu_id;
 
        /* current instruction repeat flag: */
        unsigned repeat;
+       /* current instruction repeat indx/offset (for --expand): */
+       unsigned repeatidx;
 };
 
 static void print_reg(struct disasm_ctx *ctx, reg_t reg, bool full, bool r,
@@ -117,19 +121,35 @@ static void print_reg(struct disasm_ctx *ctx, reg_t reg, bool full, bool r,
        } else if ((reg.num == REG_P0) && !c) {
                fprintf(ctx->out, "p0.%c", component[reg.comp]);
        } else {
-               fprintf(ctx->out, "%s%c%d.%c", full ? "" : "h", type, reg.num & 0x3f, component[reg.comp]);
+               fprintf(ctx->out, "%s%c%d.%c", full ? "" : "h", type, reg.num, component[reg.comp]);
        }
 }
 
+static unsigned regidx(reg_t reg)
+{
+       return (4 * reg.num) + reg.comp;
+}
+
+static reg_t idxreg(unsigned idx)
+{
+       return (reg_t){
+               .comp = idx & 0x3,
+               .num  = idx >> 2,
+       };
+}
 
 static void print_reg_dst(struct disasm_ctx *ctx, reg_t reg, bool full, bool addr_rel)
 {
+       reg = idxreg(regidx(reg) + ctx->repeatidx);
        print_reg(ctx, reg, full, false, false, false, false, false, addr_rel);
 }
 
 static void print_reg_src(struct disasm_ctx *ctx, reg_t reg, bool full, bool r,
                bool c, bool im, bool neg, bool abs, bool addr_rel)
 {
+       if (r)
+               reg = idxreg(regidx(reg) + ctx->repeatidx);
+
        print_reg(ctx, reg, full, r, c, im, neg, abs, addr_rel);
 }
 
@@ -165,6 +185,7 @@ static void print_instr_cat0(struct disasm_ctx *ctx, instr_t *instr)
 
        switch (cat0->opc) {
        case OPC_KILL:
+       case OPC_CONDEND:
                fprintf(ctx->out, " %sp0.%c", cat0->inv ? "!" : "",
                                component[cat0->comp]);
                break;
@@ -271,20 +292,22 @@ static void print_instr_cat2(struct disasm_ctx *ctx, instr_t *instr)
        print_reg_dst(ctx, (reg_t)(cat2->dst), cat2->full ^ cat2->dst_half, false);
        fprintf(ctx->out, ", ");
 
+       unsigned src1_r = cat2->repeat ? cat2->src1_r : 0;
        if (cat2->c1.src1_c) {
-               print_reg_src(ctx, (reg_t)(cat2->c1.src1), cat2->full, cat2->src1_r,
+               print_reg_src(ctx, (reg_t)(cat2->c1.src1), cat2->full, src1_r,
                                cat2->c1.src1_c, cat2->src1_im, cat2->src1_neg,
                                cat2->src1_abs, false);
        } else if (cat2->rel1.src1_rel) {
-               print_reg_src(ctx, (reg_t)(cat2->rel1.src1), cat2->full, cat2->src1_r,
+               print_reg_src(ctx, (reg_t)(cat2->rel1.src1), cat2->full, src1_r,
                                cat2->rel1.src1_c, cat2->src1_im, cat2->src1_neg,
                                cat2->src1_abs, cat2->rel1.src1_rel);
        } else {
-               print_reg_src(ctx, (reg_t)(cat2->src1), cat2->full, cat2->src1_r,
+               print_reg_src(ctx, (reg_t)(cat2->src1), cat2->full, src1_r,
                                false, cat2->src1_im, cat2->src1_neg,
                                cat2->src1_abs, false);
        }
 
+       unsigned src2_r = cat2->repeat ? cat2->src2_r : 0;
        switch (_OPC(2, cat2->opc)) {
        case OPC_ABSNEG_F:
        case OPC_ABSNEG_S:
@@ -305,15 +328,15 @@ static void print_instr_cat2(struct disasm_ctx *ctx, instr_t *instr)
        default:
                fprintf(ctx->out, ", ");
                if (cat2->c2.src2_c) {
-                       print_reg_src(ctx, (reg_t)(cat2->c2.src2), cat2->full, cat2->src2_r,
+                       print_reg_src(ctx, (reg_t)(cat2->c2.src2), cat2->full, src2_r,
                                        cat2->c2.src2_c, cat2->src2_im, cat2->src2_neg,
                                        cat2->src2_abs, false);
                } else if (cat2->rel2.src2_rel) {
-                       print_reg_src(ctx, (reg_t)(cat2->rel2.src2), cat2->full, cat2->src2_r,
+                       print_reg_src(ctx, (reg_t)(cat2->rel2.src2), cat2->full, src2_r,
                                        cat2->rel2.src2_c, cat2->src2_im, cat2->src2_neg,
                                        cat2->src2_abs, cat2->rel2.src2_rel);
                } else {
-                       print_reg_src(ctx, (reg_t)(cat2->src2), cat2->full, cat2->src2_r,
+                       print_reg_src(ctx, (reg_t)(cat2->src2), cat2->full, src2_r,
                                        false, cat2->src2_im, cat2->src2_neg,
                                        cat2->src2_abs, false);
                }
@@ -329,22 +352,24 @@ static void print_instr_cat3(struct disasm_ctx *ctx, instr_t *instr)
        fprintf(ctx->out, " ");
        print_reg_dst(ctx, (reg_t)(cat3->dst), full ^ cat3->dst_half, false);
        fprintf(ctx->out, ", ");
+       unsigned src1_r = cat3->repeat ? cat3->src1_r : 0;
        if (cat3->c1.src1_c) {
                print_reg_src(ctx, (reg_t)(cat3->c1.src1), full,
-                               cat3->src1_r, cat3->c1.src1_c, false, cat3->src1_neg,
+                               src1_r, cat3->c1.src1_c, false, cat3->src1_neg,
                                false, false);
        } else if (cat3->rel1.src1_rel) {
                print_reg_src(ctx, (reg_t)(cat3->rel1.src1), full,
-                               cat3->src1_r, cat3->rel1.src1_c, false, cat3->src1_neg,
+                               src1_r, cat3->rel1.src1_c, false, cat3->src1_neg,
                                false, cat3->rel1.src1_rel);
        } else {
                print_reg_src(ctx, (reg_t)(cat3->src1), full,
-                               cat3->src1_r, false, false, cat3->src1_neg,
+                               src1_r, false, false, cat3->src1_neg,
                                false, false);
        }
        fprintf(ctx->out, ", ");
+       unsigned src2_r = cat3->repeat ? cat3->src2_r : 0;
        print_reg_src(ctx, (reg_t)cat3->src2, full,
-                       cat3->src2_r, cat3->src2_c, false, cat3->src2_neg,
+                       src2_r, cat3->src2_c, false, cat3->src2_neg,
                        false, false);
        fprintf(ctx->out, ", ");
        if (cat3->c2.src3_c) {
@@ -419,7 +444,7 @@ static void print_instr_cat5(struct disasm_ctx *ctx, instr_t *instr)
                        [opc_op(OPC_SAMGP3)]   = { true,  false, true,  true,  },
                        [opc_op(OPC_DSXPP_1)]  = { true,  false, false, false, },
                        [opc_op(OPC_DSYPP_1)]  = { true,  false, false, false, },
-                       [opc_op(OPC_RGETPOS)]  = { false, false, false, false, },
+                       [opc_op(OPC_RGETPOS)]  = { true,  false, false, false, },
                        [opc_op(OPC_RGETINFO)] = { false, false, false, false, },
        };
        instr_cat5_t *cat5 = &instr->cat5;
@@ -458,9 +483,11 @@ static void print_instr_cat5(struct disasm_ctx *ctx, instr_t *instr)
        }
 
        if (cat5->is_s2en) {
-               fprintf(ctx->out, ", ");
-               print_reg_src(ctx, (reg_t)(cat5->s2en.src2), cat5->full, false, false, false,
-                               false, false, false);
+               if (cat5->is_o || info[cat5->opc].src2) {
+                       fprintf(ctx->out, ", ");
+                       print_reg_src(ctx, (reg_t)(cat5->s2en.src2), cat5->full,
+                                       false, false, false, false, false, false);
+               }
                fprintf(ctx->out, ", ");
                print_reg_src(ctx, (reg_t)(cat5->s2en.src3), false, false, false, false,
                                false, false, false);
@@ -487,7 +514,7 @@ static void print_instr_cat5(struct disasm_ctx *ctx, instr_t *instr)
        }
 }
 
-static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
+static void print_instr_cat6_a3xx(struct disasm_ctx *ctx, instr_t *instr)
 {
        instr_cat6_t *cat6 = &instr->cat6;
        char sd = 0, ss = 0;  /* dst/src address space */
@@ -515,7 +542,6 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
        case OPC_STG:
        case OPC_STL:
        case OPC_STP:
-       case OPC_STI:
        case OPC_STLW:
        case OPC_STIB:
                dst.full  = true;
@@ -612,10 +638,6 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
                ss = 'g';
                nodst = true;
                break;
-
-       case OPC_STI:
-               dst.full = false;  // XXX or inverts??
-               break;
        }
 
        if ((_OPC(6, cat6->opc) == OPC_STGB) || (_OPC(6, cat6->opc) == OPC_STIB)) {
@@ -719,6 +741,26 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
                if (debug & PRINT_VERBOSE)
                        fprintf(ctx->out, " (pad0=%x, pad3=%x, mustbe0=%x)", cat6->ldgb.pad0, cat6->ldgb.pad3, cat6->ldgb.mustbe0);
 
+               return;
+       } else if (_OPC(6, cat6->opc) == OPC_LDG && cat6->a.src1_im && cat6->a.src2_im) {
+               struct reginfo src3;
+
+               memset(&src3, 0, sizeof(src3));
+               src1.reg = (reg_t)(cat6->a.src1);
+               src2.reg = (reg_t)(cat6->a.src2);
+               src2.im  = cat6->a.src2_im;
+               src3.reg = (reg_t)(cat6->a.off);
+               src3.full = true;
+               dst.reg  = (reg_t)(cat6->d.dst);
+
+               print_src(ctx, &dst);
+               fprintf(ctx->out, ", g[");
+               print_src(ctx, &src1);
+               fprintf(ctx->out, "+");
+               print_src(ctx, &src3);
+               fprintf(ctx->out, "], ");
+               print_src(ctx, &src2);
+
                return;
        }
        if (cat6->dst_off) {
@@ -746,7 +788,13 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
                        fprintf(ctx->out, "%c[", sd);
                /* note: dst might actually be a src (ie. address to store to) */
                print_src(ctx, &dst);
-               if (dstoff)
+               if (cat6->dst_off && cat6->g) {
+                       struct reginfo dstoff_reg = {0};
+                       dstoff_reg.reg = (reg_t) cat6->c.off;
+                       dstoff_reg.full  = true;
+                       fprintf(ctx->out, "+");
+                       print_src(ctx, &dstoff_reg);
+               } else if (dstoff)
                        fprintf(ctx->out, "%+d", dstoff);
                if (sd)
                        fprintf(ctx->out, "]");
@@ -763,7 +811,9 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
                print_src(ctx, &src1);
        }
 
-       if (src1off)
+       if (cat6->src_off && cat6->g)
+               print_src(ctx, &src2);
+       else if (src1off)
                fprintf(ctx->out, "%+d", src1off);
        if (ss)
                fprintf(ctx->out, "]");
@@ -779,6 +829,64 @@ static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
        }
 }
 
+static void print_instr_cat6_a6xx(struct disasm_ctx *ctx, instr_t *instr)
+{
+       instr_cat6_a6xx_t *cat6 = &instr->cat6_a6xx;
+       struct reginfo src1, src2;
+       bool has_dest = _OPC(6, cat6->opc) == OPC_LDIB;
+       char ss = 0;
+
+       memset(&src1, 0, sizeof(src1));
+       memset(&src2, 0, sizeof(src2));
+
+       fprintf(ctx->out, ".%s", cat6->typed ? "typed" : "untyped");
+       fprintf(ctx->out, ".%dd", cat6->d + 1);
+       fprintf(ctx->out, ".%s", type[cat6->type]);
+       fprintf(ctx->out, ".%u ", cat6->type_size + 1);
+
+       if (has_dest) {
+               src2.reg = (reg_t)(cat6->src2);
+               src2.full = true; // XXX
+               print_src(ctx, &src2);
+
+               fprintf(ctx->out, ", ");
+       }
+
+       /* NOTE: blob seems to use old encoding for ldl/stl (local memory) */
+       ss = 'g';
+
+       fprintf(ctx->out, "%c[%u", ss, cat6->ssbo);
+       fprintf(ctx->out, "] + ");
+       src1.reg = (reg_t)(cat6->src1);
+       src1.full = true; // XXX
+       print_src(ctx, &src1);
+
+       if (!has_dest) {
+               fprintf(ctx->out, ", ");
+
+               src2.reg = (reg_t)(cat6->src2);
+               src2.full = true; // XXX
+               print_src(ctx, &src2);
+       }
+
+       if (debug & PRINT_VERBOSE) {
+               fprintf(ctx->out, " (pad1=%x, pad2=%x, pad3=%x, pad4=%x)", cat6->pad1,
+                               cat6->pad2, cat6->pad3, cat6->pad4);
+       }
+}
+
+static void print_instr_cat6(struct disasm_ctx *ctx, instr_t *instr)
+{
+       if (!is_cat6_legacy(instr, ctx->gpu_id)) {
+               print_instr_cat6_a6xx(ctx, instr);
+               if (debug & PRINT_VERBOSE)
+                       fprintf(ctx->out, " NEW");
+       } else {
+               print_instr_cat6_a3xx(ctx, instr);
+               if (debug & PRINT_VERBOSE)
+                       fprintf(ctx->out, " LEGACY");
+       }
+}
 static void print_instr_cat7(struct disasm_ctx *ctx, instr_t *instr)
 {
        instr_cat7_t *cat7 = &instr->cat7;
@@ -819,6 +927,8 @@ static const struct opc_info {
        OPC(0, OPC_CHMASK,       chmask),
        OPC(0, OPC_CHSH,         chsh),
        OPC(0, OPC_FLOW_REV,     flow_rev),
+       OPC(0, OPC_CONDEND,      condend),
+       OPC(0, OPC_ENDPATCH,     endpatch),
 
        /* category 1: */
        OPC(1, OPC_MOV, ),
@@ -854,8 +964,8 @@ static const struct opc_info {
        OPC(2, OPC_XOR_B,        xor.b),
        OPC(2, OPC_CMPV_U,       cmpv.u),
        OPC(2, OPC_CMPV_S,       cmpv.s),
-       OPC(2, OPC_MUL_U,        mul.u),
-       OPC(2, OPC_MUL_S,        mul.s),
+       OPC(2, OPC_MUL_U24,      mul.u24),
+       OPC(2, OPC_MUL_S24,      mul.s24),
        OPC(2, OPC_MULL_U,       mull.u),
        OPC(2, OPC_BFREV_B,      bfrev.b),
        OPC(2, OPC_CLZ_S,        clz.s),
@@ -897,6 +1007,9 @@ static const struct opc_info {
        OPC(4, OPC_SIN,          sin),
        OPC(4, OPC_COS,          cos),
        OPC(4, OPC_SQRT,         sqrt),
+       OPC(4, OPC_HRSQ,         hrsq),
+       OPC(4, OPC_HLOG2,        hlog2),
+       OPC(4, OPC_HEXP2,        hexp2),
 
        /* category 5: */
        OPC(5, OPC_ISAM,         isam),
@@ -936,7 +1049,7 @@ static const struct opc_info {
        OPC(6, OPC_STG,          stg),
        OPC(6, OPC_STL,          stl),
        OPC(6, OPC_STP,          stp),
-       OPC(6, OPC_STI,          sti),
+       OPC(6, OPC_LDIB,         ldib),
        OPC(6, OPC_G2L,          g2l),
        OPC(6, OPC_L2G,          l2g),
        OPC(6, OPC_PREFETCH,     prefetch),
@@ -967,7 +1080,7 @@ static const struct opc_info {
 #undef OPC
 };
 
-#define GETINFO(instr) (&(opcs[((instr)->opc_cat << NOPC_BITS) | instr_opc(instr)]))
+#define GETINFO(instr) (&(opcs[((instr)->opc_cat << NOPC_BITS) | instr_opc(instr, ctx->gpu_id)]))
 
 // XXX hack.. probably should move this table somewhere common:
 #include "ir3.h"
@@ -977,11 +1090,35 @@ const char *ir3_instr_name(struct ir3_instruction *instr)
        return opcs[instr->opc].name;
 }
 
+static void print_single_instr(struct disasm_ctx *ctx, instr_t *instr)
+{
+       const char *name = GETINFO(instr)->name;
+       uint32_t opc = instr_opc(instr, ctx->gpu_id);
+
+       if (name) {
+               fprintf(ctx->out, "%s", name);
+               GETINFO(instr)->print(ctx, instr);
+       } else {
+               fprintf(ctx->out, "unknown(%d,%d)", instr->opc_cat, opc);
+
+               switch (instr->opc_cat) {
+               case 0: print_instr_cat0(ctx, instr); break;
+               case 1: print_instr_cat1(ctx, instr); break;
+               case 2: print_instr_cat2(ctx, instr); break;
+               case 3: print_instr_cat3(ctx, instr); break;
+               case 4: print_instr_cat4(ctx, instr); break;
+               case 5: print_instr_cat5(ctx, instr); break;
+               case 6: print_instr_cat6(ctx, instr); break;
+               case 7: print_instr_cat7(ctx, instr); break;
+               }
+       }
+}
+
 static bool print_instr(struct disasm_ctx *ctx, uint32_t *dwords, int n)
 {
        instr_t *instr = (instr_t *)dwords;
-       uint32_t opc = instr_opc(instr);
-       const char *name;
+       uint32_t opc = instr_opc(instr, ctx->gpu_id);
+       unsigned nop = 0;
 
        if (debug & PRINT_VERBOSE)
                fprintf(ctx->out, "%s%04d[%08xx_%08xx] ", levels[ctx->level], n, dwords[1], dwords[0]);
@@ -993,46 +1130,72 @@ static bool print_instr(struct disasm_ctx *ctx, uint32_t *dwords, int n)
 
        ctx->repeat = instr_repeat(instr);
 
-       if (instr->sync)
+       if (instr->sync) {
                fprintf(ctx->out, "(sy)");
-       if (instr->ss && ((instr->opc_cat <= 4) || (instr->opc_cat == 7)))
+       }
+       if (instr->ss && ((instr->opc_cat <= 4) || (instr->opc_cat == 7))) {
                fprintf(ctx->out, "(ss)");
+       }
        if (instr->jmp_tgt)
                fprintf(ctx->out, "(jp)");
        if (instr_sat(instr))
                fprintf(ctx->out, "(sat)");
        if (ctx->repeat)
                fprintf(ctx->out, "(rpt%d)", ctx->repeat);
+       else if ((instr->opc_cat == 2) && (instr->cat2.src1_r || instr->cat2.src2_r))
+               nop = (instr->cat2.src2_r * 2) + instr->cat2.src1_r;
+       else if ((instr->opc_cat == 3) && (instr->cat3.src1_r || instr->cat3.src2_r))
+               nop = (instr->cat3.src2_r * 2) + instr->cat3.src1_r;
+       if (nop)
+               fprintf(ctx->out, "(nop%d)", nop);
+
        if (instr->ul && ((2 <= instr->opc_cat) && (instr->opc_cat <= 4)))
                fprintf(ctx->out, "(ul)");
 
-       name = GETINFO(instr)->name;
+       print_single_instr(ctx, instr);
+       fprintf(ctx->out, "\n");
 
-       if (name) {
-               fprintf(ctx->out, "%s", name);
-               GETINFO(instr)->print(ctx, instr);
-       } else {
-               fprintf(ctx->out, "unknown(%d,%d)", instr->opc_cat, opc);
-       }
+       if ((instr->opc_cat <= 4) && (debug & EXPAND_REPEAT)) {
+               int i;
+               for (i = 0; i < nop; i++) {
+                       fprintf(ctx->out, "%s%04d[                   ] ", levels[ctx->level], n);
+                       fprintf(ctx->out, "nop\n");
+               }
+               for (i = 0; i < ctx->repeat; i++) {
+                       ctx->repeatidx = i + 1;
+                       fprintf(ctx->out, "%s%04d[                   ] ", levels[ctx->level], n);
 
-       fprintf(ctx->out, "\n");
+                       print_single_instr(ctx, instr);
+                       fprintf(ctx->out, "\n");
+               }
+               ctx->repeatidx = 0;
+       }
 
        return (instr->opc_cat == 0) && (opc == OPC_END);
 }
 
-int disasm_a3xx(uint32_t *dwords, int sizedwords, int level, FILE *out)
+int disasm_a3xx(uint32_t *dwords, int sizedwords, int level, FILE *out, unsigned gpu_id)
 {
        struct disasm_ctx ctx;
        int i;
+       int nop_count = 0;
 
        assert((sizedwords % 2) == 0);
 
        memset(&ctx, 0, sizeof(ctx));
        ctx.out = out;
        ctx.level = level;
+       ctx.gpu_id = gpu_id;
 
-       for (i = 0; i < sizedwords; i += 2)
+       for (i = 0; i < sizedwords; i += 2) {
                print_instr(&ctx, &dwords[i], i/2);
+               if (dwords[i] == 0 && dwords[i + 1] == 0)
+                       nop_count++;
+               else
+                       nop_count = 0;
+               if (nop_count > 3)
+                       break;
+       }
 
        return 0;
 }