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