2 * Copyright (c) 2013 Rob Clark <robdclark@gmail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 #include <util/u_debug.h>
33 #include "instr-a3xx.h"
35 /* bitmask of debug flags */
37 PRINT_RAW
= 0x1, /* dump raw hexdump */
41 static enum debug_t debug
;
43 #define printf debug_printf
45 static const char *levels
[] = {
64 static const char *component
= "xyzw";
66 static const char *type
[] = {
82 /* current instruction repeat flag: */
86 static void print_reg(struct disasm_ctx
*ctx
, reg_t reg
, bool full
, bool r
,
87 bool c
, bool im
, bool neg
, bool abs
, bool addr_rel
)
89 const char type
= c
? 'c' : 'r';
91 // XXX I prefer - and || for neg/abs, but preserving format used
92 // by libllvm-a3xx for easy diffing..
95 fprintf(ctx
->out
, "(absneg)");
97 fprintf(ctx
->out
, "(neg)");
99 fprintf(ctx
->out
, "(abs)");
102 fprintf(ctx
->out
, "(r)");
105 fprintf(ctx
->out
, "%d", reg
.iim_val
);
106 } else if (addr_rel
) {
107 /* I would just use %+d but trying to make it diff'able with
111 fprintf(ctx
->out
, "%s%c<a0.x - %d>", full
? "" : "h", type
, -reg
.iim_val
);
112 else if (reg
.iim_val
> 0)
113 fprintf(ctx
->out
, "%s%c<a0.x + %d>", full
? "" : "h", type
, reg
.iim_val
);
115 fprintf(ctx
->out
, "%s%c<a0.x>", full
? "" : "h", type
);
116 } else if ((reg
.num
== REG_A0
) && !c
) {
117 fprintf(ctx
->out
, "a0.%c", component
[reg
.comp
]);
118 } else if ((reg
.num
== REG_P0
) && !c
) {
119 fprintf(ctx
->out
, "p0.%c", component
[reg
.comp
]);
121 fprintf(ctx
->out
, "%s%c%d.%c", full
? "" : "h", type
, reg
.num
, component
[reg
.comp
]);
126 static void print_reg_dst(struct disasm_ctx
*ctx
, reg_t reg
, bool full
, bool addr_rel
)
128 print_reg(ctx
, reg
, full
, false, false, false, false, false, addr_rel
);
131 static void print_reg_src(struct disasm_ctx
*ctx
, reg_t reg
, bool full
, bool r
,
132 bool c
, bool im
, bool neg
, bool abs
, bool addr_rel
)
134 print_reg(ctx
, reg
, full
, r
, c
, im
, neg
, abs
, addr_rel
);
137 /* TODO switch to using reginfo struct everywhere, since more readable
138 * than passing a bunch of bools to print_reg_src
152 static void print_src(struct disasm_ctx
*ctx
, struct reginfo
*info
)
154 print_reg_src(ctx
, info
->reg
, info
->full
, info
->r
, info
->c
, info
->im
,
155 info
->neg
, info
->abs
, info
->addr_rel
);
158 //static void print_dst(struct disasm_ctx *ctx, struct reginfo *info)
160 // print_reg_dst(ctx, info->reg, info->full, info->addr_rel);
163 static void print_instr_cat0(struct disasm_ctx
*ctx
, instr_t
*instr
)
165 instr_cat0_t
*cat0
= &instr
->cat0
;
169 fprintf(ctx
->out
, " %sp0.%c", cat0
->inv
? "!" : "",
170 component
[cat0
->comp
]);
173 fprintf(ctx
->out
, " %sp0.%c, #%d", cat0
->inv
? "!" : "",
174 component
[cat0
->comp
], cat0
->a3xx
.immed
);
178 fprintf(ctx
->out
, " #%d", cat0
->a3xx
.immed
);
182 if ((debug
& PRINT_VERBOSE
) && (cat0
->dummy2
|cat0
->dummy3
|cat0
->dummy4
))
183 fprintf(ctx
->out
, "\t{0: %x,%x,%x}", cat0
->dummy2
, cat0
->dummy3
, cat0
->dummy4
);
186 static void print_instr_cat1(struct disasm_ctx
*ctx
, instr_t
*instr
)
188 instr_cat1_t
*cat1
= &instr
->cat1
;
191 fprintf(ctx
->out
, "(ul)");
193 if (cat1
->src_type
== cat1
->dst_type
) {
194 if ((cat1
->src_type
== TYPE_S16
) && (((reg_t
)cat1
->dst
).num
== REG_A0
)) {
195 /* special case (nmemonic?): */
196 fprintf(ctx
->out
, "mova");
198 fprintf(ctx
->out
, "mov.%s%s", type
[cat1
->src_type
], type
[cat1
->dst_type
]);
201 fprintf(ctx
->out
, "cov.%s%s", type
[cat1
->src_type
], type
[cat1
->dst_type
]);
204 fprintf(ctx
->out
, " ");
207 fprintf(ctx
->out
, "(even)");
210 fprintf(ctx
->out
, "(pos_infinity)");
212 print_reg_dst(ctx
, (reg_t
)(cat1
->dst
), type_size(cat1
->dst_type
) == 32,
215 fprintf(ctx
->out
, ", ");
217 /* ugg, have to special case this.. vs print_reg().. */
219 if (type_float(cat1
->src_type
))
220 fprintf(ctx
->out
, "(%f)", cat1
->fim_val
);
221 else if (type_uint(cat1
->src_type
))
222 fprintf(ctx
->out
, "0x%08x", cat1
->uim_val
);
224 fprintf(ctx
->out
, "%d", cat1
->iim_val
);
225 } else if (cat1
->src_rel
&& !cat1
->src_c
) {
226 /* I would just use %+d but trying to make it diff'able with
229 char type
= cat1
->src_rel_c
? 'c' : 'r';
231 fprintf(ctx
->out
, "%c<a0.x - %d>", type
, -cat1
->off
);
232 else if (cat1
->off
> 0)
233 fprintf(ctx
->out
, "%c<a0.x + %d>", type
, cat1
->off
);
235 fprintf(ctx
->out
, "%c<a0.x>", type
);
237 print_reg_src(ctx
, (reg_t
)(cat1
->src
), type_size(cat1
->src_type
) == 32,
238 cat1
->src_r
, cat1
->src_c
, cat1
->src_im
, false, false, false);
241 if ((debug
& PRINT_VERBOSE
) && (cat1
->must_be_0
))
242 fprintf(ctx
->out
, "\t{1: %x}", cat1
->must_be_0
);
245 static void print_instr_cat2(struct disasm_ctx
*ctx
, instr_t
*instr
)
247 instr_cat2_t
*cat2
= &instr
->cat2
;
248 static const char *cond
[] = {
258 switch (_OPC(2, cat2
->opc
)) {
265 fprintf(ctx
->out
, ".%s", cond
[cat2
->cond
]);
269 fprintf(ctx
->out
, " ");
271 fprintf(ctx
->out
, "(ei)");
272 print_reg_dst(ctx
, (reg_t
)(cat2
->dst
), cat2
->full
^ cat2
->dst_half
, false);
273 fprintf(ctx
->out
, ", ");
275 unsigned src1_r
= cat2
->repeat
? cat2
->src1_r
: 0;
276 if (cat2
->c1
.src1_c
) {
277 print_reg_src(ctx
, (reg_t
)(cat2
->c1
.src1
), cat2
->full
, src1_r
,
278 cat2
->c1
.src1_c
, cat2
->src1_im
, cat2
->src1_neg
,
279 cat2
->src1_abs
, false);
280 } else if (cat2
->rel1
.src1_rel
) {
281 print_reg_src(ctx
, (reg_t
)(cat2
->rel1
.src1
), cat2
->full
, src1_r
,
282 cat2
->rel1
.src1_c
, cat2
->src1_im
, cat2
->src1_neg
,
283 cat2
->src1_abs
, cat2
->rel1
.src1_rel
);
285 print_reg_src(ctx
, (reg_t
)(cat2
->src1
), cat2
->full
, src1_r
,
286 false, cat2
->src1_im
, cat2
->src1_neg
,
287 cat2
->src1_abs
, false);
290 unsigned src2_r
= cat2
->repeat
? cat2
->src2_r
: 0;
291 switch (_OPC(2, cat2
->opc
)) {
306 /* these only have one src reg */
309 fprintf(ctx
->out
, ", ");
310 if (cat2
->c2
.src2_c
) {
311 print_reg_src(ctx
, (reg_t
)(cat2
->c2
.src2
), cat2
->full
, src2_r
,
312 cat2
->c2
.src2_c
, cat2
->src2_im
, cat2
->src2_neg
,
313 cat2
->src2_abs
, false);
314 } else if (cat2
->rel2
.src2_rel
) {
315 print_reg_src(ctx
, (reg_t
)(cat2
->rel2
.src2
), cat2
->full
, src2_r
,
316 cat2
->rel2
.src2_c
, cat2
->src2_im
, cat2
->src2_neg
,
317 cat2
->src2_abs
, cat2
->rel2
.src2_rel
);
319 print_reg_src(ctx
, (reg_t
)(cat2
->src2
), cat2
->full
, src2_r
,
320 false, cat2
->src2_im
, cat2
->src2_neg
,
321 cat2
->src2_abs
, false);
327 static void print_instr_cat3(struct disasm_ctx
*ctx
, instr_t
*instr
)
329 instr_cat3_t
*cat3
= &instr
->cat3
;
330 bool full
= instr_cat3_full(cat3
);
332 fprintf(ctx
->out
, " ");
333 print_reg_dst(ctx
, (reg_t
)(cat3
->dst
), full
^ cat3
->dst_half
, false);
334 fprintf(ctx
->out
, ", ");
335 unsigned src1_r
= cat3
->repeat
? cat3
->src1_r
: 0;
336 if (cat3
->c1
.src1_c
) {
337 print_reg_src(ctx
, (reg_t
)(cat3
->c1
.src1
), full
,
338 src1_r
, cat3
->c1
.src1_c
, false, cat3
->src1_neg
,
340 } else if (cat3
->rel1
.src1_rel
) {
341 print_reg_src(ctx
, (reg_t
)(cat3
->rel1
.src1
), full
,
342 src1_r
, cat3
->rel1
.src1_c
, false, cat3
->src1_neg
,
343 false, cat3
->rel1
.src1_rel
);
345 print_reg_src(ctx
, (reg_t
)(cat3
->src1
), full
,
346 src1_r
, false, false, cat3
->src1_neg
,
349 fprintf(ctx
->out
, ", ");
350 unsigned src2_r
= cat3
->repeat
? cat3
->src2_r
: 0;
351 print_reg_src(ctx
, (reg_t
)cat3
->src2
, full
,
352 src2_r
, cat3
->src2_c
, false, cat3
->src2_neg
,
354 fprintf(ctx
->out
, ", ");
355 if (cat3
->c2
.src3_c
) {
356 print_reg_src(ctx
, (reg_t
)(cat3
->c2
.src3
), full
,
357 cat3
->src3_r
, cat3
->c2
.src3_c
, false, cat3
->src3_neg
,
359 } else if (cat3
->rel2
.src3_rel
) {
360 print_reg_src(ctx
, (reg_t
)(cat3
->rel2
.src3
), full
,
361 cat3
->src3_r
, cat3
->rel2
.src3_c
, false, cat3
->src3_neg
,
362 false, cat3
->rel2
.src3_rel
);
364 print_reg_src(ctx
, (reg_t
)(cat3
->src3
), full
,
365 cat3
->src3_r
, false, false, cat3
->src3_neg
,
370 static void print_instr_cat4(struct disasm_ctx
*ctx
, instr_t
*instr
)
372 instr_cat4_t
*cat4
= &instr
->cat4
;
374 fprintf(ctx
->out
, " ");
375 print_reg_dst(ctx
, (reg_t
)(cat4
->dst
), cat4
->full
^ cat4
->dst_half
, false);
376 fprintf(ctx
->out
, ", ");
379 print_reg_src(ctx
, (reg_t
)(cat4
->c
.src
), cat4
->full
,
380 cat4
->src_r
, cat4
->c
.src_c
, cat4
->src_im
,
381 cat4
->src_neg
, cat4
->src_abs
, false);
382 } else if (cat4
->rel
.src_rel
) {
383 print_reg_src(ctx
, (reg_t
)(cat4
->rel
.src
), cat4
->full
,
384 cat4
->src_r
, cat4
->rel
.src_c
, cat4
->src_im
,
385 cat4
->src_neg
, cat4
->src_abs
, cat4
->rel
.src_rel
);
387 print_reg_src(ctx
, (reg_t
)(cat4
->src
), cat4
->full
,
388 cat4
->src_r
, false, cat4
->src_im
,
389 cat4
->src_neg
, cat4
->src_abs
, false);
392 if ((debug
& PRINT_VERBOSE
) && (cat4
->dummy1
|cat4
->dummy2
))
393 fprintf(ctx
->out
, "\t{4: %x,%x}", cat4
->dummy1
, cat4
->dummy2
);
396 static void print_instr_cat5(struct disasm_ctx
*ctx
, instr_t
*instr
)
398 static const struct {
399 bool src1
, src2
, samp
, tex
;
401 [opc_op(OPC_ISAM
)] = { true, false, true, true, },
402 [opc_op(OPC_ISAML
)] = { true, true, true, true, },
403 [opc_op(OPC_ISAMM
)] = { true, false, true, true, },
404 [opc_op(OPC_SAM
)] = { true, false, true, true, },
405 [opc_op(OPC_SAMB
)] = { true, true, true, true, },
406 [opc_op(OPC_SAML
)] = { true, true, true, true, },
407 [opc_op(OPC_SAMGQ
)] = { true, false, true, true, },
408 [opc_op(OPC_GETLOD
)] = { true, false, true, true, },
409 [opc_op(OPC_CONV
)] = { true, true, true, true, },
410 [opc_op(OPC_CONVM
)] = { true, true, true, true, },
411 [opc_op(OPC_GETSIZE
)] = { true, false, false, true, },
412 [opc_op(OPC_GETBUF
)] = { false, false, false, true, },
413 [opc_op(OPC_GETPOS
)] = { true, false, false, true, },
414 [opc_op(OPC_GETINFO
)] = { false, false, false, true, },
415 [opc_op(OPC_DSX
)] = { true, false, false, false, },
416 [opc_op(OPC_DSY
)] = { true, false, false, false, },
417 [opc_op(OPC_GATHER4R
)] = { true, false, true, true, },
418 [opc_op(OPC_GATHER4G
)] = { true, false, true, true, },
419 [opc_op(OPC_GATHER4B
)] = { true, false, true, true, },
420 [opc_op(OPC_GATHER4A
)] = { true, false, true, true, },
421 [opc_op(OPC_SAMGP0
)] = { true, false, true, true, },
422 [opc_op(OPC_SAMGP1
)] = { true, false, true, true, },
423 [opc_op(OPC_SAMGP2
)] = { true, false, true, true, },
424 [opc_op(OPC_SAMGP3
)] = { true, false, true, true, },
425 [opc_op(OPC_DSXPP_1
)] = { true, false, false, false, },
426 [opc_op(OPC_DSYPP_1
)] = { true, false, false, false, },
427 [opc_op(OPC_RGETPOS
)] = { false, false, false, false, },
428 [opc_op(OPC_RGETINFO
)] = { false, false, false, false, },
430 instr_cat5_t
*cat5
= &instr
->cat5
;
433 if (cat5
->is_3d
) fprintf(ctx
->out
, ".3d");
434 if (cat5
->is_a
) fprintf(ctx
->out
, ".a");
435 if (cat5
->is_o
) fprintf(ctx
->out
, ".o");
436 if (cat5
->is_p
) fprintf(ctx
->out
, ".p");
437 if (cat5
->is_s
) fprintf(ctx
->out
, ".s");
438 if (cat5
->is_s2en
) fprintf(ctx
->out
, ".s2en");
440 fprintf(ctx
->out
, " ");
442 switch (_OPC(5, cat5
->opc
)) {
447 fprintf(ctx
->out
, "(%s)", type
[cat5
->type
]);
451 fprintf(ctx
->out
, "(");
452 for (i
= 0; i
< 4; i
++)
453 if (cat5
->wrmask
& (1 << i
))
454 fprintf(ctx
->out
, "%c", "xyzw"[i
]);
455 fprintf(ctx
->out
, ")");
457 print_reg_dst(ctx
, (reg_t
)(cat5
->dst
), type_size(cat5
->type
) == 32, false);
459 if (info
[cat5
->opc
].src1
) {
460 fprintf(ctx
->out
, ", ");
461 print_reg_src(ctx
, (reg_t
)(cat5
->src1
), cat5
->full
, false, false, false,
462 false, false, false);
466 fprintf(ctx
->out
, ", ");
467 print_reg_src(ctx
, (reg_t
)(cat5
->s2en
.src2
), cat5
->full
, false, false, false,
468 false, false, false);
469 fprintf(ctx
->out
, ", ");
470 print_reg_src(ctx
, (reg_t
)(cat5
->s2en
.src3
), false, false, false, false,
471 false, false, false);
473 if (cat5
->is_o
|| info
[cat5
->opc
].src2
) {
474 fprintf(ctx
->out
, ", ");
475 print_reg_src(ctx
, (reg_t
)(cat5
->norm
.src2
), cat5
->full
,
476 false, false, false, false, false, false);
478 if (info
[cat5
->opc
].samp
)
479 fprintf(ctx
->out
, ", s#%d", cat5
->norm
.samp
);
480 if (info
[cat5
->opc
].tex
)
481 fprintf(ctx
->out
, ", t#%d", cat5
->norm
.tex
);
484 if (debug
& PRINT_VERBOSE
) {
486 if ((debug
& PRINT_VERBOSE
) && (cat5
->s2en
.dummy1
|cat5
->s2en
.dummy2
|cat5
->dummy2
))
487 fprintf(ctx
->out
, "\t{5: %x,%x,%x}", cat5
->s2en
.dummy1
, cat5
->s2en
.dummy2
, cat5
->dummy2
);
489 if ((debug
& PRINT_VERBOSE
) && (cat5
->norm
.dummy1
|cat5
->dummy2
))
490 fprintf(ctx
->out
, "\t{5: %x,%x}", cat5
->norm
.dummy1
, cat5
->dummy2
);
495 static void print_instr_cat6_a3xx(struct disasm_ctx
*ctx
, instr_t
*instr
)
497 instr_cat6_t
*cat6
= &instr
->cat6
;
498 char sd
= 0, ss
= 0; /* dst/src address space */
500 struct reginfo dst
, src1
, src2
;
501 int src1off
= 0, dstoff
= 0;
503 memset(&dst
, 0, sizeof(dst
));
504 memset(&src1
, 0, sizeof(src1
));
505 memset(&src2
, 0, sizeof(src2
));
507 switch (_OPC(6, cat6
->opc
)) {
510 dst
.full
= type_size(cat6
->type
) == 32;
511 src1
.full
= type_size(cat6
->type
) == 32;
512 src2
.full
= type_size(cat6
->type
) == 32;
526 src1
.full
= type_size(cat6
->type
) == 32;
527 src2
.full
= type_size(cat6
->type
) == 32;
530 dst
.full
= type_size(cat6
->type
) == 32;
536 switch (_OPC(6, cat6
->opc
)) {
540 fprintf(ctx
->out
, ".%dd", cat6
->ldgb
.d
+ 1);
543 fprintf(ctx
->out
, ".%s", cat6
->ldgb
.typed
? "typed" : "untyped");
544 fprintf(ctx
->out
, ".%dd", cat6
->ldgb
.d
+ 1);
545 fprintf(ctx
->out
, ".%s", type
[cat6
->type
]);
546 fprintf(ctx
->out
, ".%d", cat6
->ldgb
.type_size
+ 1);
550 fprintf(ctx
->out
, ".%s", cat6
->stgb
.typed
? "typed" : "untyped");
551 fprintf(ctx
->out
, ".%dd", cat6
->stgb
.d
+ 1);
552 fprintf(ctx
->out
, ".%s", type
[cat6
->type
]);
553 fprintf(ctx
->out
, ".%d", cat6
->stgb
.type_size
+ 1);
557 case OPC_ATOMIC_XCHG
:
560 case OPC_ATOMIC_CMPXCHG
:
566 ss
= cat6
->g
? 'g' : 'l';
567 fprintf(ctx
->out
, ".%s", cat6
->ldgb
.typed
? "typed" : "untyped");
568 fprintf(ctx
->out
, ".%dd", cat6
->ldgb
.d
+ 1);
569 fprintf(ctx
->out
, ".%s", type
[cat6
->type
]);
570 fprintf(ctx
->out
, ".%d", cat6
->ldgb
.type_size
+ 1);
571 fprintf(ctx
->out
, ".%c", ss
);
574 dst
.im
= cat6
->g
&& !cat6
->dst_off
;
575 fprintf(ctx
->out
, ".%s", type
[cat6
->type
]);
578 fprintf(ctx
->out
, " ");
580 switch (_OPC(6, cat6
->opc
)) {
621 if ((_OPC(6, cat6
->opc
) == OPC_STGB
) || (_OPC(6, cat6
->opc
) == OPC_STIB
)) {
624 memset(&src3
, 0, sizeof(src3
));
626 src1
.reg
= (reg_t
)(cat6
->stgb
.src1
);
627 src2
.reg
= (reg_t
)(cat6
->stgb
.src2
);
628 src2
.im
= cat6
->stgb
.src2_im
;
629 src3
.reg
= (reg_t
)(cat6
->stgb
.src3
);
630 src3
.im
= cat6
->stgb
.src3_im
;
633 fprintf(ctx
->out
, "g[%u], ", cat6
->stgb
.dst_ssbo
);
634 print_src(ctx
, &src1
);
635 fprintf(ctx
->out
, ", ");
636 print_src(ctx
, &src2
);
637 fprintf(ctx
->out
, ", ");
638 print_src(ctx
, &src3
);
640 if (debug
& PRINT_VERBOSE
)
641 fprintf(ctx
->out
, " (pad0=%x, pad3=%x)", cat6
->stgb
.pad0
, cat6
->stgb
.pad3
);
646 if (is_atomic(_OPC(6, cat6
->opc
))) {
648 src1
.reg
= (reg_t
)(cat6
->ldgb
.src1
);
649 src1
.im
= cat6
->ldgb
.src1_im
;
650 src2
.reg
= (reg_t
)(cat6
->ldgb
.src2
);
651 src2
.im
= cat6
->ldgb
.src2_im
;
652 dst
.reg
= (reg_t
)(cat6
->ldgb
.dst
);
654 print_src(ctx
, &dst
);
655 fprintf(ctx
->out
, ", ");
658 memset(&src3
, 0, sizeof(src3
));
660 src3
.reg
= (reg_t
)(cat6
->ldgb
.src3
);
663 /* For images, the ".typed" variant is used and src2 is
664 * the ivecN coordinates, ie ivec2 for 2d.
666 * For SSBOs, the ".untyped" variant is used and src2 is
667 * a simple dword offset.. src3 appears to be
668 * uvec2(offset * 4, 0). Not sure the point of that.
671 fprintf(ctx
->out
, "g[%u], ", cat6
->ldgb
.src_ssbo
);
672 print_src(ctx
, &src1
); /* value */
673 fprintf(ctx
->out
, ", ");
674 print_src(ctx
, &src2
); /* offset/coords */
675 fprintf(ctx
->out
, ", ");
676 print_src(ctx
, &src3
); /* 64b byte offset.. */
678 if (debug
& PRINT_VERBOSE
) {
679 fprintf(ctx
->out
, " (pad0=%x, pad3=%x, mustbe0=%x)", cat6
->ldgb
.pad0
,
680 cat6
->ldgb
.pad3
, cat6
->ldgb
.mustbe0
);
682 } else { /* ss == 'l' */
683 fprintf(ctx
->out
, "l[");
684 print_src(ctx
, &src1
); /* simple byte offset */
685 fprintf(ctx
->out
, "], ");
686 print_src(ctx
, &src2
); /* value */
688 if (debug
& PRINT_VERBOSE
) {
689 fprintf(ctx
->out
, " (src3=%x, pad0=%x, pad3=%x, mustbe0=%x)",
690 cat6
->ldgb
.src3
, cat6
->ldgb
.pad0
,
691 cat6
->ldgb
.pad3
, cat6
->ldgb
.mustbe0
);
696 } else if (_OPC(6, cat6
->opc
) == OPC_RESINFO
) {
697 dst
.reg
= (reg_t
)(cat6
->ldgb
.dst
);
699 print_src(ctx
, &dst
);
700 fprintf(ctx
->out
, ", ");
701 fprintf(ctx
->out
, "g[%u]", cat6
->ldgb
.src_ssbo
);
704 } else if (_OPC(6, cat6
->opc
) == OPC_LDGB
) {
706 src1
.reg
= (reg_t
)(cat6
->ldgb
.src1
);
707 src1
.im
= cat6
->ldgb
.src1_im
;
708 src2
.reg
= (reg_t
)(cat6
->ldgb
.src2
);
709 src2
.im
= cat6
->ldgb
.src2_im
;
710 dst
.reg
= (reg_t
)(cat6
->ldgb
.dst
);
712 print_src(ctx
, &dst
);
713 fprintf(ctx
->out
, ", ");
714 fprintf(ctx
->out
, "g[%u], ", cat6
->ldgb
.src_ssbo
);
715 print_src(ctx
, &src1
);
716 fprintf(ctx
->out
, ", ");
717 print_src(ctx
, &src2
);
719 if (debug
& PRINT_VERBOSE
)
720 fprintf(ctx
->out
, " (pad0=%x, pad3=%x, mustbe0=%x)", cat6
->ldgb
.pad0
, cat6
->ldgb
.pad3
, cat6
->ldgb
.mustbe0
);
725 dst
.reg
= (reg_t
)(cat6
->c
.dst
);
726 dstoff
= cat6
->c
.off
;
728 dst
.reg
= (reg_t
)(cat6
->d
.dst
);
732 src1
.reg
= (reg_t
)(cat6
->a
.src1
);
733 src1
.im
= cat6
->a
.src1_im
;
734 src2
.reg
= (reg_t
)(cat6
->a
.src2
);
735 src2
.im
= cat6
->a
.src2_im
;
736 src1off
= cat6
->a
.off
;
738 src1
.reg
= (reg_t
)(cat6
->b
.src1
);
739 src1
.im
= cat6
->b
.src1_im
;
740 src2
.reg
= (reg_t
)(cat6
->b
.src2
);
741 src2
.im
= cat6
->b
.src2_im
;
746 fprintf(ctx
->out
, "%c[", sd
);
747 /* note: dst might actually be a src (ie. address to store to) */
748 print_src(ctx
, &dst
);
750 fprintf(ctx
->out
, "%+d", dstoff
);
752 fprintf(ctx
->out
, "]");
753 fprintf(ctx
->out
, ", ");
757 fprintf(ctx
->out
, "%c[", ss
);
759 /* can have a larger than normal immed, so hack: */
761 fprintf(ctx
->out
, "%u", src1
.reg
.dummy13
);
763 print_src(ctx
, &src1
);
767 fprintf(ctx
->out
, "%+d", src1off
);
769 fprintf(ctx
->out
, "]");
771 switch (_OPC(6, cat6
->opc
)) {
776 fprintf(ctx
->out
, ", ");
777 print_src(ctx
, &src2
);
782 static void print_instr_cat6_a6xx(struct disasm_ctx
*ctx
, instr_t
*instr
)
784 instr_cat6_a6xx_t
*cat6
= &instr
->cat6_a6xx
;
785 struct reginfo src1
, src2
;
786 bool has_dest
= _OPC(6, cat6
->opc
) == OPC_LDIB
;
789 memset(&src1
, 0, sizeof(src1
));
790 memset(&src2
, 0, sizeof(src2
));
792 fprintf(ctx
->out
, ".%s", cat6
->typed
? "typed" : "untyped");
793 fprintf(ctx
->out
, ".%dd", cat6
->d
+ 1);
794 fprintf(ctx
->out
, ".%s", type
[cat6
->type
]);
795 fprintf(ctx
->out
, ".%u ", cat6
->type_size
+ 1);
798 src2
.reg
= (reg_t
)(cat6
->src2
);
799 src2
.full
= true; // XXX
800 print_src(ctx
, &src2
);
802 fprintf(ctx
->out
, ", ");
805 /* NOTE: blob seems to use old encoding for ldl/stl (local memory) */
808 fprintf(ctx
->out
, "%c[%u", ss
, cat6
->ssbo
);
809 fprintf(ctx
->out
, "] + ");
810 src1
.reg
= (reg_t
)(cat6
->src1
);
811 src1
.full
= true; // XXX
812 print_src(ctx
, &src1
);
815 fprintf(ctx
->out
, ", ");
817 src2
.reg
= (reg_t
)(cat6
->src2
);
818 src2
.full
= true; // XXX
819 print_src(ctx
, &src2
);
822 if (debug
& PRINT_VERBOSE
) {
823 fprintf(ctx
->out
, " (pad1=%x, pad2=%x, pad3=%x, pad4=%x)", cat6
->pad1
,
824 cat6
->pad2
, cat6
->pad3
, cat6
->pad4
);
828 static void print_instr_cat6(struct disasm_ctx
*ctx
, instr_t
*instr
)
830 if (!is_cat6_legacy(instr
, ctx
->gpu_id
)) {
831 print_instr_cat6_a6xx(ctx
, instr
);
832 if (debug
& PRINT_VERBOSE
)
833 fprintf(ctx
->out
, " NEW");
835 print_instr_cat6_a3xx(ctx
, instr
);
836 if (debug
& PRINT_VERBOSE
)
837 fprintf(ctx
->out
, " LEGACY");
840 static void print_instr_cat7(struct disasm_ctx
*ctx
, instr_t
*instr
)
842 instr_cat7_t
*cat7
= &instr
->cat7
;
845 fprintf(ctx
->out
, ".g");
847 fprintf(ctx
->out
, ".l");
849 if (_OPC(7, cat7
->opc
) == OPC_FENCE
) {
851 fprintf(ctx
->out
, ".r");
853 fprintf(ctx
->out
, ".w");
857 /* size of largest OPC field of all the instruction categories: */
860 static const struct opc_info
{
864 void (*print
)(struct disasm_ctx
*ctx
, instr_t
*instr
);
865 } opcs
[1 << (3+NOPC_BITS
)] = {
866 #define OPC(cat, opc, name) [(opc)] = { (cat), (opc), #name, print_instr_cat##cat }
868 OPC(0, OPC_NOP
, nop
),
870 OPC(0, OPC_JUMP
, jump
),
871 OPC(0, OPC_CALL
, call
),
872 OPC(0, OPC_RET
, ret
),
873 OPC(0, OPC_KILL
, kill
),
874 OPC(0, OPC_END
, end
),
875 OPC(0, OPC_EMIT
, emit
),
876 OPC(0, OPC_CUT
, cut
),
877 OPC(0, OPC_CHMASK
, chmask
),
878 OPC(0, OPC_CHSH
, chsh
),
879 OPC(0, OPC_FLOW_REV
, flow_rev
),
885 OPC(2, OPC_ADD_F
, add
.f
),
886 OPC(2, OPC_MIN_F
, min
.f
),
887 OPC(2, OPC_MAX_F
, max
.f
),
888 OPC(2, OPC_MUL_F
, mul
.f
),
889 OPC(2, OPC_SIGN_F
, sign
.f
),
890 OPC(2, OPC_CMPS_F
, cmps
.f
),
891 OPC(2, OPC_ABSNEG_F
, absneg
.f
),
892 OPC(2, OPC_CMPV_F
, cmpv
.f
),
893 OPC(2, OPC_FLOOR_F
, floor
.f
),
894 OPC(2, OPC_CEIL_F
, ceil
.f
),
895 OPC(2, OPC_RNDNE_F
, rndne
.f
),
896 OPC(2, OPC_RNDAZ_F
, rndaz
.f
),
897 OPC(2, OPC_TRUNC_F
, trunc
.f
),
898 OPC(2, OPC_ADD_U
, add
.u
),
899 OPC(2, OPC_ADD_S
, add
.s
),
900 OPC(2, OPC_SUB_U
, sub
.u
),
901 OPC(2, OPC_SUB_S
, sub
.s
),
902 OPC(2, OPC_CMPS_U
, cmps
.u
),
903 OPC(2, OPC_CMPS_S
, cmps
.s
),
904 OPC(2, OPC_MIN_U
, min
.u
),
905 OPC(2, OPC_MIN_S
, min
.s
),
906 OPC(2, OPC_MAX_U
, max
.u
),
907 OPC(2, OPC_MAX_S
, max
.s
),
908 OPC(2, OPC_ABSNEG_S
, absneg
.s
),
909 OPC(2, OPC_AND_B
, and.b
),
910 OPC(2, OPC_OR_B
, or.b
),
911 OPC(2, OPC_NOT_B
, not.b
),
912 OPC(2, OPC_XOR_B
, xor.b
),
913 OPC(2, OPC_CMPV_U
, cmpv
.u
),
914 OPC(2, OPC_CMPV_S
, cmpv
.s
),
915 OPC(2, OPC_MUL_U
, mul
.u
),
916 OPC(2, OPC_MUL_S
, mul
.s
),
917 OPC(2, OPC_MULL_U
, mull
.u
),
918 OPC(2, OPC_BFREV_B
, bfrev
.b
),
919 OPC(2, OPC_CLZ_S
, clz
.s
),
920 OPC(2, OPC_CLZ_B
, clz
.b
),
921 OPC(2, OPC_SHL_B
, shl
.b
),
922 OPC(2, OPC_SHR_B
, shr
.b
),
923 OPC(2, OPC_ASHR_B
, ashr
.b
),
924 OPC(2, OPC_BARY_F
, bary
.f
),
925 OPC(2, OPC_MGEN_B
, mgen
.b
),
926 OPC(2, OPC_GETBIT_B
, getbit
.b
),
927 OPC(2, OPC_SETRM
, setrm
),
928 OPC(2, OPC_CBITS_B
, cbits
.b
),
929 OPC(2, OPC_SHB
, shb
),
930 OPC(2, OPC_MSAD
, msad
),
933 OPC(3, OPC_MAD_U16
, mad
.u16
),
934 OPC(3, OPC_MADSH_U16
, madsh
.u16
),
935 OPC(3, OPC_MAD_S16
, mad
.s16
),
936 OPC(3, OPC_MADSH_M16
, madsh
.m16
),
937 OPC(3, OPC_MAD_U24
, mad
.u24
),
938 OPC(3, OPC_MAD_S24
, mad
.s24
),
939 OPC(3, OPC_MAD_F16
, mad
.f16
),
940 OPC(3, OPC_MAD_F32
, mad
.f32
),
941 OPC(3, OPC_SEL_B16
, sel
.b16
),
942 OPC(3, OPC_SEL_B32
, sel
.b32
),
943 OPC(3, OPC_SEL_S16
, sel
.s16
),
944 OPC(3, OPC_SEL_S32
, sel
.s32
),
945 OPC(3, OPC_SEL_F16
, sel
.f16
),
946 OPC(3, OPC_SEL_F32
, sel
.f32
),
947 OPC(3, OPC_SAD_S16
, sad
.s16
),
948 OPC(3, OPC_SAD_S32
, sad
.s32
),
951 OPC(4, OPC_RCP
, rcp
),
952 OPC(4, OPC_RSQ
, rsq
),
953 OPC(4, OPC_LOG2
, log2
),
954 OPC(4, OPC_EXP2
, exp2
),
955 OPC(4, OPC_SIN
, sin
),
956 OPC(4, OPC_COS
, cos
),
957 OPC(4, OPC_SQRT
, sqrt
),
960 OPC(5, OPC_ISAM
, isam
),
961 OPC(5, OPC_ISAML
, isaml
),
962 OPC(5, OPC_ISAMM
, isamm
),
963 OPC(5, OPC_SAM
, sam
),
964 OPC(5, OPC_SAMB
, samb
),
965 OPC(5, OPC_SAML
, saml
),
966 OPC(5, OPC_SAMGQ
, samgq
),
967 OPC(5, OPC_GETLOD
, getlod
),
968 OPC(5, OPC_CONV
, conv
),
969 OPC(5, OPC_CONVM
, convm
),
970 OPC(5, OPC_GETSIZE
, getsize
),
971 OPC(5, OPC_GETBUF
, getbuf
),
972 OPC(5, OPC_GETPOS
, getpos
),
973 OPC(5, OPC_GETINFO
, getinfo
),
974 OPC(5, OPC_DSX
, dsx
),
975 OPC(5, OPC_DSY
, dsy
),
976 OPC(5, OPC_GATHER4R
, gather4r
),
977 OPC(5, OPC_GATHER4G
, gather4g
),
978 OPC(5, OPC_GATHER4B
, gather4b
),
979 OPC(5, OPC_GATHER4A
, gather4a
),
980 OPC(5, OPC_SAMGP0
, samgp0
),
981 OPC(5, OPC_SAMGP1
, samgp1
),
982 OPC(5, OPC_SAMGP2
, samgp2
),
983 OPC(5, OPC_SAMGP3
, samgp3
),
984 OPC(5, OPC_DSXPP_1
, dsxpp
.1),
985 OPC(5, OPC_DSYPP_1
, dsypp
.1),
986 OPC(5, OPC_RGETPOS
, rgetpos
),
987 OPC(5, OPC_RGETINFO
, rgetinfo
),
991 OPC(6, OPC_LDG
, ldg
),
992 OPC(6, OPC_LDL
, ldl
),
993 OPC(6, OPC_LDP
, ldp
),
994 OPC(6, OPC_STG
, stg
),
995 OPC(6, OPC_STL
, stl
),
996 OPC(6, OPC_STP
, stp
),
997 OPC(6, OPC_LDIB
, ldib
),
998 OPC(6, OPC_G2L
, g2l
),
999 OPC(6, OPC_L2G
, l2g
),
1000 OPC(6, OPC_PREFETCH
, prefetch
),
1001 OPC(6, OPC_LDLW
, ldlw
),
1002 OPC(6, OPC_STLW
, stlw
),
1003 OPC(6, OPC_RESFMT
, resfmt
),
1004 OPC(6, OPC_RESINFO
, resinfo
),
1005 OPC(6, OPC_ATOMIC_ADD
, atomic
.add
),
1006 OPC(6, OPC_ATOMIC_SUB
, atomic
.sub
),
1007 OPC(6, OPC_ATOMIC_XCHG
, atomic
.xchg
),
1008 OPC(6, OPC_ATOMIC_INC
, atomic
.inc
),
1009 OPC(6, OPC_ATOMIC_DEC
, atomic
.dec
),
1010 OPC(6, OPC_ATOMIC_CMPXCHG
, atomic
.cmpxchg
),
1011 OPC(6, OPC_ATOMIC_MIN
, atomic
.min
),
1012 OPC(6, OPC_ATOMIC_MAX
, atomic
.max
),
1013 OPC(6, OPC_ATOMIC_AND
, atomic
.and),
1014 OPC(6, OPC_ATOMIC_OR
, atomic
.or),
1015 OPC(6, OPC_ATOMIC_XOR
, atomic
.xor),
1016 OPC(6, OPC_LDGB
, ldgb
),
1017 OPC(6, OPC_STGB
, stgb
),
1018 OPC(6, OPC_STIB
, stib
),
1019 OPC(6, OPC_LDC
, ldc
),
1020 OPC(6, OPC_LDLV
, ldlv
),
1022 OPC(7, OPC_BAR
, bar
),
1023 OPC(7, OPC_FENCE
, fence
),
1028 #define GETINFO(instr) (&(opcs[((instr)->opc_cat << NOPC_BITS) | instr_opc(instr, ctx->gpu_id)]))
1030 // XXX hack.. probably should move this table somewhere common:
1032 const char *ir3_instr_name(struct ir3_instruction
*instr
)
1034 if (opc_cat(instr
->opc
) == -1) return "??meta??";
1035 return opcs
[instr
->opc
].name
;
1038 static bool print_instr(struct disasm_ctx
*ctx
, uint32_t *dwords
, int n
)
1040 instr_t
*instr
= (instr_t
*)dwords
;
1041 uint32_t opc
= instr_opc(instr
, ctx
->gpu_id
);
1044 if (debug
& PRINT_VERBOSE
)
1045 fprintf(ctx
->out
, "%s%04d[%08xx_%08xx] ", levels
[ctx
->level
], n
, dwords
[1], dwords
[0]);
1047 /* NOTE: order flags are printed is a bit fugly.. but for now I
1048 * try to match the order in llvm-a3xx disassembler for easy
1052 ctx
->repeat
= instr_repeat(instr
);
1055 fprintf(ctx
->out
, "(sy)");
1056 if (instr
->ss
&& ((instr
->opc_cat
<= 4) || (instr
->opc_cat
== 7)))
1057 fprintf(ctx
->out
, "(ss)");
1059 fprintf(ctx
->out
, "(jp)");
1060 if (instr_sat(instr
))
1061 fprintf(ctx
->out
, "(sat)");
1063 fprintf(ctx
->out
, "(rpt%d)", ctx
->repeat
);
1064 } else if ((instr
->opc_cat
== 2) && (instr
->cat2
.src1_r
|| instr
->cat2
.src2_r
)) {
1065 unsigned nop
= (instr
->cat2
.src2_r
* 2) + instr
->cat2
.src1_r
;
1066 fprintf(ctx
->out
, "(nop%d)", nop
);
1067 } else if ((instr
->opc_cat
== 3) && (instr
->cat3
.src1_r
|| instr
->cat3
.src2_r
)) {
1068 unsigned nop
= (instr
->cat3
.src2_r
* 2) + instr
->cat3
.src1_r
;
1069 fprintf(ctx
->out
, "(nop%d)", nop
);
1071 if (instr
->ul
&& ((2 <= instr
->opc_cat
) && (instr
->opc_cat
<= 4)))
1072 fprintf(ctx
->out
, "(ul)");
1074 name
= GETINFO(instr
)->name
;
1077 fprintf(ctx
->out
, "%s", name
);
1078 GETINFO(instr
)->print(ctx
, instr
);
1080 fprintf(ctx
->out
, "unknown(%d,%d)", instr
->opc_cat
, opc
);
1083 fprintf(ctx
->out
, "\n");
1085 return (instr
->opc_cat
== 0) && (opc
== OPC_END
);
1088 int disasm_a3xx(uint32_t *dwords
, int sizedwords
, int level
, FILE *out
, unsigned gpu_id
)
1090 struct disasm_ctx ctx
;
1093 assert((sizedwords
% 2) == 0);
1095 memset(&ctx
, 0, sizeof(ctx
));
1098 ctx
.gpu_id
= gpu_id
;
1100 for (i
= 0; i
< sizedwords
; i
+= 2)
1101 print_instr(&ctx
, &dwords
[i
], i
/2);