chiark / gitweb /
Merge branch '2.4.x' into 2.5.x
[catacomb] / base / regdump.c
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
124 struct 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
146 static 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)
156 FORMATS(FMTFUNC)
157 #undef FMTFUNC
158
159 static 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
182 static 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
207 static 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
377 void regdump_init(void) { ; }
378
379 static 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
407 static const char
408   *pcmap[] = { "sgl", "???", "dbl", "ext" },
409   *rcmap[] = { "nr", "-∞", "+∞", "0" };
410
411 static 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
454 static 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
488 void 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
506 void 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
527 void 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
551 unsigned regdump__flags = 0;
552
553 void 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
559 static 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
597 static 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
633 void 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
646 void 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 |
662             REGSRC_SIMD | i | (6 << REGF_WDSHIFT));
663
664   printf(";; Floating-point state:\n");
665   dump_fpflags(0, map->fp->fpscr);
666 }
667
668 void regdump_simd(const struct regmap *map) { ; }
669
670 #endif
671
672 /*----- ARM64 -------------------------------------------------------------*/
673
674 #if CPUFAM_ARM64
675
676 void regdump_init(void) { ; }
677
678 static 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
690 static 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
728 void 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
742 void 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
757 void 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
778 void 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
943 void regdump_freshline(void) { putchar('\n'); }
944
945 /*----- That's all, folks -------------------------------------------------*/