0.8.20.14:
[sbcl.git] / src / compiler / disassem.lisp
1 ;;;; machine-independent disassembler
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 (in-package "SB!DISASSEM")
13 \f
14 ;;; types and defaults
15
16 (def!constant label-column-width 7)
17
18 (deftype text-width () '(integer 0 1000))
19 (deftype alignment () '(integer 0 64))
20 (deftype offset () '(signed-byte 24))
21 (deftype address () '(unsigned-byte #.sb!vm:n-word-bits))
22 (deftype disassem-length () '(unsigned-byte 24))
23 (deftype column () '(integer 0 1000))
24
25 (def!constant max-filtered-value-index 32)
26 (deftype filtered-value-index ()
27   `(integer 0 ,max-filtered-value-index))
28 (deftype filtered-value-vector ()
29   `(simple-array t (,max-filtered-value-index)))
30 \f
31 ;;;; disassembly parameters
32
33 ;;; instructions
34 (defvar *disassem-insts* (make-hash-table :test 'eq))
35 (declaim (type hash-table *disassem-insts*))
36
37 (defvar *disassem-inst-space* nil)
38
39 ;;; minimum alignment of instructions, in bytes
40 (defvar *disassem-inst-alignment-bytes* sb!vm:n-word-bytes)
41 (declaim (type alignment *disassem-inst-alignment-bytes*))
42
43 (defvar *disassem-location-column-width* 8)
44 (declaim (type text-width *disassem-location-column-width*))
45
46 ;;; the width of the column in which instruction-names are printed. A
47 ;;; value of zero gives the effect of not aligning the arguments at
48 ;;; all.
49 (defvar *disassem-opcode-column-width* 6)
50 (declaim (type text-width *disassem-opcode-column-width*))
51
52 ;;; the width of the column in which instruction-bytes are printed. A
53 ;;; value of zero disables the printing of instruction bytes.
54 (defvar *disassem-inst-column-width* 16
55   #!+sb-doc
56   "The width of instruction bytes.") 
57 (declaim (type text-width *disassem-inst-column-width*))
58          
59
60 (defvar *disassem-note-column* (+ 45 *disassem-inst-column-width*)
61   #!+sb-doc
62   "The column in which end-of-line comments for notes are started.")
63
64 ;;; the old CMU CL code to set the CMU CL disassembly parameters
65 #|
66 (defmacro set-disassem-params (&rest args)
67   #!+sb-doc
68   "Specify global disassembler params. &KEY arguments include:
69
70   :INSTRUCTION-ALIGNMENT number
71       Minimum alignment of instructions, in bits.
72
73   :ADDRESS-SIZE number
74       Size of a machine address, in bits.
75
76   :OPCODE-COLUMN-WIDTH
77       Width of the column used for printing the opcode portion of the
78       instruction, or NIL to use the default."
79   (gen-preamble-form args))
80
81 (defun gen-preamble-form (args)
82   #!+sb-doc
83   "Generate a form to specify global disassembler params. See the
84   documentation for SET-DISASSEM-PARAMS for more info."
85   (destructuring-bind
86       (&key instruction-alignment
87             address-size
88             (opcode-column-width nil opcode-column-width-p))
89       args
90     `(progn
91        (eval-when (:compile-toplevel :execute)
92          ;; these are not in the params because they only exist at compile time
93          (defparameter ,(format-table-name) (make-hash-table))
94          (defparameter ,(arg-type-table-name) nil)
95          (defparameter ,(fun-cache-name) (make-fun-cache)))
96        (let ((params
97               (or sb!c:*backend-disassem-params*
98                   (setf sb!c:*backend-disassem-params* (make-params)))))
99          (declare (ignorable params))
100          ,(when instruction-alignment
101             `(setf (params-instruction-alignment params)
102                    (bits-to-bytes ,instruction-alignment)))
103          ,(when address-size
104             `(setf (params-location-column-width params)
105                    (* 2 ,address-size)))
106          ,(when opcode-column-width-p
107             `(setf (params-opcode-column-width params) ,opcode-column-width))
108          'disassem-params))))
109 |#
110 \f
111 ;;;; cached functions
112 ;;;;
113 ;;;; FIXME: Is it important to cache these? For performance? Or why?
114 ;;;; If performance: *Really*? How fast does disassembly need to be??
115 ;;;; So: Could we just punt this?
116
117 (defstruct (fun-cache (:copier nil))
118   (printers nil :type list)
119   (labellers nil :type list)
120   (prefilters nil :type list))
121
122 (defvar *disassem-fun-cache* (make-fun-cache))
123 (declaim (type fun-cache *disassem-fun-cache*))
124 \f
125 ;;;; A DCHUNK contains the bits we look at to decode an
126 ;;;; instruction.
127 ;;;; I tried to keep this abstract so that if using integers > the machine
128 ;;;; word size conses too much, it can be changed to use bit-vectors or
129 ;;;; something.
130 ;;;;
131 ;;;; KLUDGE: It's not clear that using bit-vectors would be any more efficient.
132 ;;;; Perhaps the abstraction could go away. -- WHN 19991124
133
134 #!-sb-fluid
135 (declaim (inline dchunk-or dchunk-and dchunk-clear dchunk-not
136                  dchunk-make-mask dchunk-make-field
137                  sap-ref-dchunk
138                  dchunk-extract
139                  dchunk=
140                  dchunk-count-bits))
141
142 (def!constant dchunk-bits #.sb!vm:n-word-bits)
143
144 (deftype dchunk ()
145   `(unsigned-byte ,dchunk-bits))
146 (deftype dchunk-index ()
147   `(integer 0 ,dchunk-bits))
148
149 (def!constant dchunk-zero 0)
150 (def!constant dchunk-one #.(1- (expt 2 sb!vm:n-word-bits)))
151
152 (defun dchunk-extract (from pos)
153   (declare (type dchunk from))
154   (the dchunk (ldb pos (the dchunk from))))
155
156 (defmacro dchunk-copy (x)
157   `(the dchunk ,x))
158
159 (defun dchunk-or (to from)
160   (declare (type dchunk to from))
161   (the dchunk (logior to from)))
162 (defun dchunk-and (to from)
163   (declare (type dchunk to from))
164   (the dchunk (logand to from)))
165 (defun dchunk-clear (to from)
166   (declare (type dchunk to from))
167   (the dchunk (logandc2 to from)))
168 (defun dchunk-not (from)
169   (declare (type dchunk from))
170   (the dchunk (logand dchunk-one (lognot from))))
171
172 (defmacro dchunk-andf (to from)
173   `(setf ,to (dchunk-and ,to ,from)))
174 (defmacro dchunk-orf (to from)
175   `(setf ,to (dchunk-or ,to ,from)))
176 (defmacro dchunk-clearf (to from)
177   `(setf ,to (dchunk-clear ,to ,from)))
178
179 (defun dchunk-make-mask (pos)
180   (the dchunk (mask-field pos -1)))
181 (defun dchunk-make-field (pos value)
182   (the dchunk (dpb value pos 0)))
183
184 (defmacro make-dchunk (value)
185   `(the dchunk ,value))
186
187 (defun sap-ref-dchunk (sap byte-offset byte-order)
188   (declare (type sb!sys:system-area-pointer sap)
189            (type offset byte-offset)
190            (optimize (speed 3) (safety 0)))
191   (the dchunk
192        (if (eq byte-order :big-endian)
193            (+ (ash (sb!sys:sap-ref-8 sap byte-offset) 24)
194               (ash (sb!sys:sap-ref-8 sap (+ 1 byte-offset)) 16)
195               (ash (sb!sys:sap-ref-8 sap (+ 2 byte-offset)) 8)
196               (sb!sys:sap-ref-8 sap (+ 3 byte-offset)))
197            (+ (sb!sys:sap-ref-8 sap byte-offset)
198               (ash (sb!sys:sap-ref-8 sap (+ 1 byte-offset)) 8)
199               (ash (sb!sys:sap-ref-8 sap (+ 2 byte-offset)) 16)
200               (ash (sb!sys:sap-ref-8 sap (+ 3 byte-offset)) 24)))))
201
202 (defun dchunk-corrected-extract (from pos unit-bits byte-order)
203   (declare (type dchunk from))
204   (if (eq byte-order :big-endian)
205       (ldb (byte (byte-size pos)
206                  (+ (byte-position pos) (- dchunk-bits unit-bits)))
207            (the dchunk from))
208       (ldb pos (the dchunk from))))
209
210 (defmacro dchunk-insertf (place pos value)
211   `(setf ,place (the dchunk (dpb ,value ,pos (the dchunk,place)))))
212
213 (defun dchunk= (x y)
214   (declare (type dchunk x y))
215   (= x y))
216 (defmacro dchunk-zerop (x)
217   `(dchunk= ,x dchunk-zero))
218
219 (defun dchunk-strict-superset-p (sup sub)
220   (and (zerop (logandc2 sub sup))
221        (not (zerop (logandc2 sup sub)))))
222
223 (defun dchunk-count-bits (x)
224   (declare (type dchunk x))
225   (logcount x))
226 \f
227 (defstruct (instruction (:conc-name inst-)
228                         (:constructor
229                          make-instruction (name
230                                            format-name
231                                            print-name
232                                            length
233                                            mask id
234                                            printer
235                                            labeller prefilter control))
236                         (:copier nil))
237   (name nil :type (or symbol string))
238   (format-name nil :type (or symbol string))
239
240   (mask dchunk-zero :type dchunk)       ; bits in the inst that are constant
241   (id dchunk-zero :type dchunk)         ; value of those constant bits
242
243   (length 0 :type disassem-length)               ; in bytes
244
245   (print-name nil :type symbol)
246
247   ;; disassembly functions
248   (prefilter nil :type (or null function))
249   (labeller nil :type (or null function))
250   (printer (missing-arg) :type (or null function))
251   (control nil :type (or null function))
252
253   ;; instructions that are the same as this instruction but with more
254   ;; constraints
255   (specializers nil :type list))
256 (def!method print-object ((inst instruction) stream)
257   (print-unreadable-object (inst stream :type t :identity t)
258     (format stream "~A(~A)" (inst-name inst) (inst-format-name inst))))
259 \f
260 ;;;; an instruction space holds all known machine instructions in a
261 ;;;; form that can be easily searched
262
263 (defstruct (inst-space (:conc-name ispace-)
264                        (:copier nil))
265   (valid-mask dchunk-zero :type dchunk) ; applies to *children*
266   (choices nil :type list))
267 (def!method print-object ((ispace inst-space) stream)
268   (print-unreadable-object (ispace stream :type t :identity t)))
269
270 ;;; now that we've defined the structure, we can declaim the type of
271 ;;; the variable:
272 (declaim (type (or null inst-space) *disassem-inst-space*))
273
274 (defstruct (inst-space-choice (:conc-name ischoice-)
275                               (:copier nil))
276   (common-id dchunk-zero :type dchunk)  ; applies to *parent's* mask
277   (subspace (missing-arg) :type (or inst-space instruction)))
278 \f
279 ;;;; These are the kind of values we can compute for an argument, and
280 ;;;; how to compute them. The :CHECKER functions make sure that a given
281 ;;;; argument is compatible with another argument for a given use.
282
283 (defvar *arg-form-kinds* nil)
284
285 (defstruct (arg-form-kind (:copier nil))
286   (names nil :type list)
287   (producer (missing-arg) :type function)
288   (checker (missing-arg) :type function))
289
290 (defun arg-form-kind-or-lose (kind)
291   (or (getf *arg-form-kinds* kind)
292       (pd-error "unknown arg-form kind ~S" kind)))
293
294 (defun find-arg-form-producer (kind)
295   (arg-form-kind-producer (arg-form-kind-or-lose kind)))
296 (defun find-arg-form-checker (kind)
297   (arg-form-kind-checker (arg-form-kind-or-lose kind)))
298
299 (defun canonicalize-arg-form-kind (kind)
300   (car (arg-form-kind-names (arg-form-kind-or-lose kind))))
301 \f
302 ;;;; only used during compilation of the instructions for a backend
303 ;;;;
304 ;;;; FIXME: If only used then, isn't there some way we could do
305 ;;;; EVAL-WHEN tricks to keep this stuff from appearing in the target
306 ;;;; system?
307
308 (defvar *disassem-inst-formats* (make-hash-table))
309 (defvar *disassem-arg-types* nil)
310 (defvar *disassem-fun-cache* (make-fun-cache))
311
312 (defstruct (arg (:copier nil)
313                 (:predicate nil))
314   (name nil :type symbol)
315   (fields nil :type list)
316
317   (value nil :type (or list integer))
318   (sign-extend-p nil :type (member t nil))
319
320   ;; position in a vector of prefiltered values
321   (position 0 :type fixnum)
322
323   ;; functions to use
324   (printer nil)
325   (prefilter nil)
326   (use-label nil))
327
328 (defstruct (instruction-format (:conc-name format-)
329                                (:copier nil))
330   (name nil)
331   (args nil :type list)
332
333   (length 0 :type disassem-length)               ; in bytes
334
335   (default-printer nil :type list))
336 \f
337 ;;; A FUNSTATE holds the state of any arguments used in a disassembly
338 ;;; function.
339 (defstruct (funstate (:conc-name funstate-)
340                      (:constructor %make-funstate)
341                      (:copier nil))
342   (args nil :type list)
343   (arg-temps nil :type list))           ; See below.
344
345 (defun make-funstate (args)
346   ;; give the args a position
347   (let ((i 0))
348     (dolist (arg args)
349       (setf (arg-position arg) i)
350       (incf i)))
351   (%make-funstate :args args))
352
353 (defun funstate-compatible-p (funstate args)
354   (every (lambda (this-arg-temps)
355            (let* ((old-arg (car this-arg-temps))
356                   (new-arg (find (arg-name old-arg) args :key #'arg-name)))
357              (and new-arg
358                   (= (arg-position old-arg) (arg-position new-arg))
359                   (every (lambda (this-kind-temps)
360                            (funcall (find-arg-form-checker
361                                      (car this-kind-temps))
362                                     new-arg
363                                     old-arg))
364                          (cdr this-arg-temps)))))
365          (funstate-arg-temps funstate)))
366
367 (defun arg-or-lose (name funstate)
368   (let ((arg (find name (funstate-args funstate) :key #'arg-name)))
369     (when (null arg)
370       (pd-error "unknown argument ~S" name))
371     arg))
372 \f
373 ;;;; Since we can't include some values in compiled output as they are
374 ;;;; (notably functions), we sometimes use a VALSRC structure to keep
375 ;;;; track of the source from which they were derived.
376
377 (defstruct (valsrc (:constructor %make-valsrc)
378                    (:copier nil))
379   (value nil)
380   (source nil))
381
382 (defun make-valsrc (value source)
383   (cond ((equal value source)
384          source)
385         ((and (listp value) (eq (car value) 'function))
386          value)
387         (t
388          (%make-valsrc :value value :source source))))
389
390 ;;; machinery to provide more meaningful error messages during compilation
391 (defvar *current-instruction-flavor* nil)
392 (defun pd-error (fmt &rest args)
393   (if *current-instruction-flavor*
394       (error "~@<in printer-definition for ~S(~S): ~3I~:_~?~:>"
395              (car *current-instruction-flavor*)
396              (cdr *current-instruction-flavor*)
397              fmt args)
398       (apply #'error fmt args)))
399
400 ;;; FIXME:
401 ;;;  1. This should become a utility in SB!INT.
402 ;;;  2. Arrays and structures and maybe other things are
403 ;;;     self-evaluating too.
404 (defun self-evaluating-p (x)
405   (typecase x
406     (null t)
407     (keyword t)
408     (symbol (eq x t))
409     (cons nil)
410     (t t)))
411
412 (defun maybe-quote (evalp form)
413   (if (or evalp (self-evaluating-p form)) form `',form))
414
415 ;;; Detect things that obviously don't need wrapping, like
416 ;;; variable-refs and #'function.
417 (defun doesnt-need-wrapping-p (form)
418   (or (symbolp form)
419       (and (listp form)
420            (eq (car form) 'function)
421            (symbolp (cadr form)))))
422
423 (defun make-wrapper (form arg-name funargs prefix)
424   (if (and (listp form)
425            (eq (car form) 'function))
426       ;; a function def
427       (let ((wrapper-name (symbolicate prefix "-" arg-name "-WRAPPER"))
428             (wrapper-args (make-gensym-list (length funargs))))
429         (values `#',wrapper-name
430                 `(defun ,wrapper-name ,wrapper-args
431                    (funcall ,form ,@wrapper-args))))
432       ;; something else
433       (let ((wrapper-name (symbolicate "*" prefix "-" arg-name "-WRAPPER*")))
434         (values wrapper-name `(defparameter ,wrapper-name ,form)))))
435
436 (defun filter-overrides (overrides evalp)
437   (mapcar (lambda (override)
438             (list* (car override) (cadr override)
439                    (munge-fun-refs (cddr override) evalp)))
440           overrides))
441
442 (defparameter *arg-fun-params*
443   '((:printer . (value stream dstate))
444     (:use-label . (value dstate))
445     (:prefilter . (value dstate))))
446
447 (defun munge-fun-refs (params evalp &optional wrap-defs-p (prefix ""))
448   (let ((params (copy-list params)))
449     (do ((tail params (cdr tail))
450          (wrapper-defs nil))
451         ((null tail)
452          (values params (nreverse wrapper-defs)))
453       (let ((fun-arg (assoc (car tail) *arg-fun-params*)))
454         (when fun-arg
455           (let* ((fun-form (cadr tail))
456                  (quoted-fun-form `',fun-form))
457             (when (and wrap-defs-p (not (doesnt-need-wrapping-p fun-form)))
458               (multiple-value-bind (access-form wrapper-def-form)
459                   (make-wrapper fun-form (car fun-arg) (cdr fun-arg) prefix)
460                 (setf quoted-fun-form `',access-form)
461                 (push wrapper-def-form wrapper-defs)))
462             (if evalp
463                 (setf (cadr tail)
464                       `(make-valsrc ,fun-form ,quoted-fun-form))
465                 (setf (cadr tail)
466                       fun-form))))))))
467
468 (defun gen-args-def-form (overrides format-form &optional (evalp t))
469   (let ((args-var (gensym)))
470     `(let ((,args-var (copy-list (format-args ,format-form))))
471        ,@(mapcar (lambda (override)
472                    (update-args-form args-var
473                                      `',(car override)
474                                      (and (cdr override)
475                                           (cons :value (cdr override)))
476                                      evalp))
477                  overrides)
478        ,args-var)))
479
480 (defun gen-printer-def-forms-def-form (base-name
481                                        uniquified-name
482                                        def
483                                        &optional
484                                        (evalp t))
485   (declare (type symbol base-name))
486   (declare (type (or symbol string) uniquified-name))
487   (destructuring-bind
488       (format-name
489        (&rest field-defs)
490        &optional (printer-form :default)
491        &key ((:print-name print-name-form) `',base-name) control)
492       def
493     (let ((format-var (gensym))
494           (field-defs (filter-overrides field-defs evalp)))
495       `(let* ((*current-instruction-flavor* ',(cons base-name format-name))
496               (,format-var (format-or-lose ',format-name))
497               (args ,(gen-args-def-form field-defs format-var evalp))
498               (funcache *disassem-fun-cache*))
499          (multiple-value-bind (printer-fun printer-defun)
500              (find-printer-fun ',uniquified-name
501                                ',format-name
502                                ,(if (eq printer-form :default)
503                                      `(format-default-printer ,format-var)
504                                      (maybe-quote evalp printer-form))
505                                args funcache)
506            (multiple-value-bind (labeller-fun labeller-defun)
507                (find-labeller-fun ',uniquified-name args funcache)
508              (multiple-value-bind (prefilter-fun prefilter-defun)
509                  (find-prefilter-fun ',uniquified-name
510                                      ',format-name
511                                      args
512                                      funcache)
513                (multiple-value-bind (mask id)
514                    (compute-mask-id args)
515                  (values
516                   `(make-instruction ',',base-name
517                                      ',',format-name
518                                      ,',print-name-form
519                                      ,(format-length ,format-var)
520                                      ,mask
521                                      ,id
522                                      ,(and printer-fun `#',printer-fun)
523                                      ,(and labeller-fun `#',labeller-fun)
524                                      ,(and prefilter-fun `#',prefilter-fun)
525                                      ,',control)
526                   `(progn
527                      ,@(and printer-defun (list printer-defun))
528                      ,@(and labeller-defun (list labeller-defun))
529                      ,@(and prefilter-defun (list prefilter-defun))))
530                  ))))))))
531
532 (defun update-args-form (var name-form descrip-forms evalp
533                              &optional format-length-form)
534   `(setf ,var
535          ,(if evalp
536               `(modify-or-add-arg ,name-form
537                                   ,var
538                                   *disassem-arg-types*
539                                   ,@(and format-length-form
540                                          `(:format-length
541                                             ,format-length-form))
542                                   ,@descrip-forms)
543               `(apply #'modify-or-add-arg
544                       ,name-form
545                       ,var
546                       *disassem-arg-types*
547                       ,@(and format-length-form
548                              `(:format-length ,format-length-form))
549                       ',descrip-forms))))
550
551 (defun format-or-lose (name)
552   (or (gethash name *disassem-inst-formats*)
553       (pd-error "unknown instruction format ~S" name)))
554
555 ;;; FIXME: needed only at build-the-system time, not in running system
556 (defmacro define-instruction-format (header &rest fields)
557   #!+sb-doc
558   "DEFINE-INSTRUCTION-FORMAT (Name Length {Format-Key Value}*) Arg-Def*
559   Define an instruction format NAME for the disassembler's use. LENGTH is
560   the length of the format in bits.
561   Possible FORMAT-KEYs:
562
563   :INCLUDE other-format-name
564       Inherit all arguments and properties of the given format. Any
565       arguments defined in the current format definition will either modify
566       the copy of an existing argument (keeping in the same order with
567       respect to when pre-filter's are called), if it has the same name as
568       one, or be added to the end.
569   :DEFAULT-PRINTER printer-list
570       Use the given PRINTER-LIST as a format to print any instructions of
571       this format when they don't specify something else.
572
573   Each ARG-DEF defines one argument in the format, and is of the form
574     (Arg-Name {Arg-Key Value}*)
575
576   Possible ARG-KEYs (the values are evaluated unless otherwise specified):
577
578   :FIELDS byte-spec-list
579       The argument takes values from these fields in the instruction. If
580       the list is of length one, then the corresponding value is supplied by
581       itself; otherwise it is a list of the values. The list may be NIL.
582   :FIELD byte-spec
583       The same as :FIELDS (list byte-spec).
584
585   :VALUE value
586       If the argument only has one field, this is the value it should have,
587       otherwise it's a list of the values of the individual fields. This can
588       be overridden in an instruction-definition or a format definition
589       including this one by specifying another, or NIL to indicate that it's
590       variable.
591
592   :SIGN-EXTEND boolean
593       If non-NIL, the raw value of this argument is sign-extended,
594       immediately after being extracted from the instruction (before any
595       prefilters are run, for instance). If the argument has multiple
596       fields, they are all sign-extended.
597
598   :TYPE arg-type-name
599       Inherit any properties of the given argument type.
600
601   :PREFILTER function
602       A function which is called (along with all other prefilters, in the
603       order that their arguments appear in the instruction-format) before
604       any printing is done, to filter the raw value. Any uses of READ-SUFFIX
605       must be done inside a prefilter.
606
607   :PRINTER function-string-or-vector
608       A function, string, or vector which is used to print this argument.
609
610   :USE-LABEL
611       If non-NIL, the value of this argument is used as an address, and if
612       that address occurs inside the disassembled code, it is replaced by a
613       label. If this is a function, it is called to filter the value."
614   (gen-format-def-form header fields))
615
616 ;;; FIXME: needed only at build-the-system time, not in running system
617 (defun gen-format-def-form (header descrips &optional (evalp t))
618   #!+sb-doc
619   "Generate a form to define an instruction format. See
620   DEFINE-INSTRUCTION-FORMAT for more info."
621   (when (atom header)
622     (setf header (list header)))
623   (destructuring-bind (name length &key default-printer include) header
624     (let ((args-var (gensym))
625           (length-var (gensym))
626           (all-wrapper-defs nil)
627           (arg-count 0))
628       (collect ((arg-def-forms))
629         (dolist (descrip descrips)
630           (let ((name (pop descrip)))
631             (multiple-value-bind (descrip wrapper-defs)
632                 (munge-fun-refs
633                  descrip evalp t (format nil "~:@(~A~)-~D" name arg-count))
634               (arg-def-forms
635                (update-args-form args-var `',name descrip evalp length-var))
636               (setf all-wrapper-defs
637                     (nconc wrapper-defs all-wrapper-defs)))
638             (incf arg-count)))
639         `(progn
640            ,@all-wrapper-defs
641            (eval-when (:compile-toplevel :execute)
642              (let ((,length-var ,length)
643                    (,args-var
644                     ,(and include
645                           `(copy-list
646                             (format-args
647                              (format-or-lose ,include))))))
648                ,@(arg-def-forms)
649                (setf (gethash ',name *disassem-inst-formats*)
650                      (make-instruction-format
651                       :name ',name
652                       :length (bits-to-bytes ,length-var)
653                       :default-printer ,(maybe-quote evalp default-printer)
654                       :args ,args-var))
655                (eval
656                 `(progn
657                    ,@(mapcar (lambda (arg)
658                                (when (arg-fields arg)
659                                  (gen-arg-access-macro-def-form
660                                   arg ,args-var ',name)))
661                              ,args-var))))))))))
662
663 ;;; FIXME: probably needed only at build-the-system time, not in
664 ;;; final target system
665 (defun modify-or-add-arg (arg-name
666                           args
667                           type-table
668                           &key
669                           (value nil value-p)
670                           (type nil type-p)
671                           (prefilter nil prefilter-p)
672                           (printer nil printer-p)
673                           (sign-extend nil sign-extend-p)
674                           (use-label nil use-label-p)
675                           (field nil field-p)
676                           (fields nil fields-p)
677                           format-length)
678   (let* ((arg-pos (position arg-name args :key #'arg-name))
679          (arg
680           (if (null arg-pos)
681               (let ((arg (make-arg :name arg-name)))
682                 (if (null args)
683                     (setf args (list arg))
684                     (push arg (cdr (last args))))
685                 arg)
686               (setf (nth arg-pos args)
687                     (copy-structure (nth arg-pos args))))))
688     (when (and field-p (not fields-p))
689       (setf fields (list field))
690       (setf fields-p t))
691     (when type-p
692       (set-arg-from-type arg type type-table))
693     (when value-p
694       (setf (arg-value arg) value))
695     (when prefilter-p
696       (setf (arg-prefilter arg) prefilter))
697     (when sign-extend-p
698       (setf (arg-sign-extend-p arg) sign-extend))
699     (when printer-p
700       (setf (arg-printer arg) printer))
701     (when use-label-p
702       (setf (arg-use-label arg) use-label))
703     (when fields-p
704       (when (null format-length)
705         (error
706          "~@<in arg ~S: ~3I~:_~
707           can't specify fields except using DEFINE-INSTRUCTION-FORMAT~:>"
708          arg-name))
709       (setf (arg-fields arg)
710             (mapcar (lambda (bytespec)
711                       (when (> (+ (byte-position bytespec)
712                                   (byte-size bytespec))
713                                format-length)
714                         (error "~@<in arg ~S: ~3I~:_~
715                                      The field ~S doesn't fit in an ~
716                                      instruction-format ~W bits wide.~:>"
717                                arg-name
718                                bytespec
719                                format-length))
720                       (correct-dchunk-bytespec-for-endianness
721                        bytespec
722                        format-length
723                        sb!c:*backend-byte-order*))
724                     fields)))
725     args))
726
727 (defun gen-arg-access-macro-def-form (arg args format-name)
728   (let* ((funstate (make-funstate args))
729          (arg-val-form (arg-value-form arg funstate :adjusted))
730          (bindings (make-arg-temp-bindings funstate)))
731     `(sb!xc:defmacro ,(symbolicate format-name "-" (arg-name arg))
732          (chunk dstate)
733        `(let ((chunk ,chunk) (dstate ,dstate))
734           (declare (ignorable chunk dstate))
735           (flet ((local-filtered-value (offset)
736                    (declare (type filtered-value-index offset))
737                    (aref (dstate-filtered-values dstate) offset))
738                  (local-extract (bytespec)
739                    (dchunk-extract chunk bytespec)))
740             (declare (ignorable #'local-filtered-value #'local-extract)
741                      (inline local-filtered-value local-extract))
742             (let* ,',bindings
743               ,',arg-val-form))))))
744
745 (defun arg-value-form (arg funstate
746                        &optional
747                        (kind :final)
748                        (allow-multiple-p (not (eq kind :numeric))))
749   (let ((forms (gen-arg-forms arg kind funstate)))
750     (when (and (not allow-multiple-p)
751                (listp forms)
752                (/= (length forms) 1))
753       (pd-error "~S must not have multiple values." arg))
754     (maybe-listify forms)))
755
756 (defun correct-dchunk-bytespec-for-endianness (bs unit-bits byte-order)
757   (if (eq byte-order :big-endian)
758       (byte (byte-size bs) (+ (byte-position bs) (- dchunk-bits unit-bits)))
759       bs))
760
761 (defun make-arg-temp-bindings (funstate)
762   ;; (Everything is in reverse order, so we just use PUSH, which
763   ;; results in everything being in the right order at the end.)
764   (let ((bindings nil))
765     (dolist (ats (funstate-arg-temps funstate))
766       (dolist (atk (cdr ats))
767         (cond ((null (cadr atk)))
768               ((atom (cadr atk))
769                (push `(,(cadr atk) ,(cddr atk)) bindings))
770               (t
771                (mapc (lambda (var form)
772                        (push `(,var ,form) bindings))
773                      (cadr atk)
774                      (cddr atk))))))
775     bindings))
776
777 (defun gen-arg-forms (arg kind funstate)
778   (multiple-value-bind (vars forms)
779       (get-arg-temp arg kind funstate)
780     (when (null forms)
781       (multiple-value-bind (new-forms single-value-p)
782           (funcall (find-arg-form-producer kind) arg funstate)
783         (setq forms new-forms)
784         (cond ((or single-value-p (atom forms))
785                (unless (symbolp forms)
786                  (setq vars (gensym))))
787               ((every #'symbolp forms)
788                ;; just use the same as the forms
789                (setq vars nil))
790               (t
791                (setq vars (make-gensym-list (length forms)))))
792         (set-arg-temps vars forms arg kind funstate)))
793     (or vars forms)))
794
795 (defun maybe-listify (forms)
796   (cond ((atom forms)
797          forms)
798         ((/= (length forms) 1)
799          `(list ,@forms))
800         (t
801          (car forms))))
802 \f
803 (defun set-arg-from-type (arg type-name table)
804   (let ((type-arg (find type-name table :key #'arg-name)))
805     (when (null type-arg)
806       (pd-error "unknown argument type: ~S" type-name))
807     (setf (arg-printer arg) (arg-printer type-arg))
808     (setf (arg-prefilter arg) (arg-prefilter type-arg))
809     (setf (arg-sign-extend-p arg) (arg-sign-extend-p type-arg))
810     (setf (arg-use-label arg) (arg-use-label type-arg))))
811
812 (defun get-arg-temp (arg kind funstate)
813   (let ((this-arg-temps (assoc arg (funstate-arg-temps funstate))))
814     (if this-arg-temps
815         (let ((this-kind-temps
816                (assoc (canonicalize-arg-form-kind kind)
817                       (cdr this-arg-temps))))
818           (values (cadr this-kind-temps) (cddr this-kind-temps)))
819         (values nil nil))))
820
821 (defun set-arg-temps (vars forms arg kind funstate)
822   (let ((this-arg-temps
823          (or (assoc arg (funstate-arg-temps funstate))
824              (car (push (cons arg nil) (funstate-arg-temps funstate)))))
825         (kind (canonicalize-arg-form-kind kind)))
826     (let ((this-kind-temps
827            (or (assoc kind (cdr this-arg-temps))
828                (car (push (cons kind nil) (cdr this-arg-temps))))))
829       (setf (cdr this-kind-temps) (cons vars forms)))))
830 \f
831 ;;; DEFINE-ARG-TYPE Name {Key Value}*
832 ;;;
833 ;;; Define a disassembler argument type NAME (which can then be referenced in
834 ;;; another argument definition using the :TYPE argument). &KEY args are:
835 ;;;
836 ;;;  :SIGN-EXTEND boolean
837 ;;;     If non-NIL, the raw value of this argument is sign-extended.
838 ;;;
839 ;;;  :TYPE arg-type-name
840 ;;;     Inherit any properties of given arg-type.
841 ;;; 
842 ;;; :PREFILTER function
843 ;;;     A function which is called (along with all other prefilters,
844 ;;;     in the order that their arguments appear in the instruction-
845 ;;;     format) before any printing is done, to filter the raw value.
846 ;;;     Any uses of READ-SUFFIX must be done inside a prefilter.
847 ;;; 
848 ;;; :PRINTER function-string-or-vector
849 ;;;     A function, string, or vector which is used to print an argument of
850 ;;;     this type.
851 ;;; 
852 ;;; :USE-LABEL
853 ;;;     If non-NIL, the value of an argument of this type is used as
854 ;;;     an address, and if that address occurs inside the disassembled
855 ;;;     code, it is replaced by a label. If this is a function, it is
856 ;;;     called to filter the value.
857 (defmacro define-arg-type (name &rest args)
858   (gen-arg-type-def-form name args))
859
860 ;;; Generate a form to define a disassembler argument type. See
861 ;;; DEFINE-ARG-TYPE for more information.
862 (defun gen-arg-type-def-form (name args &optional (evalp t))
863   (multiple-value-bind (args wrapper-defs)
864       (munge-fun-refs args evalp t name)
865     `(progn
866        ,@wrapper-defs
867        (eval-when (:compile-toplevel :execute)
868          ,(update-args-form '*disassem-arg-types* `',name args evalp))
869        ',name)))
870 \f
871 (defmacro def-arg-form-kind ((&rest names) &rest inits)
872   `(let ((kind (make-arg-form-kind :names ',names ,@inits)))
873      ,@(mapcar (lambda (name)
874                  `(setf (getf *arg-form-kinds* ',name) kind))
875                names)))
876
877 (def-arg-form-kind (:raw)
878   :producer (lambda (arg funstate)
879               (declare (ignore funstate))
880               (mapcar (lambda (bytespec)
881                         `(the (unsigned-byte ,(byte-size bytespec))
882                            (local-extract ',bytespec)))
883                       (arg-fields arg)))
884   :checker (lambda (new-arg old-arg)
885              (equal (arg-fields new-arg)
886                     (arg-fields old-arg))))
887
888 (def-arg-form-kind (:sign-extended :unfiltered)
889   :producer (lambda (arg funstate)
890               (let ((raw-forms (gen-arg-forms arg :raw funstate)))
891                 (if (and (arg-sign-extend-p arg) (listp raw-forms))
892                     (mapcar (lambda (form field)
893                               `(the (signed-byte ,(byte-size field))
894                                  (sign-extend ,form
895                                               ,(byte-size field))))
896                             raw-forms
897                             (arg-fields arg))
898                     raw-forms)))
899   :checker (lambda (new-arg old-arg)
900              (equal (arg-sign-extend-p new-arg)
901                     (arg-sign-extend-p old-arg))))
902
903 (defun valsrc-equal (f1 f2)
904   (if (null f1)
905       (null f2)
906       (equal (value-or-source f1)
907              (value-or-source f2))))
908
909 (def-arg-form-kind (:filtering)
910   :producer (lambda (arg funstate)
911               (let ((sign-extended-forms
912                      (gen-arg-forms arg :sign-extended funstate))
913                     (pf (arg-prefilter arg)))
914                 (if pf
915                     (values
916                      `(local-filter ,(maybe-listify sign-extended-forms)
917                                     ,(source-form pf))
918                      t)
919                     (values sign-extended-forms nil))))
920   :checker (lambda (new-arg old-arg)
921              (valsrc-equal (arg-prefilter new-arg) (arg-prefilter old-arg))))
922
923 (def-arg-form-kind (:filtered :unadjusted)
924   :producer (lambda (arg funstate)
925               (let ((pf (arg-prefilter arg)))
926                 (if pf
927                     (values `(local-filtered-value ,(arg-position arg)) t)
928                     (gen-arg-forms arg :sign-extended funstate))))
929   :checker (lambda (new-arg old-arg)
930              (let ((pf1 (arg-prefilter new-arg))
931                    (pf2 (arg-prefilter old-arg)))
932                (if (null pf1)
933                    (null pf2)
934                    (= (arg-position new-arg)
935                       (arg-position old-arg))))))
936
937 (def-arg-form-kind (:adjusted :numeric :unlabelled)
938   :producer (lambda (arg funstate)
939               (let ((filtered-forms (gen-arg-forms arg :filtered funstate))
940                     (use-label (arg-use-label arg)))
941                 (if (and use-label (not (eq use-label t)))
942                     (list
943                      `(adjust-label ,(maybe-listify filtered-forms)
944                                     ,(source-form use-label)))
945                     filtered-forms)))
946   :checker (lambda (new-arg old-arg)
947              (valsrc-equal (arg-use-label new-arg) (arg-use-label old-arg))))
948
949 (def-arg-form-kind (:labelled :final)
950   :producer (lambda (arg funstate)
951               (let ((adjusted-forms
952                      (gen-arg-forms arg :adjusted funstate))
953                     (use-label (arg-use-label arg)))
954                 (if use-label
955                     (let ((form (maybe-listify adjusted-forms)))
956                       (if (and (not (eq use-label t))
957                                (not (atom adjusted-forms))
958                                (/= (length adjusted-forms) 1))
959                           (pd-error
960                            "cannot label a multiple-field argument ~
961                               unless using a function: ~S" arg)
962                           `((lookup-label ,form))))
963                     adjusted-forms)))
964   :checker (lambda (new-arg old-arg)
965              (let ((lf1 (arg-use-label new-arg))
966                    (lf2 (arg-use-label old-arg)))
967                (if (null lf1) (null lf2) t))))
968
969 ;;; This is a bogus kind that's just used to ensure that printers are
970 ;;; compatible...
971 (def-arg-form-kind (:printed)
972   :producer (lambda (&rest noise)
973               (declare (ignore noise))
974               (pd-error "bogus! can't use the :printed value of an arg!"))
975   :checker (lambda (new-arg old-arg)
976              (valsrc-equal (arg-printer new-arg) (arg-printer old-arg))))
977
978 (defun remember-printer-use (arg funstate)
979   (set-arg-temps nil nil arg :printed funstate))
980 \f
981 ;;; Returns a version of THING suitable for including in an evaluable
982 ;;; position in some form.
983 (defun source-form (thing)
984   (cond ((valsrc-p thing)
985          (valsrc-source thing))
986         ((functionp thing)
987          (pd-error
988           "can't dump functions, so function ref form must be quoted: ~S"
989           thing))
990         ((self-evaluating-p thing)
991          thing)
992         ((eq (car thing) 'function)
993          thing)
994         (t
995          `',thing)))
996
997 ;;; Return anything but a VALSRC structure.
998 (defun value-or-source (thing)
999   (if (valsrc-p thing)
1000       (valsrc-value thing)
1001       thing))
1002 \f
1003 (defstruct (cached-fun (:conc-name cached-fun-)
1004                        (:copier nil))
1005   (funstate nil :type (or null funstate))
1006   (constraint nil :type list)
1007   (name nil :type (or null symbol)))
1008
1009 (defun find-cached-fun (cached-funs args constraint)
1010   (dolist (cached-fun cached-funs nil)
1011     (let ((funstate (cached-fun-funstate cached-fun)))
1012       (when (and (equal constraint (cached-fun-constraint cached-fun))
1013                  (or (null funstate)
1014                      (funstate-compatible-p funstate args)))
1015         (return cached-fun)))))
1016
1017 (defmacro !with-cached-fun ((name-var
1018                              funstate-var
1019                              cache
1020                              cache-slot
1021                              args
1022                              &key
1023                              constraint
1024                              (stem (missing-arg)))
1025                             &body defun-maker-forms)
1026   (let ((cache-var (gensym))
1027         (constraint-var (gensym)))
1028     `(let* ((,constraint-var ,constraint)
1029             (,cache-var (find-cached-fun (,cache-slot ,cache)
1030                                          ,args ,constraint-var)))
1031        (cond (,cache-var
1032               (values (cached-fun-name ,cache-var) nil))
1033              (t
1034               (let* ((,name-var (symbolicate "CACHED-FUN--" ,stem))
1035                      (,funstate-var (make-funstate ,args))
1036                      (,cache-var
1037                       (make-cached-fun :name ,name-var
1038                                        :funstate ,funstate-var
1039                                        :constraint ,constraint-var)))
1040                 (values ,name-var
1041                         `(progn
1042                            ,(progn ,@defun-maker-forms)
1043                            (eval-when (:compile-toplevel :execute)
1044                              (push ,,cache-var
1045                                    (,',cache-slot ',,cache)))))))))))
1046 \f
1047 (defun find-printer-fun (%name %format-name printer-source args cache)
1048   (declare (type (or string symbol) %name))
1049   (if (null printer-source)
1050       (values nil nil)
1051       (let ((printer-source (preprocess-printer printer-source args)))
1052         (!with-cached-fun
1053            (name funstate cache fun-cache-printers args
1054                  :constraint printer-source
1055                  :stem (concatenate 'string
1056                                     (string %name)
1057                                     "-"
1058                                     (symbol-name %format-name)
1059                                     "-PRINTER"))
1060          (make-printer-defun printer-source funstate name)))))
1061 \f
1062 (defun make-printer-defun (source funstate fun-name)
1063   (let ((printer-form (compile-printer-list source funstate))
1064         (bindings (make-arg-temp-bindings funstate)))
1065     `(defun ,fun-name (chunk inst stream dstate)
1066        (declare (type dchunk chunk)
1067                 (type instruction inst)
1068                 (type stream stream)
1069                 (type disassem-state dstate))
1070        (macrolet ((local-format-arg (arg fmt)
1071                     `(funcall (formatter ,fmt) stream ,arg)))
1072          (flet ((local-tab-to-arg-column ()
1073                   (tab (dstate-argument-column dstate) stream))
1074                 (local-print-name ()
1075                   (princ (inst-print-name inst) stream))
1076                 (local-write-char (ch)
1077                   (write-char ch stream))
1078                 (local-princ (thing)
1079                   (princ thing stream))
1080                 (local-princ16 (thing)
1081                   (princ16 thing stream))
1082                 (local-call-arg-printer (arg printer)
1083                   (funcall printer arg stream dstate))
1084                 (local-call-global-printer (fun)
1085                   (funcall fun chunk inst stream dstate))
1086                 (local-filtered-value (offset)
1087                   (declare (type filtered-value-index offset))
1088                   (aref (dstate-filtered-values dstate) offset))
1089                 (local-extract (bytespec)
1090                   (dchunk-extract chunk bytespec))
1091                 (lookup-label (lab)
1092                   (or (gethash lab (dstate-label-hash dstate))
1093                       lab))
1094                 (adjust-label (val adjust-fun)
1095                   (funcall adjust-fun val dstate)))
1096            (declare (ignorable #'local-tab-to-arg-column
1097                                #'local-print-name
1098                                #'local-princ #'local-princ16
1099                                #'local-write-char
1100                                #'local-call-arg-printer
1101                                #'local-call-global-printer
1102                                #'local-extract
1103                                #'local-filtered-value
1104                                #'lookup-label #'adjust-label)
1105                     (inline local-tab-to-arg-column
1106                             local-princ local-princ16
1107                             local-call-arg-printer local-call-global-printer
1108                             local-filtered-value local-extract
1109                             lookup-label adjust-label))
1110            (let* ,bindings
1111              ,@printer-form))))))
1112 \f
1113 (defun preprocess-test (subj form args)
1114   (multiple-value-bind (subj test)
1115       (if (and (consp form) (symbolp (car form)) (not (keywordp (car form))))
1116           (values (car form) (cdr form))
1117           (values subj form))
1118     (let ((key (if (consp test) (car test) test))
1119           (body (if (consp test) (cdr test) nil)))
1120       (case key
1121         (:constant
1122          (if (null body)
1123              ;; If no supplied constant values, just any constant is ok,
1124              ;; just see whether there's some constant value in the arg.
1125              (not
1126               (null
1127                (arg-value
1128                 (or (find subj args :key #'arg-name)
1129                     (pd-error "unknown argument ~S" subj)))))
1130              ;; Otherwise, defer to run-time.
1131              form))
1132         ((:or :and :not)
1133          (sharing-cons
1134           form
1135           subj
1136           (sharing-cons
1137            test
1138            key
1139            (sharing-mapcar
1140             (lambda (sub-test)
1141               (preprocess-test subj sub-test args))
1142             body))))
1143         (t form)))))
1144
1145 (defun preprocess-conditionals (printer args)
1146   (if (atom printer)
1147       printer
1148       (case (car printer)
1149         (:unless
1150          (preprocess-conditionals
1151           `(:cond ((:not ,(nth 1 printer)) ,@(nthcdr 2 printer)))
1152           args))
1153         (:when
1154          (preprocess-conditionals `(:cond (,(cdr printer))) args))
1155         (:if
1156          (preprocess-conditionals
1157           `(:cond (,(nth 1 printer) ,(nth 2 printer))
1158                   (t ,(nth 3 printer)))
1159           args))
1160         (:cond
1161          (sharing-cons
1162           printer
1163           :cond
1164           (sharing-mapcar
1165            (lambda (clause)
1166              (let ((filtered-body
1167                     (sharing-mapcar
1168                      (lambda (sub-printer)
1169                        (preprocess-conditionals sub-printer args))
1170                      (cdr clause))))
1171                (sharing-cons
1172                 clause
1173                 (preprocess-test (find-first-field-name filtered-body)
1174                                  (car clause)
1175                                  args)
1176                 filtered-body)))
1177            (cdr printer))))
1178         (quote printer)
1179         (t
1180          (sharing-mapcar
1181           (lambda (sub-printer)
1182             (preprocess-conditionals sub-printer args))
1183           printer)))))
1184
1185 ;;; Return a version of the disassembly-template PRINTER with
1186 ;;; compile-time tests (e.g. :constant without a value), and any
1187 ;;; :CHOOSE operators resolved properly for the args ARGS.
1188 ;;;
1189 ;;; (:CHOOSE Sub*) simply returns the first Sub in which every field
1190 ;;; reference refers to a valid arg.
1191 (defun preprocess-printer (printer args)
1192   (preprocess-conditionals (preprocess-chooses printer args) args))
1193 \f
1194 ;;; Return the first non-keyword symbol in a depth-first search of TREE.
1195 (defun find-first-field-name (tree)
1196   (cond ((null tree)
1197          nil)
1198         ((and (symbolp tree) (not (keywordp tree)))
1199          tree)
1200         ((atom tree)
1201          nil)
1202         ((eq (car tree) 'quote)
1203          nil)
1204         (t
1205          (or (find-first-field-name (car tree))
1206              (find-first-field-name (cdr tree))))))
1207
1208 (defun preprocess-chooses (printer args)
1209   (cond ((atom printer)
1210          printer)
1211         ((eq (car printer) :choose)
1212          (pick-printer-choice (cdr printer) args))
1213         (t
1214          (sharing-mapcar (lambda (sub) (preprocess-chooses sub args))
1215                          printer))))
1216 \f
1217 ;;;; some simple functions that help avoid consing when we're just
1218 ;;;; recursively filtering things that usually don't change
1219
1220 (defun sharing-cons (old-cons car cdr)
1221   #!+sb-doc
1222   "If CAR is eq to the car of OLD-CONS and CDR is eq to the CDR, return
1223   OLD-CONS, otherwise return (cons CAR CDR)."
1224   (if (and (eq car (car old-cons)) (eq cdr (cdr old-cons)))
1225       old-cons
1226       (cons car cdr)))
1227
1228 (defun sharing-mapcar (fun list)
1229   (declare (type function fun))
1230   #!+sb-doc
1231   "A simple (one list arg) mapcar that avoids consing up a new list
1232   as long as the results of calling FUN on the elements of LIST are
1233   eq to the original."
1234   (and list
1235        (sharing-cons list
1236                      (funcall fun (car list))
1237                      (sharing-mapcar fun (cdr list)))))
1238 \f
1239 (defun all-arg-refs-relevant-p (printer args)
1240   (cond ((or (null printer) (keywordp printer) (eq printer t))
1241          t)
1242         ((symbolp printer)
1243          (find printer args :key #'arg-name))
1244         ((listp printer)
1245          (every (lambda (x) (all-arg-refs-relevant-p x args))
1246                 printer))
1247         (t t)))
1248
1249 (defun pick-printer-choice (choices args)
1250   (dolist (choice choices
1251            (pd-error "no suitable choice found in ~S" choices))
1252     (when (all-arg-refs-relevant-p choice args)
1253       (return choice))))
1254
1255 (defun compile-printer-list (sources funstate)
1256   (unless (null sources)
1257     ;; Coalesce adjacent symbols/strings, and convert to strings if possible,
1258     ;; since they require less consing to write.
1259     (do ((el (car sources) (car sources))
1260          (names nil (cons (strip-quote el) names)))
1261         ((not (string-or-qsym-p el))
1262          (when names
1263            ;; concatenate adjacent strings and symbols
1264            (let ((string
1265                   (apply #'concatenate
1266                          'string
1267                          (mapcar #'string (nreverse names)))))
1268              (push (if (some #'alpha-char-p string)
1269                        `',(make-symbol string) ; Preserve casifying output.
1270                        string)
1271                    sources))))
1272       (pop sources))
1273     (cons (compile-printer-body (car sources) funstate)
1274           (compile-printer-list (cdr sources) funstate))))
1275
1276 (defun compile-printer-body (source funstate)
1277   (cond ((null source)
1278          nil)
1279         ((eq source :name)
1280          `(local-print-name))
1281         ((eq source :tab)
1282          `(local-tab-to-arg-column))
1283         ((keywordp source)
1284          (pd-error "unknown printer element: ~S" source))
1285         ((symbolp source)
1286          (compile-print source funstate))
1287         ((atom source)
1288          `(local-princ ',source))
1289         ((eq (car source) :using)
1290          (unless (or (stringp (cadr source))
1291                      (and (listp (cadr source))
1292                           (eq (caadr source) 'function)))
1293            (pd-error "The first arg to :USING must be a string or #'function."))
1294          (compile-print (caddr source) funstate
1295                         (cons (eval (cadr source)) (cadr source))))
1296         ((eq (car source) :plus-integer)
1297          ;; prints the given field proceed with a + or a -
1298          (let ((form
1299                 (arg-value-form (arg-or-lose (cadr source) funstate)
1300                                 funstate
1301                                 :numeric)))
1302            `(progn
1303               (when (>= ,form 0)
1304                 (local-write-char #\+))
1305               (local-princ ,form))))
1306         ((eq (car source) 'quote)
1307          `(local-princ ,source))
1308         ((eq (car source) 'function)
1309          `(local-call-global-printer ,source))
1310         ((eq (car source) :cond)
1311          `(cond ,@(mapcar (lambda (clause)
1312                             `(,(compile-test (find-first-field-name
1313                                               (cdr clause))
1314                                              (car clause)
1315                                              funstate)
1316                               ,@(compile-printer-list (cdr clause)
1317                                                       funstate)))
1318                           (cdr source))))
1319         ;; :IF, :UNLESS, and :WHEN are replaced by :COND during preprocessing
1320         (t
1321          `(progn ,@(compile-printer-list source funstate)))))
1322
1323 (defun compile-print (arg-name funstate &optional printer)
1324   (let* ((arg (arg-or-lose arg-name funstate))
1325          (printer (or printer (arg-printer arg)))
1326          (printer-val (value-or-source printer))
1327          (printer-src (source-form printer)))
1328     (remember-printer-use arg funstate)
1329     (cond ((stringp printer-val)
1330            `(local-format-arg ,(arg-value-form arg funstate) ,printer-val))
1331           ((vectorp printer-val)
1332            `(local-princ
1333              (aref ,printer-src
1334                    ,(arg-value-form arg funstate :numeric))))
1335           ((or (functionp printer-val)
1336                (and (consp printer-val) (eq (car printer-val) 'function)))
1337            `(local-call-arg-printer ,(arg-value-form arg funstate)
1338                                     ,printer-src))
1339           ((or (null printer-val) (eq printer-val t))
1340            `(,(if (arg-use-label arg) 'local-princ16 'local-princ)
1341              ,(arg-value-form arg funstate)))
1342           (t
1343            (pd-error "illegal printer: ~S" printer-src)))))
1344
1345 (defun string-or-qsym-p (thing)
1346   (or (stringp thing)
1347       (and (consp thing)
1348            (eq (car thing) 'quote)
1349            (or (stringp (cadr thing))
1350                (symbolp (cadr thing))))))
1351
1352 (defun strip-quote (thing)
1353   (if (and (consp thing) (eq (car thing) 'quote))
1354       (cadr thing)
1355       thing))
1356 \f
1357 (defun compare-fields-form (val-form-1 val-form-2)
1358   (flet ((listify-fields (fields)
1359            (cond ((symbolp fields) fields)
1360                  ((every #'constantp fields) `',fields)
1361                  (t `(list ,@fields)))))
1362     (cond ((or (symbolp val-form-1) (symbolp val-form-2))
1363            `(equal ,(listify-fields val-form-1)
1364                    ,(listify-fields val-form-2)))
1365           (t
1366            `(and ,@(mapcar (lambda (v1 v2) `(= ,v1 ,v2))
1367                            val-form-1 val-form-2))))))
1368
1369 (defun compile-test (subj test funstate)
1370   (when (and (consp test) (symbolp (car test)) (not (keywordp (car test))))
1371     (setf subj (car test)
1372           test (cdr test)))
1373   (let ((key (if (consp test) (car test) test))
1374         (body (if (consp test) (cdr test) nil)))
1375     (cond ((null key)
1376            nil)
1377           ((eq key t)
1378            t)
1379           ((eq key :constant)
1380            (let* ((arg (arg-or-lose subj funstate))
1381                   (fields (arg-fields arg))
1382                   (consts body))
1383              (when (not (= (length fields) (length consts)))
1384                (pd-error "The number of constants doesn't match number of ~
1385                           fields in: (~S :constant~{ ~S~})"
1386                          subj body))
1387              (compare-fields-form (gen-arg-forms arg :numeric funstate)
1388                                   consts)))
1389           ((eq key :positive)
1390            `(> ,(arg-value-form (arg-or-lose subj funstate) funstate :numeric)
1391                0))
1392           ((eq key :negative)
1393            `(< ,(arg-value-form (arg-or-lose subj funstate) funstate :numeric)
1394                0))
1395           ((eq key :same-as)
1396            (let ((arg1 (arg-or-lose subj funstate))
1397                  (arg2 (arg-or-lose (car body) funstate)))
1398              (unless (and (= (length (arg-fields arg1))
1399                              (length (arg-fields arg2)))
1400                           (every (lambda (bs1 bs2)
1401                                    (= (byte-size bs1) (byte-size bs2)))
1402                                  (arg-fields arg1)
1403                                  (arg-fields arg2)))
1404                (pd-error "can't compare differently sized fields: ~
1405                           (~S :same-as ~S)" subj (car body)))
1406              (compare-fields-form (gen-arg-forms arg1 :numeric funstate)
1407                                   (gen-arg-forms arg2 :numeric funstate))))
1408           ((eq key :or)
1409            `(or ,@(mapcar (lambda (sub) (compile-test subj sub funstate))
1410                           body)))
1411           ((eq key :and)
1412            `(and ,@(mapcar (lambda (sub) (compile-test subj sub funstate))
1413                            body)))
1414           ((eq key :not)
1415            `(not ,(compile-test subj (car body) funstate)))
1416           ((and (consp key) (null body))
1417            (compile-test subj key funstate))
1418           (t
1419            (pd-error "bogus test-form: ~S" test)))))
1420 \f
1421 (defun find-labeller-fun (%name args cache)
1422   (let ((labelled-fields
1423          (mapcar #'arg-name (remove-if-not #'arg-use-label args))))
1424     (if (null labelled-fields)
1425         (values nil nil)
1426         (!with-cached-fun
1427             (name funstate cache fun-cache-labellers args
1428              :stem (concatenate 'string "LABELLER-" (string %name))
1429              :constraint labelled-fields)
1430           (let ((labels-form 'labels))
1431             (dolist (arg args)
1432               (when (arg-use-label arg)
1433                 (setf labels-form
1434                       `(let ((labels ,labels-form)
1435                              (addr
1436                               ,(arg-value-form arg funstate :adjusted nil)))
1437                          (if (assoc addr labels :test #'eq)
1438                              labels
1439                              (cons (cons addr nil) labels))))))
1440             `(defun ,name (chunk labels dstate)
1441                (declare (type list labels)
1442                         (type dchunk chunk)
1443                         (type disassem-state dstate))
1444                (flet ((local-filtered-value (offset)
1445                         (declare (type filtered-value-index offset))
1446                         (aref (dstate-filtered-values dstate) offset))
1447                       (local-extract (bytespec)
1448                         (dchunk-extract chunk bytespec))
1449                       (adjust-label (val adjust-fun)
1450                         (funcall adjust-fun val dstate)))
1451                  (declare (ignorable #'local-filtered-value #'local-extract
1452                                      #'adjust-label)
1453                           (inline local-filtered-value local-extract
1454                                   adjust-label))
1455                  (let* ,(make-arg-temp-bindings funstate)
1456                    ,labels-form))))))))
1457
1458 (defun find-prefilter-fun (%name %format-name args cache)
1459   (declare (type (or symbol string) %name %format-name))
1460   (let ((filtered-args (mapcar #'arg-name
1461                                (remove-if-not #'arg-prefilter args))))
1462     (if (null filtered-args)
1463         (values nil nil)
1464         (!with-cached-fun
1465             (name funstate cache fun-cache-prefilters args
1466              :stem (concatenate 'string
1467                                 (string %name)
1468                                 "-"
1469                                 (string %format-name)
1470                                 "-PREFILTER")
1471              :constraint filtered-args)
1472           (collect ((forms))
1473             (dolist (arg args)
1474               (let ((pf (arg-prefilter arg)))
1475                 (when pf
1476                   (forms
1477                    `(setf (local-filtered-value ,(arg-position arg))
1478                           ,(maybe-listify
1479                             (gen-arg-forms arg :filtering funstate)))))
1480                 ))
1481             `(defun ,name (chunk dstate)
1482                (declare (type dchunk chunk)
1483                         (type disassem-state dstate))
1484                (flet (((setf local-filtered-value) (value offset)
1485                        (declare (type filtered-value-index offset))
1486                        (setf (aref (dstate-filtered-values dstate) offset)
1487                              value))
1488                       (local-filter (value filter)
1489                                     (funcall filter value dstate))
1490                       (local-extract (bytespec)
1491                                      (dchunk-extract chunk bytespec)))
1492                 (declare (ignorable #'local-filter #'local-extract)
1493                          (inline (setf local-filtered-value)
1494                                  local-filter local-extract))
1495                 ;; Use them for side effects only.
1496                 (let* ,(make-arg-temp-bindings funstate)
1497                   ,@(forms)))))))))
1498 \f
1499 (defun compute-mask-id (args)
1500   (let ((mask dchunk-zero)
1501         (id dchunk-zero))
1502     (dolist (arg args (values mask id))
1503       (let ((av (arg-value arg)))
1504         (when av
1505           (do ((fields (arg-fields arg) (cdr fields))
1506                (values (if (atom av) (list av) av) (cdr values)))
1507               ((null fields))
1508             (let ((field-mask (dchunk-make-mask (car fields))))
1509               (when (/= (dchunk-and mask field-mask) dchunk-zero)
1510                 (pd-error "The field ~S in arg ~S overlaps some other field."
1511                           (car fields)
1512                           (arg-name arg)))
1513               (dchunk-insertf id (car fields) (car values))
1514               (dchunk-orf mask field-mask))))))))
1515
1516 (defun install-inst-flavors (name flavors)
1517   (setf (gethash name *disassem-insts*)
1518         flavors))
1519 \f
1520 #!-sb-fluid (declaim (inline bytes-to-bits))
1521 (declaim (maybe-inline sign-extend aligned-p align tab tab0))
1522
1523 (defun bytes-to-bits (bytes)
1524   (declare (type disassem-length bytes))
1525   (* bytes sb!vm:n-byte-bits))
1526
1527 (defun bits-to-bytes (bits)
1528   (declare (type disassem-length bits))
1529   (multiple-value-bind (bytes rbits)
1530       (truncate bits sb!vm:n-byte-bits)
1531     (when (not (zerop rbits))
1532       (error "~W bits is not a byte-multiple." bits))
1533     bytes))
1534
1535 (defun sign-extend (int size)
1536   (declare (type integer int)
1537            (type (integer 0 128) size))
1538   (if (logbitp (1- size) int)
1539       (dpb int (byte size 0) -1)
1540       int))
1541
1542 ;;; Is ADDRESS aligned on a SIZE byte boundary?
1543 (defun aligned-p (address size)
1544   (declare (type address address)
1545            (type alignment size))
1546   (zerop (logand (1- size) address)))
1547
1548 ;;; Return ADDRESS aligned *upward* to a SIZE byte boundary.
1549 (defun align (address size)
1550   (declare (type address address)
1551            (type alignment size))
1552   (logandc1 (1- size) (+ (1- size) address)))
1553
1554 (defun tab (column stream)
1555   (funcall (formatter "~V,1t") stream column)
1556   nil)
1557 (defun tab0 (column stream)
1558   (funcall (formatter "~V,0t") stream column)
1559   nil)
1560
1561 (defun princ16 (value stream)
1562   (write value :stream stream :radix t :base 16 :escape nil))
1563 \f
1564 (defun read-signed-suffix (length dstate)
1565   (declare (type (member 8 16 32 64) length)
1566            (type disassem-state dstate)
1567            (optimize (speed 3) (safety 0)))
1568   (sign-extend (read-suffix length dstate) length))
1569 \f
1570 ;;; All state during disassembly. We store some seemingly redundant
1571 ;;; information so that we can allow garbage collect during disassembly and
1572 ;;; not get tripped up by a code block being moved...
1573 (defstruct (disassem-state (:conc-name dstate-)
1574                            (:constructor %make-dstate)
1575                            (:copier nil))
1576   ;; offset of current pos in segment
1577   (cur-offs 0 :type offset)             
1578   ;; offset of next position
1579   (next-offs 0 :type offset)            
1580   ;; a sap pointing to our segment
1581   (segment-sap (missing-arg) :type sb!sys:system-area-pointer)
1582   ;; the current segment                                        
1583   (segment nil :type (or null segment)) 
1584   ;; what to align to in most cases
1585   (alignment sb!vm:n-word-bytes :type alignment) 
1586   (byte-order :little-endian
1587               :type (member :big-endian :little-endian))
1588   ;; for user code to hang stuff off of
1589   (properties nil :type list)
1590   ;; for user code to hang stuff off of, cleared each time before an
1591   ;; instruction is processed
1592   (inst-properties nil :type list)
1593   (filtered-values (make-array max-filtered-value-index)
1594                    :type filtered-value-vector)
1595   ;; used for prettifying printing
1596   (addr-print-len nil :type (or null (integer 0 20)))
1597   (argument-column 0 :type column)
1598   ;; to make output look nicer
1599   (output-state :beginning              
1600                 :type (member :beginning
1601                               :block-boundary
1602                               nil))
1603
1604   ;; alist of (address . label-number)
1605   (labels nil :type list)               
1606   ;; same as LABELS slot data, but in a different form
1607   (label-hash (make-hash-table) :type hash-table)
1608   ;; list of function
1609   (fun-hooks nil :type list)            
1610
1611   ;; alist of (address . label-number), popped as it's used
1612   (cur-labels nil :type list)
1613   ;; OFFS-HOOKs, popped as they're used
1614   (cur-offs-hooks nil :type list)       
1615
1616   ;; for the current location
1617   (notes nil :type list)
1618
1619   ;; currently active source variables
1620   (current-valid-locations nil :type (or null (vector bit))))
1621 (def!method print-object ((dstate disassem-state) stream)
1622   (print-unreadable-object (dstate stream :type t)
1623     (format stream
1624             "+~W~@[ in ~S~]"
1625             (dstate-cur-offs dstate)
1626             (dstate-segment dstate))))
1627
1628 ;;; Return the absolute address of the current instruction in DSTATE.
1629 (defun dstate-cur-addr (dstate)
1630   (the address (+ (seg-virtual-location (dstate-segment dstate))
1631                   (dstate-cur-offs dstate))))
1632
1633 ;;; Return the absolute address of the next instruction in DSTATE.
1634 (defun dstate-next-addr (dstate)
1635   (the address (+ (seg-virtual-location (dstate-segment dstate))
1636                   (dstate-next-offs dstate))))
1637
1638 ;;; Get the value of the property called NAME in DSTATE. Also SETF'able.
1639 ;;;
1640 ;;; KLUDGE: The associated run-time machinery for this is in
1641 ;;; target-disassem.lisp (much later). This is here just to make sure
1642 ;;; it's defined before it's used. -- WHN ca. 19990701
1643 (defmacro dstate-get-prop (dstate name)
1644   `(getf (dstate-properties ,dstate) ,name))
1645
1646 ;;; Push NAME on the list of instruction properties in DSTATE.
1647 (defun dstate-put-inst-prop (dstate name)
1648   (push name (dstate-inst-properties dstate)))
1649
1650 ;;; Return non-NIL if NAME is on the list of instruction properties in
1651 ;;; DSTATE.
1652 (defun dstate-get-inst-prop (dstate name)
1653   (member name (dstate-inst-properties dstate) :test #'eq))