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