• 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(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 = v3d_qpu_magic_waddr_name(instr->sig_addr);
191                 if (name)
192                         append(disasm, ".%s", name);
193                 else
194                         append(disasm, ".UNKNOWN%d", instr->sig_addr);
195         }
196 }
197 
198 static void
v3d_qpu_disasm_sig(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)199 v3d_qpu_disasm_sig(struct disasm_state *disasm,
200                    const struct v3d_qpu_instr *instr)
201 {
202         const struct v3d_qpu_sig *sig = &instr->sig;
203 
204         if (!sig->thrsw &&
205             !sig->ldvary &&
206             !sig->ldvpm &&
207             !sig->ldtmu &&
208             !sig->ldtlb &&
209             !sig->ldtlbu &&
210             !sig->ldunif &&
211             !sig->ldunifrf &&
212             !sig->ldunifa &&
213             !sig->ldunifarf &&
214             !sig->wrtmuc) {
215                 return;
216         }
217 
218         pad_to(disasm, 41);
219 
220         if (sig->thrsw)
221                 append(disasm, "; thrsw");
222         if (sig->ldvary) {
223                 append(disasm, "; ldvary");
224                 v3d_qpu_disasm_sig_addr(disasm, instr);
225         }
226         if (sig->ldvpm)
227                 append(disasm, "; ldvpm");
228         if (sig->ldtmu) {
229                 append(disasm, "; ldtmu");
230                 v3d_qpu_disasm_sig_addr(disasm, instr);
231         }
232         if (sig->ldtlb) {
233                 append(disasm, "; ldtlb");
234                 v3d_qpu_disasm_sig_addr(disasm, instr);
235         }
236         if (sig->ldtlbu) {
237                 append(disasm, "; ldtlbu");
238                 v3d_qpu_disasm_sig_addr(disasm, instr);
239         }
240         if (sig->ldunif)
241                 append(disasm, "; ldunif");
242         if (sig->ldunifrf) {
243                 append(disasm, "; ldunifrf");
244                 v3d_qpu_disasm_sig_addr(disasm, instr);
245         }
246         if (sig->ldunifa)
247                 append(disasm, "; ldunifa");
248         if (sig->ldunifarf) {
249                 append(disasm, "; ldunifarf");
250                 v3d_qpu_disasm_sig_addr(disasm, instr);
251         }
252         if (sig->wrtmuc)
253                 append(disasm, "; wrtmuc");
254 }
255 
256 static void
v3d_qpu_disasm_alu(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)257 v3d_qpu_disasm_alu(struct disasm_state *disasm,
258                    const struct v3d_qpu_instr *instr)
259 {
260         v3d_qpu_disasm_add(disasm, instr);
261         v3d_qpu_disasm_mul(disasm, instr);
262         v3d_qpu_disasm_sig(disasm, instr);
263 }
264 
265 static void
v3d_qpu_disasm_branch(struct disasm_state * disasm,const struct v3d_qpu_instr * instr)266 v3d_qpu_disasm_branch(struct disasm_state *disasm,
267                       const struct v3d_qpu_instr *instr)
268 {
269         append(disasm, "b");
270         if (instr->branch.ub)
271                 append(disasm, "u");
272         append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond));
273         append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign));
274 
275         switch (instr->branch.bdi) {
276         case V3D_QPU_BRANCH_DEST_ABS:
277                 append(disasm, "  zero_addr+0x%08x", instr->branch.offset);
278                 break;
279 
280         case V3D_QPU_BRANCH_DEST_REL:
281                 append(disasm, "  %d", instr->branch.offset);
282                 break;
283 
284         case V3D_QPU_BRANCH_DEST_LINK_REG:
285                 append(disasm, "  lri");
286                 break;
287 
288         case V3D_QPU_BRANCH_DEST_REGFILE:
289                 append(disasm, "  rf%d", instr->branch.raddr_a);
290                 break;
291         }
292 
293         if (instr->branch.ub) {
294                 switch (instr->branch.bdu) {
295                 case V3D_QPU_BRANCH_DEST_ABS:
296                         append(disasm, ", a:unif");
297                         break;
298 
299                 case V3D_QPU_BRANCH_DEST_REL:
300                         append(disasm, ", r:unif");
301                         break;
302 
303                 case V3D_QPU_BRANCH_DEST_LINK_REG:
304                         append(disasm, ", lri");
305                         break;
306 
307                 case V3D_QPU_BRANCH_DEST_REGFILE:
308                         append(disasm, ", rf%d", instr->branch.raddr_a);
309                         break;
310                 }
311         }
312 }
313 
314 const char *
v3d_qpu_decode(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)315 v3d_qpu_decode(const struct v3d_device_info *devinfo,
316                const struct v3d_qpu_instr *instr)
317 {
318         struct disasm_state disasm = {
319                 .string = rzalloc_size(NULL, 1),
320                 .offset = 0,
321                 .devinfo = devinfo,
322         };
323 
324         switch (instr->type) {
325         case V3D_QPU_INSTR_TYPE_ALU:
326                 v3d_qpu_disasm_alu(&disasm, instr);
327                 break;
328 
329         case V3D_QPU_INSTR_TYPE_BRANCH:
330                 v3d_qpu_disasm_branch(&disasm, instr);
331                 break;
332         }
333 
334         return disasm.string;
335 }
336 
337 /**
338  * Returns a string containing the disassembled representation of the QPU
339  * instruction.  It is the caller's responsibility to free the return value
340  * with ralloc_free().
341  */
342 const char *
v3d_qpu_disasm(const struct v3d_device_info * devinfo,uint64_t inst)343 v3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst)
344 {
345         struct v3d_qpu_instr instr;
346         bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr);
347         assert(ok); (void)ok;
348 
349         return v3d_qpu_decode(devinfo, &instr);
350 }
351 
352 void
v3d_qpu_dump(const struct v3d_device_info * devinfo,const struct v3d_qpu_instr * instr)353 v3d_qpu_dump(const struct v3d_device_info *devinfo,
354              const struct v3d_qpu_instr *instr)
355 {
356         const char *decoded = v3d_qpu_decode(devinfo, instr);
357         fprintf(stderr, "%s", decoded);
358         ralloc_free((char *)decoded);
359 }
360