xref: /aosp_15_r20/external/mesa3d/src/broadcom/qpu/qpu_disasm.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2016 Broadcom
3  *
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:
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
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
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include <string.h>
25 #include <stdio.h>
26 #include "util/ralloc.h"
27 
28 #include "broadcom/common/v3d_device_info.h"
29 #include "qpu_instr.h"
30 #include "qpu_disasm.h"
31 
32 struct disasm_state {
33         const struct v3d_device_info *devinfo;
34         char *string;
35         size_t offset;
36 };
37 
38 static void
append(struct disasm_state * disasm,const char * fmt,...)39 append(struct disasm_state *disasm, const char *fmt, ...)
40 {
41         va_list args;
42         va_start(args, fmt);
43         ralloc_vasprintf_rewrite_tail(&disasm->string,
44                                       &disasm->offset,
45                                       fmt, args);
46         va_end(args);
47 }
48 
49 static void
pad_to(struct disasm_state * disasm,int n)50 pad_to(struct disasm_state *disasm, int n)
51 {
52         /* FIXME: Do a single append somehow. */
53         while (disasm->offset < n)
54                 append(disasm, " ");
55 }
56 
57 
58 static void
v3d42_qpu_disasm_raddr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr,enum v3d_qpu_mux mux)59 v3d42_qpu_disasm_raddr(struct disasm_state *disasm,
60                        const struct v3d_qpu_instr *instr,
61                        enum v3d_qpu_mux mux)
62 {
63         if (mux == V3D_QPU_MUX_A) {
64                 append(disasm, "rf%d", instr->raddr_a);
65         } else if (mux == V3D_QPU_MUX_B) {
66                 if (instr->sig.small_imm_b) {
67                         uint32_t val;
68                         ASSERTED bool ok =
69                                 v3d_qpu_small_imm_unpack(disasm->devinfo,
70                                                          instr->raddr_b,
71                                                          &val);
72 
73                         if ((int)val >= -16 && (int)val <= 15)
74                                 append(disasm, "%d", val);
75                         else
76                                 append(disasm, "0x%08x", val);
77                         assert(ok);
78                 } else {
79                         append(disasm, "rf%d", instr->raddr_b);
80                 }
81         } else {
82                 append(disasm, "r%d", mux);
83         }
84 }
85 
86 enum v3d_qpu_input_class {
87         V3D_QPU_ADD_A,
88         V3D_QPU_ADD_B,
89         V3D_QPU_MUL_A,
90         V3D_QPU_MUL_B
91 };
92 
93 static void
v3d71_qpu_disasm_raddr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr,uint8_t raddr,enum v3d_qpu_input_class input_class)94 v3d71_qpu_disasm_raddr(struct disasm_state *disasm,
95                        const struct v3d_qpu_instr *instr,
96                        uint8_t raddr,
97                        enum v3d_qpu_input_class input_class)
98 {
99         bool is_small_imm = false;
100         switch(input_class) {
101         case V3D_QPU_ADD_A:
102                 is_small_imm = instr->sig.small_imm_a;
103                 break;
104         case V3D_QPU_ADD_B:
105                 is_small_imm = instr->sig.small_imm_b;
106                 break;
107         case V3D_QPU_MUL_A:
108                 is_small_imm = instr->sig.small_imm_c;
109                 break;
110         case V3D_QPU_MUL_B:
111                 is_small_imm = instr->sig.small_imm_d;
112                 break;
113         }
114 
115         if (is_small_imm) {
116                 uint32_t val;
117                 ASSERTED bool ok =
118                         v3d_qpu_small_imm_unpack(disasm->devinfo,
119                                                  raddr,
120                                                  &val);
121 
122                 if ((int)val >= -16 && (int)val <= 15)
123                         append(disasm, "%d", val);
124                 else
125                         append(disasm, "0x%08x", val);
126                 assert(ok);
127         } else {
128                 append(disasm, "rf%d", raddr);
129         }
130 }
131 
132 static void
v3d_qpu_disasm_raddr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr,const struct v3d_qpu_input * input,enum v3d_qpu_input_class input_class)133 v3d_qpu_disasm_raddr(struct disasm_state *disasm,
134                      const struct v3d_qpu_instr *instr,
135                      const struct v3d_qpu_input *input,
136                      enum v3d_qpu_input_class input_class)
137 {
138         if (disasm->devinfo->ver >= 71)
139                 v3d71_qpu_disasm_raddr(disasm, instr, input->raddr, input_class);
140         else
141                 v3d42_qpu_disasm_raddr(disasm, instr, input->mux);
142 }
143 
144 static void
v3d_qpu_disasm_waddr(struct disasm_state * disasm,uint32_t waddr,bool magic)145 v3d_qpu_disasm_waddr(struct disasm_state *disasm, uint32_t waddr, bool magic)
146 {
147         if (!magic) {
148                 append(disasm, "rf%d", waddr);
149                 return;
150         }
151 
152         const char *name = v3d_qpu_magic_waddr_name(disasm->devinfo, waddr);
153         if (name)
154                 append(disasm, "%s", name);
155         else
156                 append(disasm, "waddr UNKNOWN %d", waddr);
157 }
158 
159 static void
v3d_qpu_disasm_add(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)160 v3d_qpu_disasm_add(struct disasm_state *disasm,
161                    const struct v3d_qpu_instr *instr)
162 {
163         bool has_dst = v3d_qpu_add_op_has_dst(instr->alu.add.op);
164         int num_src = v3d_qpu_add_op_num_src(instr->alu.add.op);
165 
166         append(disasm, "%s", v3d_qpu_add_op_name(instr->alu.add.op));
167         if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
168                 append(disasm, "%s", v3d_qpu_cond_name(instr->flags.ac));
169         append(disasm, "%s", v3d_qpu_pf_name(instr->flags.apf));
170         append(disasm, "%s", v3d_qpu_uf_name(instr->flags.auf));
171 
172         append(disasm, " ");
173 
174         if (has_dst) {
175                 v3d_qpu_disasm_waddr(disasm, instr->alu.add.waddr,
176                                      instr->alu.add.magic_write);
177                 append(disasm, v3d_qpu_pack_name(instr->alu.add.output_pack));
178         }
179 
180         if (num_src >= 1) {
181                 if (has_dst)
182                         append(disasm, ", ");
183                 v3d_qpu_disasm_raddr(disasm, instr, &instr->alu.add.a, V3D_QPU_ADD_A);
184                 append(disasm, "%s",
185                        v3d_qpu_unpack_name(instr->alu.add.a.unpack));
186         }
187 
188         if (num_src >= 2) {
189                 append(disasm, ", ");
190                 v3d_qpu_disasm_raddr(disasm, instr, &instr->alu.add.b, V3D_QPU_ADD_B);
191                 append(disasm, "%s",
192                        v3d_qpu_unpack_name(instr->alu.add.b.unpack));
193         }
194 }
195 
196 static void
v3d_qpu_disasm_mul(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)197 v3d_qpu_disasm_mul(struct disasm_state *disasm,
198                    const struct v3d_qpu_instr *instr)
199 {
200         bool has_dst = v3d_qpu_mul_op_has_dst(instr->alu.mul.op);
201         int num_src = v3d_qpu_mul_op_num_src(instr->alu.mul.op);
202 
203         pad_to(disasm, 30);
204         append(disasm, "; ");
205 
206         append(disasm, "%s", v3d_qpu_mul_op_name(instr->alu.mul.op));
207         if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
208                 append(disasm, "%s", v3d_qpu_cond_name(instr->flags.mc));
209         append(disasm, "%s", v3d_qpu_pf_name(instr->flags.mpf));
210         append(disasm, "%s", v3d_qpu_uf_name(instr->flags.muf));
211 
212         if (instr->alu.mul.op == V3D_QPU_M_NOP)
213                 return;
214 
215         append(disasm, " ");
216 
217         if (has_dst) {
218                 v3d_qpu_disasm_waddr(disasm, instr->alu.mul.waddr,
219                                      instr->alu.mul.magic_write);
220                 append(disasm, v3d_qpu_pack_name(instr->alu.mul.output_pack));
221         }
222 
223         if (num_src >= 1) {
224                 if (has_dst)
225                         append(disasm, ", ");
226                 v3d_qpu_disasm_raddr(disasm, instr, &instr->alu.mul.a, V3D_QPU_MUL_A);
227                 append(disasm, "%s",
228                        v3d_qpu_unpack_name(instr->alu.mul.a.unpack));
229         }
230 
231         if (num_src >= 2) {
232                 append(disasm, ", ");
233                 v3d_qpu_disasm_raddr(disasm, instr, &instr->alu.mul.b, V3D_QPU_MUL_B);
234                 append(disasm, "%s",
235                        v3d_qpu_unpack_name(instr->alu.mul.b.unpack));
236         }
237 }
238 
239 static void
v3d_qpu_disasm_sig_addr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)240 v3d_qpu_disasm_sig_addr(struct disasm_state *disasm,
241                         const struct v3d_qpu_instr *instr)
242 {
243         if (disasm->devinfo->ver < 41)
244                 return;
245 
246         if (!instr->sig_magic)
247                 append(disasm, ".rf%d", instr->sig_addr);
248         else {
249                 const char *name =
250                         v3d_qpu_magic_waddr_name(disasm->devinfo,
251                                                  instr->sig_addr);
252                 if (name)
253                         append(disasm, ".%s", name);
254                 else
255                         append(disasm, ".UNKNOWN%d", instr->sig_addr);
256         }
257 }
258 
259 static void
v3d_qpu_disasm_sig(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)260 v3d_qpu_disasm_sig(struct disasm_state *disasm,
261                    const struct v3d_qpu_instr *instr)
262 {
263         const struct v3d_qpu_sig *sig = &instr->sig;
264 
265         if (!sig->thrsw &&
266             !sig->ldvary &&
267             !sig->ldvpm &&
268             !sig->ldtmu &&
269             !sig->ldtlb &&
270             !sig->ldtlbu &&
271             !sig->ldunif &&
272             !sig->ldunifrf &&
273             !sig->ldunifa &&
274             !sig->ldunifarf &&
275             !sig->wrtmuc) {
276                 return;
277         }
278 
279         pad_to(disasm, 60);
280 
281         if (sig->thrsw)
282                 append(disasm, "; thrsw");
283         if (sig->ldvary) {
284                 append(disasm, "; ldvary");
285                 v3d_qpu_disasm_sig_addr(disasm, instr);
286         }
287         if (sig->ldvpm)
288                 append(disasm, "; ldvpm");
289         if (sig->ldtmu) {
290                 append(disasm, "; ldtmu");
291                 v3d_qpu_disasm_sig_addr(disasm, instr);
292         }
293         if (sig->ldtlb) {
294                 append(disasm, "; ldtlb");
295                 v3d_qpu_disasm_sig_addr(disasm, instr);
296         }
297         if (sig->ldtlbu) {
298                 append(disasm, "; ldtlbu");
299                 v3d_qpu_disasm_sig_addr(disasm, instr);
300         }
301         if (sig->ldunif)
302                 append(disasm, "; ldunif");
303         if (sig->ldunifrf) {
304                 append(disasm, "; ldunifrf");
305                 v3d_qpu_disasm_sig_addr(disasm, instr);
306         }
307         if (sig->ldunifa)
308                 append(disasm, "; ldunifa");
309         if (sig->ldunifarf) {
310                 append(disasm, "; ldunifarf");
311                 v3d_qpu_disasm_sig_addr(disasm, instr);
312         }
313         if (sig->wrtmuc)
314                 append(disasm, "; wrtmuc");
315 }
316 
317 static void
v3d_qpu_disasm_alu(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)318 v3d_qpu_disasm_alu(struct disasm_state *disasm,
319                    const struct v3d_qpu_instr *instr)
320 {
321         v3d_qpu_disasm_add(disasm, instr);
322         v3d_qpu_disasm_mul(disasm, instr);
323         v3d_qpu_disasm_sig(disasm, instr);
324 }
325 
326 static void
v3d_qpu_disasm_branch(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)327 v3d_qpu_disasm_branch(struct disasm_state *disasm,
328                       const struct v3d_qpu_instr *instr)
329 {
330         append(disasm, "b");
331         if (instr->branch.ub)
332                 append(disasm, "u");
333         append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond));
334         append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign));
335 
336         switch (instr->branch.bdi) {
337         case V3D_QPU_BRANCH_DEST_ABS:
338                 append(disasm, "  zero_addr+0x%08x", instr->branch.offset);
339                 break;
340 
341         case V3D_QPU_BRANCH_DEST_REL:
342                 append(disasm, "  %d", instr->branch.offset);
343                 break;
344 
345         case V3D_QPU_BRANCH_DEST_LINK_REG:
346                 append(disasm, "  lri");
347                 break;
348 
349         case V3D_QPU_BRANCH_DEST_REGFILE:
350                 append(disasm, "  rf%d", instr->branch.raddr_a);
351                 break;
352         }
353 
354         if (instr->branch.ub) {
355                 switch (instr->branch.bdu) {
356                 case V3D_QPU_BRANCH_DEST_ABS:
357                         append(disasm, ", a:unif");
358                         break;
359 
360                 case V3D_QPU_BRANCH_DEST_REL:
361                         append(disasm, ", r:unif");
362                         break;
363 
364                 case V3D_QPU_BRANCH_DEST_LINK_REG:
365                         append(disasm, ", lri");
366                         break;
367 
368                 case V3D_QPU_BRANCH_DEST_REGFILE:
369                         append(disasm, ", rf%d", instr->branch.raddr_a);
370                         break;
371                 }
372         }
373 }
374 
375 const char *
v3d_qpu_decode(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)376 v3d_qpu_decode(const struct v3d_device_info *devinfo,
377                const struct v3d_qpu_instr *instr)
378 {
379         struct disasm_state disasm = {
380                 .string = rzalloc_size(NULL, 1),
381                 .offset = 0,
382                 .devinfo = devinfo,
383         };
384 
385         switch (instr->type) {
386         case V3D_QPU_INSTR_TYPE_ALU:
387                 v3d_qpu_disasm_alu(&disasm, instr);
388                 break;
389 
390         case V3D_QPU_INSTR_TYPE_BRANCH:
391                 v3d_qpu_disasm_branch(&disasm, instr);
392                 break;
393         }
394 
395         return disasm.string;
396 }
397 
398 /**
399  * Returns a string containing the disassembled representation of the QPU
400  * instruction.  It is the caller's responsibility to free the return value
401  * with ralloc_free().
402  */
403 const char *
v3d_qpu_disasm(const struct v3d_device_info * devinfo,uint64_t inst)404 v3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst)
405 {
406         struct v3d_qpu_instr instr;
407         bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr);
408         assert(ok); (void)ok;
409 
410         return v3d_qpu_decode(devinfo, &instr);
411 }
412 
413 void
v3d_qpu_dump(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)414 v3d_qpu_dump(const struct v3d_device_info *devinfo,
415              const struct v3d_qpu_instr *instr)
416 {
417         const char *decoded = v3d_qpu_decode(devinfo, instr);
418         fprintf(stderr, "%s", decoded);
419         ralloc_free((char *)decoded);
420 }
421