• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
v3d_qpu_disasm_raddr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr,uint8_t mux)59 v3d_qpu_disasm_raddr(struct disasm_state *disasm,
60                      const struct v3d_qpu_instr *instr, uint8_t mux)
61 {
62         if (mux == V3D_QPU_MUX_A) {
63                 append(disasm, "rf%d", instr->raddr_a);
64         } else if (mux == V3D_QPU_MUX_B) {
65                 if (instr->sig.small_imm) {
66                         uint32_t val;
67                         ASSERTED bool ok =
68                                 v3d_qpu_small_imm_unpack(disasm->devinfo,
69                                                          instr->raddr_b,
70                                                          &val);
71 
72                         if ((int)val >= -16 && (int)val <= 15)
73                                 append(disasm, "%d", val);
74                         else
75                                 append(disasm, "0x%08x", val);
76                         assert(ok);
77                 } else {
78                         append(disasm, "rf%d", instr->raddr_b);
79                 }
80         } else {
81                 append(disasm, "r%d", mux);
82         }
83 }
84 
85 static void
v3d_qpu_disasm_waddr(struct disasm_state * disasm,uint32_t waddr,bool magic)86 v3d_qpu_disasm_waddr(struct disasm_state *disasm, uint32_t waddr, bool magic)
87 {
88         if (!magic) {
89                 append(disasm, "rf%d", waddr);
90                 return;
91         }
92 
93         const char *name = v3d_qpu_magic_waddr_name(disasm->devinfo, waddr);
94         if (name)
95                 append(disasm, "%s", name);
96         else
97                 append(disasm, "waddr UNKNOWN %d", waddr);
98 }
99 
100 static void
v3d_qpu_disasm_add(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)101 v3d_qpu_disasm_add(struct disasm_state *disasm,
102                    const struct v3d_qpu_instr *instr)
103 {
104         bool has_dst = v3d_qpu_add_op_has_dst(instr->alu.add.op);
105         int num_src = v3d_qpu_add_op_num_src(instr->alu.add.op);
106 
107         append(disasm, "%s", v3d_qpu_add_op_name(instr->alu.add.op));
108         if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
109                 append(disasm, "%s", v3d_qpu_cond_name(instr->flags.ac));
110         append(disasm, "%s", v3d_qpu_pf_name(instr->flags.apf));
111         append(disasm, "%s", v3d_qpu_uf_name(instr->flags.auf));
112 
113         append(disasm, "  ");
114 
115         if (has_dst) {
116                 v3d_qpu_disasm_waddr(disasm, instr->alu.add.waddr,
117                                      instr->alu.add.magic_write);
118                 append(disasm, v3d_qpu_pack_name(instr->alu.add.output_pack));
119         }
120 
121         if (num_src >= 1) {
122                 if (has_dst)
123                         append(disasm, ", ");
124                 v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.a);
125                 append(disasm, "%s",
126                        v3d_qpu_unpack_name(instr->alu.add.a_unpack));
127         }
128 
129         if (num_src >= 2) {
130                 append(disasm, ", ");
131                 v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.b);
132                 append(disasm, "%s",
133                        v3d_qpu_unpack_name(instr->alu.add.b_unpack));
134         }
135 }
136 
137 static void
v3d_qpu_disasm_mul(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)138 v3d_qpu_disasm_mul(struct disasm_state *disasm,
139                    const struct v3d_qpu_instr *instr)
140 {
141         bool has_dst = v3d_qpu_mul_op_has_dst(instr->alu.mul.op);
142         int num_src = v3d_qpu_mul_op_num_src(instr->alu.mul.op);
143 
144         pad_to(disasm, 21);
145         append(disasm, "; ");
146 
147         append(disasm, "%s", v3d_qpu_mul_op_name(instr->alu.mul.op));
148         if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig))
149                 append(disasm, "%s", v3d_qpu_cond_name(instr->flags.mc));
150         append(disasm, "%s", v3d_qpu_pf_name(instr->flags.mpf));
151         append(disasm, "%s", v3d_qpu_uf_name(instr->flags.muf));
152 
153         if (instr->alu.mul.op == V3D_QPU_M_NOP)
154                 return;
155 
156         append(disasm, "  ");
157 
158         if (has_dst) {
159                 v3d_qpu_disasm_waddr(disasm, instr->alu.mul.waddr,
160                                      instr->alu.mul.magic_write);
161                 append(disasm, v3d_qpu_pack_name(instr->alu.mul.output_pack));
162         }
163 
164         if (num_src >= 1) {
165                 if (has_dst)
166                         append(disasm, ", ");
167                 v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.a);
168                 append(disasm, "%s",
169                        v3d_qpu_unpack_name(instr->alu.mul.a_unpack));
170         }
171 
172         if (num_src >= 2) {
173                 append(disasm, ", ");
174                 v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.b);
175                 append(disasm, "%s",
176                        v3d_qpu_unpack_name(instr->alu.mul.b_unpack));
177         }
178 }
179 
180 static void
v3d_qpu_disasm_sig_addr(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)181 v3d_qpu_disasm_sig_addr(struct disasm_state *disasm,
182                         const struct v3d_qpu_instr *instr)
183 {
184         if (disasm->devinfo->ver < 41)
185                 return;
186 
187         if (!instr->sig_magic)
188                 append(disasm, ".rf%d", instr->sig_addr);
189         else {
190                 const char *name =
191                         v3d_qpu_magic_waddr_name(disasm->devinfo,
192                                                  instr->sig_addr);
193                 if (name)
194                         append(disasm, ".%s", name);
195                 else
196                         append(disasm, ".UNKNOWN%d", instr->sig_addr);
197         }
198 }
199 
200 static void
v3d_qpu_disasm_sig(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)201 v3d_qpu_disasm_sig(struct disasm_state *disasm,
202                    const struct v3d_qpu_instr *instr)
203 {
204         const struct v3d_qpu_sig *sig = &instr->sig;
205 
206         if (!sig->thrsw &&
207             !sig->ldvary &&
208             !sig->ldvpm &&
209             !sig->ldtmu &&
210             !sig->ldtlb &&
211             !sig->ldtlbu &&
212             !sig->ldunif &&
213             !sig->ldunifrf &&
214             !sig->ldunifa &&
215             !sig->ldunifarf &&
216             !sig->wrtmuc) {
217                 return;
218         }
219 
220         pad_to(disasm, 41);
221 
222         if (sig->thrsw)
223                 append(disasm, "; thrsw");
224         if (sig->ldvary) {
225                 append(disasm, "; ldvary");
226                 v3d_qpu_disasm_sig_addr(disasm, instr);
227         }
228         if (sig->ldvpm)
229                 append(disasm, "; ldvpm");
230         if (sig->ldtmu) {
231                 append(disasm, "; ldtmu");
232                 v3d_qpu_disasm_sig_addr(disasm, instr);
233         }
234         if (sig->ldtlb) {
235                 append(disasm, "; ldtlb");
236                 v3d_qpu_disasm_sig_addr(disasm, instr);
237         }
238         if (sig->ldtlbu) {
239                 append(disasm, "; ldtlbu");
240                 v3d_qpu_disasm_sig_addr(disasm, instr);
241         }
242         if (sig->ldunif)
243                 append(disasm, "; ldunif");
244         if (sig->ldunifrf) {
245                 append(disasm, "; ldunifrf");
246                 v3d_qpu_disasm_sig_addr(disasm, instr);
247         }
248         if (sig->ldunifa)
249                 append(disasm, "; ldunifa");
250         if (sig->ldunifarf) {
251                 append(disasm, "; ldunifarf");
252                 v3d_qpu_disasm_sig_addr(disasm, instr);
253         }
254         if (sig->wrtmuc)
255                 append(disasm, "; wrtmuc");
256 }
257 
258 static void
v3d_qpu_disasm_alu(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)259 v3d_qpu_disasm_alu(struct disasm_state *disasm,
260                    const struct v3d_qpu_instr *instr)
261 {
262         v3d_qpu_disasm_add(disasm, instr);
263         v3d_qpu_disasm_mul(disasm, instr);
264         v3d_qpu_disasm_sig(disasm, instr);
265 }
266 
267 static void
v3d_qpu_disasm_branch(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)268 v3d_qpu_disasm_branch(struct disasm_state *disasm,
269                       const struct v3d_qpu_instr *instr)
270 {
271         append(disasm, "b");
272         if (instr->branch.ub)
273                 append(disasm, "u");
274         append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond));
275         append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign));
276 
277         switch (instr->branch.bdi) {
278         case V3D_QPU_BRANCH_DEST_ABS:
279                 append(disasm, "  zero_addr+0x%08x", instr->branch.offset);
280                 break;
281 
282         case V3D_QPU_BRANCH_DEST_REL:
283                 append(disasm, "  %d", instr->branch.offset);
284                 break;
285 
286         case V3D_QPU_BRANCH_DEST_LINK_REG:
287                 append(disasm, "  lri");
288                 break;
289 
290         case V3D_QPU_BRANCH_DEST_REGFILE:
291                 append(disasm, "  rf%d", instr->branch.raddr_a);
292                 break;
293         }
294 
295         if (instr->branch.ub) {
296                 switch (instr->branch.bdu) {
297                 case V3D_QPU_BRANCH_DEST_ABS:
298                         append(disasm, ", a:unif");
299                         break;
300 
301                 case V3D_QPU_BRANCH_DEST_REL:
302                         append(disasm, ", r:unif");
303                         break;
304 
305                 case V3D_QPU_BRANCH_DEST_LINK_REG:
306                         append(disasm, ", lri");
307                         break;
308 
309                 case V3D_QPU_BRANCH_DEST_REGFILE:
310                         append(disasm, ", rf%d", instr->branch.raddr_a);
311                         break;
312                 }
313         }
314 }
315 
316 const char *
v3d_qpu_decode(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)317 v3d_qpu_decode(const struct v3d_device_info *devinfo,
318                const struct v3d_qpu_instr *instr)
319 {
320         struct disasm_state disasm = {
321                 .string = rzalloc_size(NULL, 1),
322                 .offset = 0,
323                 .devinfo = devinfo,
324         };
325 
326         switch (instr->type) {
327         case V3D_QPU_INSTR_TYPE_ALU:
328                 v3d_qpu_disasm_alu(&disasm, instr);
329                 break;
330 
331         case V3D_QPU_INSTR_TYPE_BRANCH:
332                 v3d_qpu_disasm_branch(&disasm, instr);
333                 break;
334         }
335 
336         return disasm.string;
337 }
338 
339 /**
340  * Returns a string containing the disassembled representation of the QPU
341  * instruction.  It is the caller's responsibility to free the return value
342  * with ralloc_free().
343  */
344 const char *
v3d_qpu_disasm(const struct v3d_device_info * devinfo,uint64_t inst)345 v3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst)
346 {
347         struct v3d_qpu_instr instr;
348         bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr);
349         assert(ok); (void)ok;
350 
351         return v3d_qpu_decode(devinfo, &instr);
352 }
353 
354 void
v3d_qpu_dump(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)355 v3d_qpu_dump(const struct v3d_device_info *devinfo,
356              const struct v3d_qpu_instr *instr)
357 {
358         const char *decoded = v3d_qpu_decode(devinfo, instr);
359         fprintf(stderr, "%s", decoded);
360         ralloc_free((char *)decoded);
361 }
362