enum debug_t {
PRINT_RAW = 0x1, /* dump raw hexdump */
PRINT_VERBOSE = 0x2,
+ EXPAND_REPEAT = 0x4,
};
static enum debug_t debug;
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,
} 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);
}
switch (cat0->opc) {
case OPC_KILL:
+ case OPC_CONDEND:
fprintf(ctx->out, " %sp0.%c", cat0->inv ? "!" : "",
component[cat0->comp]);
break;
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:
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);
}
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) {
[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;
}
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);
}
}
-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 */
case OPC_STG:
case OPC_STL:
case OPC_STP:
- case OPC_STI:
case OPC_STLW:
case OPC_STIB:
dst.full = true;
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)) {
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) {
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, "]");
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, "]");
}
}
+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;
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, ),
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),
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),
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),
#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"
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]);
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;
}