1/*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
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
13 * Software.
14 *
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 THE
18 * 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
21 * SOFTWARE.
22 */
23
24#include <stdbool.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29#include "pvr_rogue_pds_defs.h"
30#include "pvr_rogue_pds_disasm.h"
31#include "pvr_rogue_pds_encode.h"
32#include "util/log.h"
33
34#define X(lop, str) #str,
35static const char *const LOP[] = { PVR_PDS_LOP };
36#undef X
37
38static void pvr_pds_disassemble_operand(struct pvr_operand *op,
39                                        char *instr_str,
40                                        size_t instr_len)
41{
42#define X(enum, str, size) { #str, #size },
43   static const char *const regs[][2] = { PVR_PDS_OPERAND_TYPES };
44#undef X
45
46   if (op->type == LITERAL_NUM) {
47      snprintf(instr_str,
48               instr_len,
49               "%s (%llu)",
50               regs[op->type][0],
51               (unsigned long long)op->literal);
52   } else if (op->type == UNRESOLVED) {
53      snprintf(instr_str, instr_len, "UNRESOLVED");
54   } else {
55      snprintf(instr_str,
56               instr_len,
57               "%s[%u].%s",
58               regs[op->type][0],
59               op->absolute_address,
60               regs[op->type][1]);
61   }
62}
63
64static void pvr_pds_disassemble_instruction_add64(struct pvr_add *add,
65                                                  char *instr_str,
66                                                  size_t instr_len)
67{
68   char dst[32];
69   char src0[32];
70   char src1[32];
71
72   pvr_pds_disassemble_operand(add->src0, src0, sizeof(src0));
73   pvr_pds_disassemble_operand(add->src1, src1, sizeof(src1));
74   pvr_pds_disassemble_operand(add->dst, dst, sizeof(dst));
75
76   snprintf(instr_str,
77            instr_len,
78            "%-16s%s%s = %s %s %s %s",
79            "ADD64",
80            add->cc ? "? " : "",
81            dst,
82            src0,
83            add->sna ? "-" : "+",
84            src1,
85            add->alum ? "[signed]" : "");
86}
87
88static void pvr_pds_disassemble_instruction_add32(struct pvr_add *add,
89                                                  char *instr_str,
90                                                  size_t instr_len)
91{
92   char dst[32];
93   char src0[32];
94   char src1[32];
95
96   pvr_pds_disassemble_operand(add->src0, src0, sizeof(src0));
97   pvr_pds_disassemble_operand(add->src1, src1, sizeof(src1));
98   pvr_pds_disassemble_operand(add->dst, dst, sizeof(dst));
99
100   snprintf(instr_str,
101            instr_len,
102            "%-16s%s%s = %s %s %s %s",
103            "ADD32",
104            add->cc ? "? " : "",
105            dst,
106            src0,
107            add->sna ? "-" : "+",
108            src1,
109            add->alum ? "[signed]" : "");
110}
111
112static void
113pvr_pds_disassemble_instruction_sftlp32(struct pvr_sftlp *instruction,
114                                        char *instr_str,
115                                        size_t instr_len)
116{
117   char dst[32];
118   char src0[32];
119   char src1[32];
120   char src2[32];
121
122   pvr_pds_disassemble_operand(instruction->src0, src0, sizeof(src0));
123   pvr_pds_disassemble_operand(instruction->src1, src1, sizeof(src1));
124   pvr_pds_disassemble_operand(instruction->dst, dst, sizeof(dst));
125
126   if (instruction->IM)
127      snprintf(src2, sizeof(src2), "%u", (uint32_t)instruction->src2->literal);
128   else
129      pvr_pds_disassemble_operand(instruction->src2, src2, sizeof(src2));
130
131   if (instruction->lop == LOP_NONE) {
132      snprintf(instr_str,
133               instr_len,
134               "%-16s%s%s = %s %s %s",
135               "SFTLP32",
136               instruction->cc ? "? " : "",
137               dst,
138               src0,
139               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
140               src2);
141   } else if (instruction->lop == LOP_NOT) {
142      snprintf(instr_str,
143               instr_len,
144               "%-16s%s%s = (~%s) %s %s",
145               "SFTLP32",
146               instruction->cc ? "? " : "",
147               dst,
148               src0,
149               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
150               src2);
151   } else {
152      snprintf(instr_str,
153               instr_len,
154               "%-16s%s%s = (%s %s %s) %s %s",
155               "SFTLP32",
156               instruction->cc ? "? " : "",
157               dst,
158               src0,
159               LOP[instruction->lop],
160               src1,
161               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
162               src2);
163   }
164}
165
166static void pvr_pds_disassemble_instruction_stm(struct pvr_stm *instruction,
167                                                char *instr_str,
168                                                size_t instr_len)
169{
170   char src0[32];
171   char src1[32];
172   char src2[32];
173   char src3[32];
174
175   char stm_pred[64];
176
177   pvr_pds_disassemble_operand(instruction->src0, src0, sizeof(src0));
178   pvr_pds_disassemble_operand(instruction->src1, src1, sizeof(src1));
179   pvr_pds_disassemble_operand(instruction->src2, src2, sizeof(src2));
180   pvr_pds_disassemble_operand(instruction->src3, src3, sizeof(src3));
181
182   if (instruction->ccs_global)
183      snprintf(stm_pred, sizeof(stm_pred), "overflow_any");
184   else if (instruction->ccs_so)
185      snprintf(stm_pred, sizeof(stm_pred), "overflow_current");
186   else
187      stm_pred[0] = 0;
188
189   snprintf(instr_str,
190            instr_len,
191            "%-16s%s%s%s stm%u = %s, %s, %s, %s",
192            "STM",
193            instruction->cc ? "? " : "",
194            stm_pred,
195            instruction->tst ? " (TST only)" : "",
196            instruction->stream_out,
197            src0,
198            src1,
199            src2,
200            src3);
201}
202
203static void pds_disassemble_instruction_stmc(struct pvr_stmc *instruction,
204                                             char *instr_str,
205                                             size_t instr_len)
206{
207   char src0[32];
208
209   pvr_pds_disassemble_operand(instruction->src0, src0, sizeof(src0));
210
211   snprintf(instr_str,
212            instr_len,
213            "%-16s%s %s",
214            "STMC",
215            instruction->cc ? "? " : "",
216            src0);
217}
218
219static void
220pvr_pds_disassemble_instruction_sftlp64(struct pvr_sftlp *instruction,
221                                        char *instr_str,
222                                        size_t instr_len)
223{
224   char dst[32];
225   char src0[32];
226   char src1[32];
227   char src2[32];
228
229   pvr_pds_disassemble_operand(instruction->src0, src0, sizeof(src0));
230   pvr_pds_disassemble_operand(instruction->src1, src1, sizeof(src1));
231   pvr_pds_disassemble_operand(instruction->dst, dst, sizeof(dst));
232
233   if (instruction->IM)
234      snprintf(src2, sizeof(src2), "%u", (uint32_t)instruction->src2->literal);
235   else
236      pvr_pds_disassemble_operand(instruction->src2, src2, sizeof(src2));
237
238   if (instruction->lop == LOP_NONE) {
239      snprintf(instr_str,
240               instr_len,
241               "%-16s%s%s = %s %s %s",
242               "SFTLP64",
243               instruction->cc ? "? " : "",
244               dst,
245               src0,
246               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
247               src2);
248   } else if (instruction->lop == LOP_NOT) {
249      snprintf(instr_str,
250               instr_len,
251               "%-16s%s%s = (~%s) %s %s",
252               "SFTLP64",
253               instruction->cc ? "? " : "",
254               dst,
255               src0,
256               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
257               src2);
258   } else {
259      snprintf(instr_str,
260               instr_len,
261               "%-16s%s%s = (%s %s %s) %s %s",
262               "SFTLP64",
263               instruction->cc ? "? " : "",
264               dst,
265               src0,
266               LOP[instruction->lop],
267               src1,
268               instruction->IM ? instruction->src2->negate ? ">>" : "<<" : "<<",
269               src2);
270   }
271}
272
273static void pvr_pds_disassemble_instruction_cmp(struct pvr_cmp *cmp,
274                                                char *instr_str,
275                                                size_t instr_len)
276{
277   char src0[32];
278   char src1[32];
279   static const char *const COP[] = { "=", ">", "<", "!=" };
280
281   pvr_pds_disassemble_operand(cmp->src0, src0, sizeof(src0));
282
283   if (cmp->IM) {
284      snprintf(src1,
285               sizeof(src1),
286               "%#04llx",
287               (unsigned long long)cmp->src1->literal);
288   } else {
289      pvr_pds_disassemble_operand(cmp->src1, src1, sizeof(src1));
290   }
291
292   snprintf(instr_str,
293            instr_len,
294            "%-16s%sP0 = (%s %s %s)",
295            "CMP",
296            cmp->cc ? "? " : "",
297            src0,
298            COP[cmp->cop],
299            src1);
300}
301
302static void pvr_pds_disassemble_instruction_ldst(struct pvr_ldst *ins,
303                                                 char *instr_str,
304                                                 size_t instr_len)
305{
306   char src0[PVR_PDS_MAX_INST_STR_LEN];
307
308   pvr_pds_disassemble_operand(ins->src0, src0, sizeof(src0));
309
310   if (ins->st) {
311      snprintf(instr_str,
312               instr_len,
313               "%-16s%s%s: mem(%s) <= src(%s)",
314               "ST",
315               ins->cc ? "? " : "",
316               src0,
317               "?",
318               "?");
319   } else {
320      snprintf(instr_str,
321               instr_len,
322               "%-16s%s%s: dst(%s) <= mem(%s)",
323               "ld",
324               ins->cc ? "? " : "",
325               src0,
326               "?",
327               "?");
328   }
329}
330
331static void pvr_pds_disassemble_simple(struct pvr_simple *simple,
332                                       const char *type,
333                                       char *instr_str,
334                                       size_t instr_len)
335{
336   snprintf(instr_str, instr_len, "%-16s%s", type, simple->cc ? "? " : "");
337}
338
339static void pvr_pds_disassemble_instruction_limm(struct pvr_limm *limm,
340                                                 char *instr_str,
341                                                 size_t instr_len)
342{
343   int32_t imm = (uint32_t)limm->src0->literal;
344   char dst[PVR_PDS_MAX_INST_STR_LEN];
345
346   pvr_pds_disassemble_operand(limm->dst, dst, sizeof(dst));
347
348   if (limm->GR) {
349      char *pchGReg;
350
351      switch (imm) {
352      case 0:
353         pchGReg = "cluster";
354         break;
355      case 1:
356         pchGReg = "instance";
357         break;
358      default:
359         pchGReg = "unknown";
360      }
361
362      snprintf(instr_str,
363               instr_len,
364               "%-16s%s%s = G%d (%s)",
365               "LIMM",
366               limm->cc ? "? " : "",
367               dst,
368               imm,
369               pchGReg);
370   } else {
371      snprintf(instr_str,
372               instr_len,
373               "%-16s%s%s = %#04x",
374               "LIMM",
375               limm->cc ? "? " : "",
376               dst,
377               imm);
378   }
379}
380
381static void pvr_pds_disassemble_instruction_ddmad(struct pvr_ddmad *ddmad,
382                                                  char *instr_str,
383                                                  size_t instr_len)
384{
385   char src0[PVR_PDS_MAX_INST_STR_LEN];
386   char src1[PVR_PDS_MAX_INST_STR_LEN];
387   char src2[PVR_PDS_MAX_INST_STR_LEN];
388   char src3[PVR_PDS_MAX_INST_STR_LEN];
389
390   pvr_pds_disassemble_operand(ddmad->src0, src0, sizeof(src0));
391   pvr_pds_disassemble_operand(ddmad->src1, src1, sizeof(src1));
392   pvr_pds_disassemble_operand(ddmad->src2, src2, sizeof(src2));
393   pvr_pds_disassemble_operand(ddmad->src3, src3, sizeof(src3));
394
395   snprintf(instr_str,
396            instr_len,
397            "%-16s%sdoutd = (%s * %s) + %s, %s%s",
398            "DDMAD",
399            ddmad->cc ? "? " : "",
400            src0,
401            src1,
402            src2,
403            src3,
404            ddmad->END ? "; HALT" : "");
405}
406
407static void pvr_pds_disassemble_predicate(uint32_t predicate,
408                                          char *buffer,
409                                          size_t buffer_length)
410{
411   switch (predicate) {
412   case PVR_ROGUE_PDSINST_PREDICATE_P0:
413      snprintf(buffer, buffer_length, "%s", "p0");
414      break;
415   case PVR_ROGUE_PDSINST_PREDICATE_IF0:
416      snprintf(buffer, buffer_length, "%s", "if0");
417      break;
418   case PVR_ROGUE_PDSINST_PREDICATE_IF1:
419      snprintf(buffer, buffer_length, "%s", "if1");
420      break;
421   case PVR_ROGUE_PDSINST_PREDICATE_SO_OVERFLOW_PREDICATE_0:
422      snprintf(buffer, buffer_length, "%s", "so_overflow_0");
423      break;
424   case PVR_ROGUE_PDSINST_PREDICATE_SO_OVERFLOW_PREDICATE_1:
425      snprintf(buffer, buffer_length, "%s", "so_overflow_1");
426      break;
427   case PVR_ROGUE_PDSINST_PREDICATE_SO_OVERFLOW_PREDICATE_2:
428      snprintf(buffer, buffer_length, "%s", "so_overflow_2");
429      break;
430   case PVR_ROGUE_PDSINST_PREDICATE_SO_OVERFLOW_PREDICATE_3:
431      snprintf(buffer, buffer_length, "%s", "so_overflow_3");
432      break;
433   case PVR_ROGUE_PDSINST_PREDICATE_SO_OVERFLOW_PREDICATE_GLOBAL:
434      snprintf(buffer, buffer_length, "%s", "so_overflow_any");
435      break;
436   case PVR_ROGUE_PDSINST_PREDICATE_KEEP:
437      snprintf(buffer, buffer_length, "%s", "keep");
438      break;
439   case PVR_ROGUE_PDSINST_PREDICATE_OOB:
440      snprintf(buffer, buffer_length, "%s", "oob");
441      break;
442   default:
443      snprintf(buffer, buffer_length, "%s", "<ERROR>");
444      break;
445   }
446}
447
448static void pvr_pds_disassemble_instruction_bra(struct pvr_bra *bra,
449                                                char *instr_str,
450                                                size_t instr_len)
451{
452   char setc_pred[32];
453   char srcc_pred[32];
454
455   pvr_pds_disassemble_predicate(bra->srcc->predicate,
456                                 srcc_pred,
457                                 sizeof(srcc_pred));
458   pvr_pds_disassemble_predicate(bra->setc->predicate,
459                                 setc_pred,
460                                 sizeof(setc_pred));
461
462   if (bra->setc->predicate != PVR_ROGUE_PDSINST_PREDICATE_KEEP) {
463      snprintf(instr_str,
464               instr_len,
465               "%-16sif %s%s %d ( setc = %s )",
466               "BRA",
467               bra->srcc->negate ? "! " : "",
468               srcc_pred,
469               bra->address,
470               setc_pred);
471   } else {
472      snprintf(instr_str,
473               instr_len,
474               "%-16sif %s%s %d",
475               "BRA",
476               bra->srcc->negate ? "! " : "",
477               srcc_pred,
478               bra->address);
479   }
480}
481
482static void pvr_pds_disassemble_instruction_mad(struct pvr_mad *mad,
483                                                char *instr_str,
484                                                size_t instr_len)
485{
486   char src0[PVR_PDS_MAX_INST_STR_LEN];
487   char src1[PVR_PDS_MAX_INST_STR_LEN];
488   char src2[PVR_PDS_MAX_INST_STR_LEN];
489   char dst[PVR_PDS_MAX_INST_STR_LEN];
490
491   pvr_pds_disassemble_operand(mad->src0, src0, sizeof(src0));
492   pvr_pds_disassemble_operand(mad->src1, src1, sizeof(src1));
493   pvr_pds_disassemble_operand(mad->src2, src2, sizeof(src2));
494   pvr_pds_disassemble_operand(mad->dst, dst, sizeof(dst));
495
496   snprintf(instr_str,
497            instr_len,
498            "%-16s%s%s = (%s * %s) %s %s%s",
499            "MAD",
500            mad->cc ? "? " : "",
501            dst,
502            src0,
503            src1,
504            mad->sna ? "-" : "+",
505            src2,
506            mad->alum ? " [signed]" : "");
507}
508
509static void pvr_pds_disassemble_instruction_dout(struct pvr_dout *dout,
510                                                 char *instr_str,
511                                                 size_t instr_len)
512{
513   char src0[PVR_PDS_MAX_INST_STR_LEN];
514   char src1[PVR_PDS_MAX_INST_STR_LEN];
515
516#define X(dout_dst, str) #str,
517   static const char *const dst[] = { PVR_PDS_DOUT_DSTS };
518#undef X
519
520   pvr_pds_disassemble_operand(dout->src0, src0, sizeof(src0));
521   pvr_pds_disassemble_operand(dout->src1, src1, sizeof(src1));
522
523   {
524      snprintf(instr_str,
525               instr_len,
526               "%-16s%s%s = %s, %s%s",
527               "DOUT",
528               dout->cc ? "? " : "",
529               dst[dout->dst],
530               src0,
531               src1,
532               dout->END ? "; HALT" : "");
533   }
534}
535
536void pvr_pds_disassemble_instruction(char *instr_str,
537                                     size_t instr_len,
538                                     struct pvr_instruction *instruction)
539{
540   if (!instruction) {
541      snprintf(instr_str,
542               instr_len,
543               "Instruction was not disassembled properly\n");
544      return;
545   }
546
547   switch (instruction->type) {
548   case INS_LIMM:
549      pvr_pds_disassemble_instruction_limm((struct pvr_limm *)instruction,
550                                           instr_str,
551                                           instr_len);
552      break;
553   case INS_ADD64:
554      pvr_pds_disassemble_instruction_add64((struct pvr_add *)instruction,
555                                            instr_str,
556                                            instr_len);
557      break;
558   case INS_ADD32:
559      pvr_pds_disassemble_instruction_add32((struct pvr_add *)instruction,
560                                            instr_str,
561                                            instr_len);
562      break;
563   case INS_CMP:
564      pvr_pds_disassemble_instruction_cmp((struct pvr_cmp *)instruction,
565                                          instr_str,
566                                          instr_len);
567      break;
568   case INS_MAD:
569      pvr_pds_disassemble_instruction_mad((struct pvr_mad *)instruction,
570                                          instr_str,
571                                          instr_len);
572      break;
573   case INS_BRA:
574      pvr_pds_disassemble_instruction_bra((struct pvr_bra *)instruction,
575                                          instr_str,
576                                          instr_len);
577      break;
578   case INS_DDMAD:
579      pvr_pds_disassemble_instruction_ddmad((struct pvr_ddmad *)instruction,
580                                            instr_str,
581                                            instr_len);
582      break;
583   case INS_DOUT:
584      pvr_pds_disassemble_instruction_dout((struct pvr_dout *)instruction,
585                                           instr_str,
586                                           instr_len);
587      break;
588   case INS_LD:
589   case INS_ST:
590      pvr_pds_disassemble_instruction_ldst((struct pvr_ldst *)instruction,
591                                           instr_str,
592                                           instr_len);
593      break;
594   case INS_WDF:
595      pvr_pds_disassemble_simple((struct pvr_simple *)instruction,
596                                 "WDF",
597                                 instr_str,
598                                 instr_len);
599      break;
600   case INS_LOCK:
601      pvr_pds_disassemble_simple((struct pvr_simple *)instruction,
602                                 "LOCK",
603                                 instr_str,
604                                 instr_len);
605      break;
606   case INS_RELEASE:
607      pvr_pds_disassemble_simple((struct pvr_simple *)instruction,
608                                 "RELEASE",
609                                 instr_str,
610                                 instr_len);
611      break;
612   case INS_HALT:
613      pvr_pds_disassemble_simple((struct pvr_simple *)instruction,
614                                 "HALT",
615                                 instr_str,
616                                 instr_len);
617      break;
618   case INS_NOP:
619      pvr_pds_disassemble_simple((struct pvr_simple *)instruction,
620                                 "NOP",
621                                 instr_str,
622                                 instr_len);
623      break;
624   case INS_SFTLP32:
625      pvr_pds_disassemble_instruction_sftlp32((struct pvr_sftlp *)instruction,
626                                              instr_str,
627                                              instr_len);
628      break;
629   case INS_SFTLP64:
630      pvr_pds_disassemble_instruction_sftlp64((struct pvr_sftlp *)instruction,
631                                              instr_str,
632                                              instr_len);
633      break;
634   case INS_STM:
635      pvr_pds_disassemble_instruction_stm((struct pvr_stm *)instruction,
636                                          instr_str,
637                                          instr_len);
638      break;
639   case INS_STMC:
640      pds_disassemble_instruction_stmc((struct pvr_stmc *)instruction,
641                                       instr_str,
642                                       instr_len);
643      break;
644   default:
645      snprintf(instr_str, instr_len, "Printing not implemented\n");
646      break;
647   }
648}
649
650#if defined(DUMP_PDS)
651void pvr_pds_print_instruction(uint32_t instr)
652{
653   char instruction_str[1024];
654   struct pvr_instruction *decoded =
655      pvr_pds_disassemble_instruction2(0, 0, instr);
656
657   if (!decoded) {
658      mesa_logd("%X\n", instr);
659   } else {
660      pvr_pds_disassemble_instruction(instruction_str,
661                                      sizeof(instruction_str),
662                                      decoded);
663      mesa_logd("\t0x%08x, /* %s */\n", instr, instruction_str);
664   }
665}
666#endif
667