base/regdump.c: Dump ARM VFP/NEON registers with the correct source tag.
[catacomb] / base / regdump.c
CommitLineData
4bc8424a
MW
1/* -*-c-*-
2 *
3 * Register dumping and other diagnostic tools for assembler code
4 *
5 * (c) 2016 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include "config.h"
31
32#include <assert.h>
33#include <limits.h>
34#include <stdio.h>
35#include <string.h>
36
37#include <mLib/bits.h>
38#include <mLib/macros.h>
39
40#include "dispatch.h"
41#include "regdump.h"
42
43/*----- Low-level printing ------------------------------------------------*/
44
45/* Currently these are good for all of our targets. */
46#define STEP_8 1
47#define TY_HEX_8 uint8
48#define P_HEX_8 "0x%02x"
49#define TY_UNSGN_8 uint8
50#define P_UNSGN_8 "%3u"
51#define PV_CHR_8 " `%c'"
52#define PV_HEX_8 " %02x"
53#define PV_UNSGN_8 "%4u"
54
55#define STEP_16 2
56#define TY_HEX_16 uint16
57#define P_HEX_16 "0x%04x"
58#define TY_UNSGN_16 uint16
59#define P_UNSGN_16 "%5u"
60#define TY_SGN_16 int16
61#define P_SGN_16 "%6d"
62#define PV_HEX_16 " 0x%04x"
63#define PV_UNSGN_16 "%9u"
64#define PV_SGN_16 "%9d"
65
66#define STEP_32 4
67#define TY_HEX_32 uint32
68#define P_HEX_32 "0x%08x"
69#define TY_UNSGN_32 uint32
70#define P_UNSGN_32 "%10u"
71#define TY_SGN_32 int32
72#define P_SGN_32 "%11d"
73#define TY_FLT_32 float
74#define P_FLT_32 "%15.9g"
75#define PV_HEX_32 " 0x%08x"
76#define PV_UNSGN_32 "%19u"
77#define PV_SGN_32 "%19d"
78#define PV_FLT_32 "%19.9g"
79
80#if ULONG_MAX >> 31 > 0xffffffff
81# define PL64 "l"
82#else
83# define PL64 "ll"
84#endif
85#define STEP_64 8
86#define TY_HEX_64 uint64
87#define P_HEX_64 "0x%016"PL64"x"
88#define TY_UNSGN_64 uint64
89#define P_UNSGN_64 "%20"PL64"u"
90#define TY_SGN_64 int64
91#define P_SGN_64 "%20"PL64"d"
92#define TY_FLT_64 double
93#define P_FLT_64 "%24.17g"
94#define PV_HEX_64 " 0x%016"PL64"x"
95#define PV_UNSGN_64 "%39"PL64"u"
96#define PV_SGN_64 "%39"PL64"d"
97#define PV_FLT_64 "%39.17g"
98
99#if CPUFAM_X86
100# define STEP_80 12
101#endif
102#if CPUFAM_AMD64
103# define STEP_80 16
104#endif
105#define TY_FLT_80 long double
106#define P_FLT_80 "%29.21Lg"
107#define PV_FLT_80 P_FLT_80
108
109#if CPUFAM_X86 || CPUFAM_AMD64
110# define ARCH_FORMATS(_) \
111 _(80, FLT)
112#endif
113#ifndef ARCH_FORMATS
114# define ARCH_FORMATS(_)
115#endif
116
117#define FORMATS(_) \
118 ARCH_FORMATS(_) \
119 _(64, HEX) _(64, FLT) _(64, UNSGN) _(64, SGN) \
120 _(32, HEX) _(32, FLT) _(32, UNSGN) _(32, SGN) \
121 _(16, HEX) _(16, UNSGN) _(16, SGN) \
122 _(8, HEX) _(8, CHR) _(8, UNSGN)
123
124struct fmtinfo {
125 const unsigned char *p;
126 unsigned wd, f;
127#define FMTF_VECTOR 1u
128};
129
130#define FMTFUNC_STD(w, fmt) \
131 static void dump_##fmt##_##w(struct fmtinfo *fmt) \
132 { \
133 TY_##fmt##_##w x = *(const TY_##fmt##_##w *)fmt->p; \
134 \
135 if (fmt->f&FMTF_VECTOR) printf(PV_##fmt##_##w, x); \
136 else printf(P_##fmt##_##w, x); \
137 fmt->p += STEP_##w; fmt->wd += 8*STEP_##w; \
138 }
139
140#define FMTFUNC_HEX(w) FMTFUNC_STD(w, HEX)
141#define FMTFUNC_UNSGN(w) FMTFUNC_STD(w, UNSGN)
142#define FMTFUNC_SGN(w) FMTFUNC_STD(w, SGN)
143#define FMTFUNC_FLT(w) FMTFUNC_STD(w, FLT)
144#define FMTFUNC_CHR(w)
145
146static void dump_CHR_8(struct fmtinfo *fmt)
147{
148 unsigned char x = *(const unsigned char *)fmt->p;
149
150 if (x < 32 || x > 126) printf("\\x%02x", x);
151 else printf(" `%c'", x);
152 fmt->p += 1; fmt->wd += 8;
153}
154
155#define FMTFUNC(w, fmt) FMTFUNC_##fmt(w)
156FORMATS(FMTFUNC)
157#undef FMTFUNC
158
159static const struct fmttab {
160 uint32 mask;
161 void (*fmt)(struct fmtinfo *);
162} fmttab[] = {
163#define FMTTAB(wd, fmt) { REGF_##fmt | REGF_##wd, dump_##fmt##_##wd },
164 FORMATS(FMTTAB)
165#undef FMTTAB
166 { 0, 0 }
167};
168
169/*----- Common subroutines ------------------------------------------------*/
170
171/* --- @regwd@ --- *
172 *
173 * Arguments: @uint32 f@ = format control word; see @REGF_...@
174 *
175 * Returns: The actual width of the operand, in bits.
176 *
177 * Use: If the operand is a vector (the @REGF_WDMASK@ field is
178 * nonzero) then return the width it denotes; otherwise, return
179 * the largest width implied by the @REGF_TYMASK@ field.
180 */
181
182static unsigned regwd(uint32 f)
183{
184 unsigned wd = 1 << ((f&REGF_WDMASK) >> REGF_WDSHIFT);
185
186 if (wd > 1) return (wd);
187 else if (f&REGF_80) return (80);
188 else if (f&REGF_64) return (64);
189 else if (f&REGF_32) return (32);
190 else if (f&REGF_16) return (16);
191 else if (f&REGF_8) return (8);
192 else { assert(0); return (1); }
193}
194
195/* --- @regname@ --- *
196 *
197 * Arguments: @char *buf = pointer to output buffer@
198 * @uint32 f@ = format control word; see @REGF_...@
199 *
200 * Returns: Pointer to name string.
201 *
202 * Use: Return a pointer to the name of the register implied by @f@,
203 * or null if there is no register. Systematic register names
204 * can be built in the provided buffer.
205 */
206
207static const char *regname(char *buf, uint32 f)
208{
209 unsigned wd = regwd(f);
210 unsigned src = f&REGF_SRCMASK;
211 unsigned ix = (f&REGF_IXMASK) >> REGF_IXSHIFT;
212 char *p = buf;
213
214 switch (src) {
215
216 case REGSRC_ABS:
217 return (0);
218
219#if CPUFAM_X86 || CPUFAM_AMD64
220 case REGSRC_GP:
221 if (ix == REGIX_FLAGS) {
222 if (wd == 64) *p++ = 'r';
223 else if (wd == 32) *p++ = 'e';
224 else if (wd != 16) assert(0);
225 p += sprintf(p, "flags");
226#if CPUFAM_AMD64
227 } else if (REGIX_R8 <= ix && ix <= REGIX_R15) {
228 p += sprintf(p, "r%u", ix - REGIX_R8 + 8);
229 switch (wd) {
230 case 64: break;
231 case 32: *p++ = 'd'; break;
232 case 16: *p++ = 'w'; break;
233 case 8: *p++ = 'l'; break;
234 default: assert(0);
235 }
236# endif
237 } else {
238 if (wd == 64) *p++ = 'r';
239 else if (wd == 32) *p++ = 'e';
240 switch (ix) {
241 case REGIX_IP: *p++ = 'i'; *p++ = 'p'; goto longreg;
242 case REGIX_AX: *p++ = 'a'; goto shortreg;
243 case REGIX_BX: *p++ = 'b'; goto shortreg;
244 case REGIX_CX: *p++ = 'c'; goto shortreg;
245 case REGIX_DX: *p++ = 'd'; goto shortreg;
246 case REGIX_SI: *p++ = 's'; *p++ = 'i'; goto longreg;
247 case REGIX_DI: *p++ = 'd'; *p++ = 'i'; goto longreg;
248 case REGIX_BP: *p++ = 'b'; *p++ = 'p'; goto longreg;
249 case REGIX_SP: *p++ = 's'; *p++ = 'p'; goto longreg;
250 default: assert(0);
251 }
252 if (0) {
253 shortreg:
254 switch (wd) {
255 case 64:
256 case 32:
257 case 16: *p++ = 'x'; break;
258 case 8: *p++ = 'l'; break;
259 default: assert(0);
260 }
261 } else {
262 longreg:
263 switch (wd) {
264 case 64:
265 case 32:
266 case 16: break;
267 case 8: *p++ = 'l'; break;
268 default: assert(0);
269 }
270 }
271 }
272 *p++ = 0;
273 return (buf);
274
275 case REGSRC_SEG:
276 assert(wd == 16);
277 switch (ix) {
278 case REGIX_CS: sprintf(buf, "cs"); break;
279 case REGIX_DS: sprintf(buf, "ds"); break;
280 case REGIX_SS: sprintf(buf, "ss"); break;
281 case REGIX_ES: sprintf(buf, "es"); break;
282 case REGIX_FS: sprintf(buf, "fs"); break;
283 case REGIX_GS: sprintf(buf, "gs"); break;
284 default: assert(0);
285 }
286 return (buf);
287
288 case REGSRC_STMMX:
289 if (ix == REGIX_FPFLAGS) return (0);
290 if (f&REGF_80) sprintf(buf, "st(%u)", ix);
291 else sprintf(buf, "mm%u", ix);
292 return (buf);
293
294 case REGSRC_SIMD:
295 if (ix == REGIX_FPFLAGS) return (0);
296 switch (wd) {
297 case 32: case 64: case 128: sprintf(buf, "xmm%u", ix); break;
298 case 256: sprintf(buf, "ymm%u", ix); break;
299 default: assert(0);
300 }
301 return (buf);
302#endif
303
304#if CPUFAM_ARMEL
305 case REGSRC_GP:
306 if (ix == REGIX_CPSR) sprintf(buf, "cpsr");
307 else if (ix == 15) sprintf(buf, "pc");
308 else sprintf(buf, "r%u", ix);
309 return (buf);
310 case REGSRC_FP:
311 if (ix == REGIX_FPSCR) sprintf(buf, "fpscr");
312 else {
313 switch (wd) {
314 case 32: *p++ = 's'; break;
315 case 64: *p++ = 'd'; break;
316 case 128: *p++ = 'q'; break;
317 default: assert(0);
318 }
319 p += sprintf(p, "%u", ix);
320 *p++ = 0;
321 }
322 return (buf);
323#endif
324
325#if CPUFAM_ARM64
326 case REGSRC_GP:
327 if (ix == REGIX_PC) sprintf(buf, "pc");
328 else if (ix == REGIX_NZCV) sprintf(buf, "nzcv");
329 else if (ix == 31 && wd == 64) sprintf(buf, "sp");
330 else {
331 switch (wd) {
332 case 32: *p++ = 'w'; break;
333 case 64: *p++ = 'x'; break;
334 default: assert(0);
335 }
336 p += sprintf(p, "%u", ix);
337 *p++ = 0;
338 }
339 return (buf);
340 case REGSRC_FP:
341 if (ix == REGIX_FPFLAGS) sprintf(buf, "fpflags");
342 else {
343 if (f&REGF_WDMASK)
344 *p++ = 'v';
345 else switch (wd) {
346 case 8: *p++ = 'b'; break;
347 case 16: *p++ = 'h'; break;
348 case 32: *p++ = 's'; break;
349 case 64: *p++ = 'd'; break;
350 default: assert(0);
351 }
352 p += sprintf(p, "%u", ix);
353 *p++ = 0;
354 }
355 return (buf);
356#endif
357
358 default:
359 assert(0);
360 return ("???");
361 }
362}
363
364/*----- x86 and AMD64 -----------------------------------------------------*/
365
366#if CPUFAM_X86 || CPUFAM_AMD64
367
368#if CPUFAM_X86
369# define P_HEX_GP "0x%08x"
370# define GP(gp) (gp).u32
371#endif
372#if CPUFAM_AMD64
373# define P_HEX_GP "0x%016"PL64"x"
374# define GP(gp) (gp).u64
375#endif
376
377void regdump_init(void) { ; }
378
379static void dump_flags(const char *lbl, const char *reg, gpreg f)
380{
381 printf(";; ");
382 if (lbl) printf("%s: ", lbl);
383 if (reg) printf("%s = ", reg);
384 printf(""P_HEX_GP"\n", GP(f));
385 printf(";;\t\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n",
386 (GP(f) >> 0)&1u ? '+' : '-',
387 (GP(f) >> 2)&1u ? '+' : '-',
388 (GP(f) >> 4)&1u ? '+' : '-',
389 (GP(f) >> 6)&1u ? '+' : '-',
390 (GP(f) >> 7)&1u ? '+' : '-',
391 (GP(f) >> 10)&1u ? '+' : '-',
392 (GP(f) >> 11)&1u ? '+' : '-');
393 printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt "
394 "%crf %cvm %cac %cvif %cvip %cid\n",
395 (GP(f) >> 8)&1u ? '+' : '-',
396 (GP(f) >> 9)&1u ? '+' : '-',
397 (int)((GP(f) >> 12)&1u),
398 (GP(f) >> 14)&1u ? '+' : '-',
399 (GP(f) >> 16)&1u ? '+' : '-',
400 (GP(f) >> 17)&1u ? '+' : '-',
401 (GP(f) >> 18)&1u ? '+' : '-',
402 (GP(f) >> 19)&1u ? '+' : '-',
403 (GP(f) >> 20)&1u ? '+' : '-',
404 (GP(f) >> 21)&1u ? '+' : '-');
405}
406
407static const char
408 *pcmap[] = { "sgl", "???", "dbl", "ext" },
409 *rcmap[] = { "nr", "-∞", "+∞", "0" };
410
411static void dump_fpflags(const char *lbl, const struct fxsave *fx)
412{
413 unsigned top = (fx->fsw >> 11)&7u;
414 unsigned tag = fx->ftw;
415 int skip = lbl ? strlen(lbl) + 2 : 0;
416
417 printf(";; ");
418 if (lbl) printf("%s: ", lbl);
419
420 printf(" fcw = 0x%04x: "
421 "%cim %cdm %czm %com %cum %cpm pc=%s rc=%s %cx\n",
422 fx->fcw,
423 (fx->fcw >> 0)&1u ? '+' : '-',
424 (fx->fcw >> 1)&1u ? '+' : '-',
425 (fx->fcw >> 2)&1u ? '+' : '-',
426 (fx->fcw >> 3)&1u ? '+' : '-',
427 (fx->fcw >> 4)&1u ? '+' : '-',
428 (fx->fcw >> 5)&1u ? '+' : '-',
429 pcmap[(fx->fcw >> 8)&3u],
430 rcmap[(fx->fcw >> 10)&3u],
431 (fx->fcw >> 12)&1u ? '+' : '-');
432 printf(";; %*s fsw = 0x%04x: "
433 "%cie %cde %cze %coe %cue %cpe %csf %ces %cc0 %cc1 %cc2 %cc3 "
434 "top=%d %cb\n",
435 skip, "",
436 fx->fsw,
437 (fx->fsw >> 0)&1u ? '+' : '-',
438 (fx->fsw >> 1)&1u ? '+' : '-',
439 (fx->fsw >> 2)&1u ? '+' : '-',
440 (fx->fsw >> 3)&1u ? '+' : '-',
441 (fx->fsw >> 4)&1u ? '+' : '-',
442 (fx->fsw >> 5)&1u ? '+' : '-',
443 (fx->fsw >> 6)&1u ? '+' : '-',
444 (fx->fsw >> 7)&1u ? '+' : '-',
445 (fx->fsw >> 8)&1u ? '+' : '-',
446 (fx->fsw >> 9)&1u ? '+' : '-',
447 (fx->fsw >> 10)&1u ? '+' : '-',
448 (fx->fsw >> 14)&1u ? '+' : '-',
449 top,
450 (fx->fsw >> 15)&1u ? '+' : '-');
451 printf(";; %*s ftw = 0x%02x\n", skip, "", tag);
452}
453
454static void dump_mxflags(const char *lbl, const struct fxsave *fx)
455{
456 printf(";; ");
457 if (lbl) printf("%s: ", lbl);
458
459 printf(" mxcsr = 0x%08x\n"
460 ";;\t\tmask = %cim %cdm %czm %com %cum %cpm\n"
461 ";;\t\t exc = %cie %cde %cze %coe %cue %cpe\n"
462 ";;\t\tmisc = %cdaz %cftz rc=%s\n",
463 fx->mxcsr,
464 (fx->mxcsr >> 7)&1u ? '+' : '-',
465 (fx->mxcsr >> 8)&1u ? '+' : '-',
466 (fx->mxcsr >> 9)&1u ? '+' : '-',
467 (fx->mxcsr >> 10)&1u ? '+' : '-',
468 (fx->mxcsr >> 11)&1u ? '+' : '-',
469 (fx->mxcsr >> 12)&1u ? '+' : '-',
470 (fx->mxcsr >> 0)&1u ? '+' : '-',
471 (fx->mxcsr >> 1)&1u ? '+' : '-',
472 (fx->mxcsr >> 2)&1u ? '+' : '-',
473 (fx->mxcsr >> 3)&1u ? '+' : '-',
474 (fx->mxcsr >> 4)&1u ? '+' : '-',
475 (fx->mxcsr >> 5)&1u ? '+' : '-',
476 (fx->mxcsr >> 6)&1u ? '+' : '-',
477 (fx->mxcsr >> 15)&1u ? '+' : '-',
478 rcmap[(fx->mxcsr >> 13)&3u]);
479}
480
481#if CPUFAM_X86
482# define REGF_GPWD REGF_32
483#endif
484#if CPUFAM_AMD64
485# define REGF_GPWD REGF_64
486#endif
487
488void regdump_gp(const struct regmap *map)
489{
490 unsigned i;
491
492 printf(";; General-purpose registers:\n");
493 for (i = REGIX_AX; i < REGIX_GPLIM; i++)
494 regdump(map, 0,
495 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_GPWD | REGSRC_GP | i);
496 regdump(map, 0, REGF_HEX | REGF_GPWD | REGSRC_GP | REGIX_IP);
497
498 printf(";; Segment registers:\n");
499 for (i = 0; i < REGIX_SEGLIM; i++)
500 regdump(map, 0, REGF_HEX | REGF_16 | REGSRC_SEG | i);
501
502 printf(";; Flags:\n");
503 regdump(map, 0, REGSRC_GP | REGF_GPWD | REGIX_FLAGS);
504}
505
506void regdump_fp(const struct regmap *map)
507{
508 unsigned top = (map->fx->fsw >> 11)&7u;
509 unsigned tag = map->fx->ftw;
510 unsigned i;
511
512 printf(";; Floating-point/MMX registers:\n");
513 if (!top && tag == 0xff)
514 for (i = 0; i < 8; i++)
515 regdump(map, 0,
516 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_CHR |
517 REGF_32 | REGF_16 | REGF_8 |
518 REGSRC_STMMX | i | (6 << REGF_WDSHIFT));
519 if (tag)
520 for (i = 0; i < 8; i++)
521 regdump(map, 0, REGF_FLT | REGF_80 | REGSRC_STMMX | i);
522
523 printf(";; Floating-point state:\n");
524 dump_fpflags(0, map->fx);
525}
526
527void regdump_simd(const struct regmap *map)
528{
529 unsigned f = REGF_HEX | REGF_FLT | REGF_UNSGN | REGF_SGN | REGF_CHR |
530 REGF_64 | REGF_32 | REGF_16 | REGF_8 |
531 REGSRC_SIMD;
532 unsigned i;
533
534 if (map->avx) f |= 8 << REGF_WDSHIFT;
535 else f |= 7 << REGF_WDSHIFT;
536
537 printf(";; SSE/AVX registers:\n");
538 for (i = 0; i < N(map->fx->xmm); i++)
539 regdump(map, 0, f | i);
540
541 printf(";; SSE/AVX floating-point state:\n");
542 dump_mxflags(0, map->fx);
543}
544
545#endif
546
547/*----- ARM32 -------------------------------------------------------------*/
548
549#if CPUFAM_ARMEL
550
551unsigned regdump__flags = 0;
552
553void regdump_init(void)
554{
555 if (cpu_feature_p(CPUFEAT_ARM_VFP)) regdump__flags |= REGF_VFP;
556 if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32;
557}
558
559static void dump_flags(const char *lbl, unsigned f)
560{
561 static const char
562 *modetab[] = { "?00", "?01", "?02", "?03", "?04", "?05", "?06", "?07",
563 "?08", "?09", "?10", "?11", "?12", "?13", "?14", "?15",
564 "usr", "fiq", "irq", "svc", "?20", "?21", "mon", "abt",
565 "?24", "?25", "hyp", "und", "?28", "?29", "?30", "sys" },
566 *condtab[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
567 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" };
568
569 printf(";; ");
570 if (lbl) printf("%s: ", lbl);
571 printf(" cpsr = 0x%08x\n", f);
572 printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n",
573 (f >> 31)&1u ? '+' : '-',
574 (f >> 30)&1u ? '+' : '-',
575 (f >> 29)&1u ? '+' : '-',
576 (f >> 28)&1u ? '+' : '-',
577 (f >> 27)&1u ? '+' : '-',
578 (f >> 19)&1u ? '1' : '0',
579 (f >> 18)&1u ? '1' : '0',
580 (f >> 17)&1u ? '1' : '0',
581 (f >> 16)&1u ? '1' : '0');
582 printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n",
583 (f >> 24)&1u ? '+' : '-',
584 condtab[(f >> 12)&15u],
585 (f >> 11)&1u ? '1' : '0',
586 (f >> 10)&1u ? '1' : '0',
587 (f >> 26)&1u ? '1' : '0',
588 (f >> 25)&1u ? '1' : '0',
589 (f >> 9)&1u ? '+' : '-',
590 (f >> 8)&1u ? '+' : '-',
591 (f >> 7)&1u ? '+' : '-',
592 (f >> 6)&1u ? '+' : '-',
593 (f >> 5)&1u ? '+' : '-',
594 modetab[(f >> 0)&31u]);
595}
596
597static void dump_fpflags(const char *lbl, unsigned f)
598{
599 static const char *rcmap[] = { "nr", "+∞", "-∞", "0" };
600
601 printf(";; ");
602 if (lbl) printf("%s: ", lbl);
603 printf(" fpscr = 0x%08x\n", f);
604 printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n",
605 (f >> 31)&1u ? '+' : '-',
606 (f >> 30)&1u ? '+' : '-',
607 (f >> 29)&1u ? '+' : '-',
608 (f >> 28)&1u ? '+' : '-',
609 (f >> 27)&1u ? '+' : '-');
610 printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n",
611 (f >> 15)&1u ? '+' : '-',
612 (f >> 12)&1u ? '+' : '-',
613 (f >> 11)&1u ? '+' : '-',
614 (f >> 10)&1u ? '+' : '-',
615 (f >> 9)&1u ? '+' : '-',
616 (f >> 8)&1u ? '+' : '-');
617 printf(";;\t\terror: %cide %cixe %cufe %cofe %cdze %cioe\n",
618 (f >> 7)&1u ? '+' : '-',
619 (f >> 4)&1u ? '+' : '-',
620 (f >> 3)&1u ? '+' : '-',
621 (f >> 2)&1u ? '+' : '-',
622 (f >> 1)&1u ? '+' : '-',
623 (f >> 0)&1u ? '+' : '-');
624 printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n",
625 (f >> 26)&1u ? '+' : '-',
626 (f >> 25)&1u ? '+' : '-',
627 (f >> 24)&1u ? '+' : '-',
628 rcmap[(f >> 22)&3u],
629 (f >> 20)&3u,
630 (f >> 16)&7u);
631}
632
633void regdump_gp(const struct regmap *map)
634{
635 unsigned i;
636
637 printf(";; General-purpose registers:\n");
638 for (i = 0; i < 16; i++)
639 regdump(map, 0,
640 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_32 | REGSRC_GP | i);
641
642 printf(";; Flags:\n");
643 regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_CPSR);
644}
645
646void regdump_fp(const struct regmap *map)
647{
648 unsigned i, n;
649
650 if (!(regdump__flags&REGF_VFP)) {
651 printf(";; Floating-point and SIMD not available\n");
652 return;
653 }
654
655 printf(";; Floating-point/SIMD registers:\n");
656 if (regdump__flags&REGF_D32) n = 32;
657 else n = 16;
658 for (i = 0; i < n; i++)
659 regdump(map, 0,
660 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR |
661 REGF_64 | REGF_32 | REGF_16 | REGF_8 |
24099531 662 REGSRC_FP | i | (6 << REGF_WDSHIFT));
4bc8424a
MW
663
664 printf(";; Floating-point state:\n");
665 dump_fpflags(0, map->fp->fpscr);
666}
667
668void regdump_simd(const struct regmap *map) { ; }
669
670#endif
671
672/*----- ARM64 -------------------------------------------------------------*/
673
674#if CPUFAM_ARM64
675
676void regdump_init(void) { ; }
677
678static void dump_flags(const char *lbl, unsigned f)
679{
680 printf(";; ");
681 if (lbl) printf("%s: ", lbl);
682 printf(" nzcv = 0x%08x\n", f);
683 printf(";;\t\tuser: %cn %cz %cc %cv\n",
684 (f >> 31)&1u ? '+' : '-',
685 (f >> 30)&1u ? '+' : '-',
686 (f >> 29)&1u ? '+' : '-',
687 (f >> 28)&1u ? '+' : '-');
688}
689
690static void dump_fpflags(const char *lbl, const struct fpsave *fp)
691{
692 static const char *rcmap[] = { "nr", "+∞", "-∞", "0" };
693 int skip = lbl ? strlen(lbl) + 2 : 0;
694
695 printf(";; ");
696 if (lbl) printf("%s: ", lbl);
697 printf(" fpsr = 0x%08x\n", fp->fpsr);
698 printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n",
699 (fp->fpsr >> 31)&1u ? '+' : '-',
700 (fp->fpsr >> 30)&1u ? '+' : '-',
701 (fp->fpsr >> 29)&1u ? '+' : '-',
702 (fp->fpsr >> 28)&1u ? '+' : '-',
703 (fp->fpsr >> 27)&1u ? '+' : '-');
704 printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n",
705 (fp->fpsr >> 7)&1u ? '+' : '-',
706 (fp->fpsr >> 4)&1u ? '+' : '-',
707 (fp->fpsr >> 3)&1u ? '+' : '-',
708 (fp->fpsr >> 2)&1u ? '+' : '-',
709 (fp->fpsr >> 1)&1u ? '+' : '-',
710 (fp->fpsr >> 0)&1u ? '+' : '-');
711 printf(";; %*s fpcr = 0x%08x\n", skip, "", fp->fpcr);
712 printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n",
713 (fp->fpcr >> 15)&1u ? '+' : '-',
714 (fp->fpcr >> 12)&1u ? '+' : '-',
715 (fp->fpcr >> 11)&1u ? '+' : '-',
716 (fp->fpcr >> 10)&1u ? '+' : '-',
717 (fp->fpcr >> 9)&1u ? '+' : '-',
718 (fp->fpcr >> 8)&1u ? '+' : '-');
719 printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n",
720 (fp->fpcr >> 26)&1u ? '+' : '-',
721 (fp->fpcr >> 25)&1u ? '+' : '-',
722 (fp->fpcr >> 24)&1u ? '+' : '-',
723 rcmap[(fp->fpcr >> 22)&3u],
724 (fp->fpcr >> 20)&3u,
725 (fp->fpcr >> 16)&7u);
726}
727
728void regdump_gp(const struct regmap *map)
729{
730 unsigned i;
731
732 printf(";; General-purpose registers:\n");
733 for (i = 0; i < 32; i++)
734 regdump(map, 0,
735 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_64 | REGSRC_GP | i);
736 regdump(map, 0, REGF_HEX | REGF_64 | REGSRC_GP | REGIX_PC);
737
738 printf(";; Flags:\n");
739 regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_NZCV);
740}
741
742void regdump_fp(const struct regmap *map)
743{
744 unsigned i;
745
746 printf(";; Floating-point/SIMD registers:\n");
747 for (i = 0; i < 32; i++)
748 regdump(map, 0,
749 REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR |
750 REGF_64 | REGF_32 | REGF_16 | REGF_8 |
751 REGSRC_SIMD | i | (7 << REGF_WDSHIFT));
752
753 printf(";; Floating-point state:\n");
754 dump_fpflags(0, map->fp);
755}
756
757void regdump_simd(const struct regmap *map) { ; }
758
759#endif
760
761/*----- The main entry point ----------------------------------------------*/
762
763/* --- @regdump@ --- *
764 *
765 * Arguments: @const void *base@ = pointer to base structure, corresponding
766 * to the @REGF_SRCMASK@ part of @f@
767 * @const char *lbl@ = label to print
768 * @uint32 f@ = format control word; see @REGF_...@
769 *
770 * Returns: ---
771 *
772 * Use: Dump a register value, or chunk of memory.
773 *
774 * This function is not usually called directly; instead, use
775 * the `reg' or `mem' assembler macros.
776 */
777
778void regdump(const void *base, const char *lbl, uint32 f)
779{
780 unsigned ix = (f&REGF_IXMASK) >> REGF_IXSHIFT;
781 unsigned wd = 1 << ((f&REGF_WDMASK) >> REGF_WDSHIFT);
782 unsigned fmt, ty;
783 uint32 fmtbit, tybit;
784 const void *p;
785 char regbuf[8]; const char *reg = regname(regbuf, f);
786 const struct regmap *map;
787 const struct fmttab *tab;
788 struct fmtinfo fi;
789 int firstp = 1;
790 int skip;
791 size_t n;
792
793#if CPUFAM_X86 || CPUFAM_AMD64
794 union vreg vr;
795#endif
796
797 if (reg) {
798 n = strlen(reg);
799 if (n < 7) {
800 memmove(regbuf + 7 - n, reg, n + 1);
801 memset(regbuf, ' ', 7 - n);
802 }
803 }
804
805 switch (f&REGF_SRCMASK) {
806 case REGSRC_ABS:
807 p = base;
808 break;
809
810#if CPUFAM_X86 || CPUFAM_AMD64
811 case REGSRC_GP:
812 map = (const struct regmap *)base;
813 if (ix == REGIX_FLAGS && !(f&REGF_FMTMASK))
814 { dump_flags(lbl, reg, map->gp->gp[REGIX_FLAGS]); return; }
815 p = &map->gp->gp[ix];
816 break;
817 case REGSRC_SEG:
818 map = (const struct regmap *)base;
819 assert(wd == 1); assert((f&REGF_TYMASK) == REGF_16);
820 p = &map->gp->seg[ix];
821 break;
822 case REGSRC_STMMX:
823 map = (const struct regmap *)base;
824 if (ix == REGIX_FPFLAGS)
825 { assert(!(f&REGF_FMTMASK)); dump_fpflags(lbl, map->fx); return; }
826 if (!((map->fx->ftw << ix)&128u)) {
827 printf(";; ");
828 if (lbl) printf("%s: ", lbl);
829 if (reg) printf("%s = ", reg);
830 printf(" dead\n");
831 return;
832 }
833 p = &map->fx->stmmx[ix];
834 break;
835 case REGSRC_SIMD:
836 map = (const struct regmap *)base;
837 if (ix == REGIX_FPFLAGS)
838 { assert(!(f&REGF_FMTMASK)); dump_mxflags(lbl, map->fx); return; }
839 if (wd <= 128)
840 p = &map->fx->xmm[ix];
841 else {
842 vr.v128[0] = map->fx->xmm[ix];
843 vr.v128[1] = map->avx->ymmh[ix];
844 assert(wd == 256);
845 p = &vr;
846 }
847 break;
848#endif
849
850#if CPUFAM_ARMEL
851 case REGSRC_GP:
852 map = (const struct regmap *)base;
853 if (ix == REGIX_CPSR && !(f&REGF_FMTMASK))
854 { dump_flags(lbl, map->gp->r[REGIX_CPSR].u32); return; }
855 p = &map->gp->r[ix];
856 break;
857 case REGSRC_FP:
858 case REGSRC_SIMD:
859 map = (const struct regmap *)base;
860 if (ix == REGIX_FPSCR) {
861 assert(!(f&REGF_FMTMASK));
862 dump_fpflags(lbl, map->fp->fpscr);
863 return;
864 }
865 switch (regwd(f)) {
866 case 32: p = &map->fp->u.s[ix]; break;
867 case 64: p = &map->fp->u.d[ix]; break;
868 case 128: p = &map->fp->u.q[ix]; break;
869 default: assert(0);
870 }
871 break;
872#endif
873
874#if CPUFAM_ARM64
875 case REGSRC_GP:
876 map = (const struct regmap *)base;
877 if (ix == REGIX_NZCV && !(f&REGF_FMTMASK))
878 { dump_flags(lbl, map->gp->r[REGIX_NZCV].u64); return; }
879 p = &map->gp->r[ix];
880 break;
881 case REGSRC_FP:
882 case REGSRC_SIMD:
883 map = (const struct regmap *)base;
884 if (ix == REGIX_FPFLAGS)
885 { assert(!(f&REGF_FMTMASK)); dump_fpflags(lbl, map->fp); return; }
886 p = &map->fp->v[ix];
887 break;
888#endif
889
890 default:
891 assert(0);
892 }
893
894 skip = (lbl ? strlen(lbl) + 2 : 0) + (reg ? strlen(reg) : 0);
895 fi.f = 0; if (wd > 1) fi.f |= FMTF_VECTOR;
896
897 for (ty = (f&REGF_TYMASK) >> REGF_TYSHIFT,
898 tybit = 1 << REGF_TYSHIFT;
899 ty;
900 ty >>= 1, tybit <<= 1) {
901 if (!(ty&1u)) continue;
902
903 for (fmt = (f&REGF_FMTMASK) >> REGF_FMTSHIFT,
904 fmtbit = 1 << REGF_FMTSHIFT;
905 fmt;
906 fmt >>= 1, fmtbit <<= 1) {
907
908 if (!(fmt&1u)) continue;
909
910 for (tab = fmttab; tab->mask; tab++)
911 if (tab->mask == (fmtbit | tybit)) goto found;
912 continue;
913 found:
914
915 if (firstp) {
916 printf(";;");
917 if (lbl) printf(" %s:", lbl);
918 if (reg) printf(" %s =", reg);
919 firstp = 0;
920 } else if (wd > 1)
921 printf("\n;; %*s =", skip, "");
922 else
923 fputs(" =", stdout);
924
925 fi.p = p; fi.wd = 0;
926 while (fi.wd < wd) { putchar(' '); tab->fmt(&fi); }
927 }
928 }
929 putchar('\n');
930}
931
932/*----- Other random utilities --------------------------------------------*/
933
934/* --- @regdump_freshline@ --- *
935 *
936 * Arguments: ---
937 *
938 * Returns: ---
939 *
940 * Use: Begin a fresh line of output.
941 */
942
943void regdump_freshline(void) { putchar('\n'); }
944
945/*----- That's all, folks -------------------------------------------------*/