tests: Avoid nonsensical classes and methods in deprecation.impure.lisp
[sbcl.git] / src / compiler / disassem.lisp
blob70d5f41065e10c437a180f7ac0dcaf65a75b8afe
1 ;;;; machine-independent disassembler
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.
12 (in-package "SB!DISASSEM")
14 ;;; types and defaults
16 (defconstant label-column-width 7)
18 (deftype text-width () '(integer 0 1000))
19 (deftype alignment () '(integer 0 64))
20 (deftype offset () 'fixnum)
21 (deftype address () 'word)
22 (deftype disassem-length () '(and unsigned-byte fixnum))
23 (deftype column () '(integer 0 1000))
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)))
31 ;;;; disassembly parameters
33 ;; With a few tweaks, you can use a running SBCL as a cross-assembler
34 ;; and disassembler for other supported backends,
35 ;; if that backend has been converted to use a distinct ASM package.
36 (eval-when (:compile-toplevel :load-toplevel :execute)
37 (defparameter sb!assem::*backend-instruction-set-package*
38 (find-package #.(sb-cold::backend-asm-package-name))))
40 (defvar *disassem-inst-space* nil)
42 ;;; minimum alignment of instructions, in bytes
43 (defvar *disassem-inst-alignment-bytes* sb!vm:n-word-bytes)
44 (declaim (type alignment *disassem-inst-alignment-bytes*))
46 ;; How many columns of output to allow for the address preceding each line.
47 ;; If NIL, use the minimum possible width for the disassembly range.
48 ;; If 0, do not print addresses.
49 (defvar *disassem-location-column-width* nil)
50 (declaim (type (or null text-width) *disassem-location-column-width*))
52 ;;; the width of the column in which instruction-names are printed. A
53 ;;; value of zero gives the effect of not aligning the arguments at
54 ;;; all.
55 (defvar *disassem-opcode-column-width* 0)
56 (declaim (type text-width *disassem-opcode-column-width*))
58 ;;; the width of the column in which instruction-bytes are printed. A
59 ;;; value of zero disables the printing of instruction bytes.
60 (defvar *disassem-inst-column-width* 16
61 "The width of instruction bytes.")
62 (declaim (type text-width *disassem-inst-column-width*))
64 (defvar *disassem-note-column* (+ 45 *disassem-inst-column-width*)
65 "The column in which end-of-line comments for notes are started.")
67 ;;;; A DCHUNK contains the bits we look at to decode an
68 ;;;; instruction.
69 ;;;; I tried to keep this abstract so that if using integers > the machine
70 ;;;; word size conses too much, it can be changed to use bit-vectors or
71 ;;;; something.
72 ;;;;
73 ;;;; KLUDGE: It's not clear that using bit-vectors would be any more efficient.
74 ;;;; Perhaps the abstraction could go away. -- WHN 19991124
76 #!-sb-fluid
77 (declaim (inline dchunk-or dchunk-and dchunk-clear dchunk-not
78 dchunk-make-mask dchunk-make-field
79 dchunk-extract
80 dchunk=
81 dchunk-count-bits))
83 ;;; For variable-length instruction sets, such as x86, it is better to
84 ;;; define the dchunk size to be the smallest number of bits necessary
85 ;;; and sufficient to decode any instruction format, if that quantity
86 ;;; of bits is small enough to avoid bignum consing.
87 ;;; Ideally this constant would go in the 'insts' file for the architecture,
88 ;;; but there's really no easy way to do that at present.
89 (defconstant dchunk-bits
90 #!+x86-64 56
91 #!-x86-64 sb!vm:n-word-bits)
93 (deftype dchunk ()
94 `(unsigned-byte ,dchunk-bits))
95 (deftype dchunk-index ()
96 `(integer 0 ,dchunk-bits))
98 (defconstant dchunk-zero 0)
99 (defconstant dchunk-one (ldb (byte dchunk-bits 0) -1))
101 (defun dchunk-extract (chunk byte-spec)
102 (declare (type dchunk chunk))
103 (the dchunk (ldb byte-spec (the dchunk chunk))))
105 (defmacro dchunk-copy (x)
106 `(the dchunk ,x))
108 (defun dchunk-or (to from)
109 (declare (type dchunk to from))
110 (the dchunk (logior to from)))
111 (defun dchunk-and (to from)
112 (declare (type dchunk to from))
113 (the dchunk (logand to from)))
114 (defun dchunk-clear (to from)
115 (declare (type dchunk to from))
116 (the dchunk (logandc2 to from)))
117 (defun dchunk-not (from)
118 (declare (type dchunk from))
119 (the dchunk (logand dchunk-one (lognot from))))
121 (defmacro dchunk-andf (to from)
122 `(setf ,to (dchunk-and ,to ,from)))
123 (defmacro dchunk-orf (to from)
124 `(setf ,to (dchunk-or ,to ,from)))
125 (defmacro dchunk-clearf (to from)
126 `(setf ,to (dchunk-clear ,to ,from)))
128 (defun dchunk-make-mask (pos)
129 (the dchunk (mask-field pos -1)))
130 (defun dchunk-make-field (pos value)
131 (the dchunk (dpb value pos 0)))
133 (defmacro make-dchunk (value)
134 `(the dchunk ,value))
136 (defun dchunk-corrected-extract (from pos unit-bits byte-order)
137 (declare (type dchunk from))
138 (if (eq byte-order :big-endian)
139 (ldb (byte (byte-size pos)
140 (+ (byte-position pos) (- dchunk-bits unit-bits)))
141 (the dchunk from))
142 (ldb pos (the dchunk from))))
144 (defmacro dchunk-insertf (place pos value)
145 `(setf ,place (the dchunk (dpb ,value ,pos (the dchunk,place)))))
147 (defun dchunk= (x y)
148 (declare (type dchunk x y))
149 (= x y))
150 (defmacro dchunk-zerop (x)
151 `(dchunk= ,x dchunk-zero))
153 (defun dchunk-strict-superset-p (sup sub)
154 (and (zerop (logandc2 sub sup))
155 (not (zerop (logandc2 sup sub)))))
157 (defun dchunk-count-bits (x)
158 (declare (type dchunk x))
159 (logcount x))
161 (defstruct (instruction (:conc-name inst-)
162 (:constructor
163 make-instruction (name format-name print-name
164 length mask id printer labeller
165 prefilters control))
166 (:copier nil))
167 (name nil :type (or symbol string) :read-only t)
168 (format-name nil :type (or symbol string) :read-only t)
170 (mask dchunk-zero :type dchunk :read-only t) ; bits in the inst that are constant
171 (id dchunk-zero :type dchunk :read-only t) ; value of those constant bits
173 (length 0 :type disassem-length :read-only t) ; in bytes
175 (print-name nil :type symbol :read-only t)
177 ;; disassembly "functions"
178 (prefilters nil :type list :read-only t)
179 (labeller nil :type (or list vector) :read-only t)
180 (printer nil :type (or null function) :read-only t)
181 (control nil :type (or null function) :read-only t)
183 ;; instructions that are the same as this instruction but with more
184 ;; constraints
185 (specializers nil :type list))
186 (defmethod print-object ((inst instruction) stream)
187 (print-unreadable-object (inst stream :type t :identity t)
188 (format stream "~A(~A)" (inst-name inst) (inst-format-name inst))))
190 ;;;; an instruction space holds all known machine instructions in a
191 ;;;; form that can be easily searched
193 (defstruct (inst-space (:conc-name ispace-)
194 (:copier nil))
195 (valid-mask dchunk-zero :type dchunk) ; applies to *children*
196 (choices nil :type list))
197 (defmethod print-object ((ispace inst-space) stream)
198 (print-unreadable-object (ispace stream :type t :identity t)))
200 ;;; now that we've defined the structure, we can declaim the type of
201 ;;; the variable:
202 (declaim (type (or null inst-space) *disassem-inst-space*))
204 (defstruct (inst-space-choice (:conc-name ischoice-)
205 (:copier nil))
206 (common-id dchunk-zero :type dchunk) ; applies to *parent's* mask
207 (subspace (missing-arg) :type (or inst-space instruction)))
209 (defstruct (arg (:constructor %make-arg (name))
210 (:copier nil)
211 (:predicate nil))
212 (name nil :type symbol)
213 (fields nil :type list)
215 (value nil :type (or list integer))
216 (sign-extend-p nil :type boolean)
218 ;; functions to use
219 (printer nil :type (or null function vector))
220 (prefilter nil :type (or null function))
221 (use-label nil :type (or boolean function)))
223 (defstruct (instruction-format (:conc-name format-)
224 (:constructor make-inst-format
225 (name length default-printer args))
226 (:copier nil))
227 (name nil)
228 (args nil :type list)
230 (length 0 :type disassem-length) ; in bytes
232 (default-printer nil :type list))
234 ;;; A FUNSTATE holds the state of any arguments used in a disassembly
235 ;;; function. It is a 2-level alist. The outer list maps each ARG to
236 ;;; a list of styles in which that arg can be rendered.
237 ;;; Each rendering is named by a keyword (the key to the inner alist),
238 ;;; and is represented as a list of temp vars and values for them.
239 (defun make-funstate (args) (mapcar #'list args))
241 (defun arg-position (arg funstate)
242 ;;; The THE form is to assert that ARG is found.
243 (the filtered-value-index (position arg funstate :key #'car)))
245 (defun arg-or-lose (name funstate)
246 (or (car (assoc name funstate :key #'arg-name :test #'eq))
247 (pd-error "unknown argument ~S" name)))
249 ;;; machinery to provide more meaningful error messages during compilation
250 (defvar *current-instruction-flavor*)
251 (defun pd-error (fmt &rest args)
252 (if (boundp '*current-instruction-flavor*)
253 (error "~{A printer ~D~}: ~?" *current-instruction-flavor* fmt args)
254 (apply #'error fmt args)))
256 (defun format-or-lose (name)
257 (or (get name 'inst-format)
258 (pd-error "unknown instruction format ~S" name)))
260 ;;; Return a modified copy of ARG that has property values changed
261 ;;; depending on whether it is being used at compile-time or load-time.
262 ;;; This is to avoid evaluating #'FOO references at compile-time
263 ;;; while allowing compile-time manipulation of byte specifiers.
264 (defun massage-arg (spec when)
265 (ecase when
266 (:compile
267 ;; At compile-time we get a restricted view of the DEFINE-ARG-TYPE args,
268 ;; just enough to macroexpand :READER definitions. :TYPE and ::SIGN-EXTEND
269 ;; are as specified, but :PREFILTER, :LABELLER, and :PRINTER are not
270 ;; compile-time evaluated.
271 (loop for (indicator val) on (cdr spec) by #'cddr
272 nconc (case indicator
273 (:sign-extend ; Only a literal T or NIL is allowed
274 (list indicator (the boolean val)))
275 (:prefilter
276 ;; #'ERROR is a placeholder for any compile-time non-nil
277 ;; value. If nil, it must be literally nil, not 'NIL.
278 (list indicator (if val #'error nil)))
279 ((:field :fields :type)
280 (list indicator val)))))
281 (:eval
282 (loop for (indicator raw-val) on (cdr spec) by #'cddr
283 ;; Use NAMED-LAMBDAs to enhance debuggability,
284 for val = (if (typep raw-val '(cons (eql lambda)))
285 `(named-lambda ,(format nil "~A.~A" (car spec) indicator)
286 ,@(cdr raw-val))
287 raw-val)
288 nconc (case indicator
289 (:reader nil) ; drop it
290 (:prefilter ; Enforce compile-time-determined not-nullness.
291 (list indicator (if val `(the (not null) ,val) nil)))
292 (t (list indicator val)))))))
294 (defmacro define-instruction-format ((format-name length-in-bits
295 &key default-printer include)
296 &rest arg-specs)
297 #+sb-xc-host (declare (ignore default-printer))
298 "DEFINE-INSTRUCTION-FORMAT (Name Length {Format-Key Value}*) Arg-Def*
299 Define an instruction format NAME for the disassembler's use. LENGTH is
300 the length of the format in bits.
301 Possible FORMAT-KEYs:
303 :INCLUDE other-format-name
304 Inherit all arguments and properties of the given format. Any
305 arguments defined in the current format definition will either modify
306 the copy of an existing argument (keeping in the same order with
307 respect to when prefilters are called), if it has the same name as
308 one, or be added to the end.
309 :DEFAULT-PRINTER printer-list
310 Use the given PRINTER-LIST as a format to print any instructions of
311 this format when they don't specify something else.
313 Each ARG-DEF defines one argument in the format, and is of the form
314 (Arg-Name {Arg-Key Value}*)
316 Possible ARG-KEYs (the values are evaluated unless otherwise specified):
318 :FIELDS byte-spec-list
319 The argument takes values from these fields in the instruction. If
320 the list is of length one, then the corresponding value is supplied by
321 itself; otherwise it is a list of the values. The list may be NIL.
322 :FIELD byte-spec
323 The same as :FIELDS (list byte-spec).
325 :VALUE value
326 If the argument only has one field, this is the value it should have,
327 otherwise it's a list of the values of the individual fields. This can
328 be overridden in an instruction-definition or a format definition
329 including this one by specifying another, or NIL to indicate that it's
330 variable.
332 :SIGN-EXTEND boolean
333 If non-NIL, the raw value of this argument is sign-extended,
334 immediately after being extracted from the instruction (before any
335 prefilters are run, for instance). If the argument has multiple
336 fields, they are all sign-extended.
338 :TYPE arg-type-name
339 Inherit any properties of the given argument type.
341 :PREFILTER function
342 A function which is called (along with all other prefilters, in the
343 order that their arguments appear in the instruction-format) before
344 any printing is done, to filter the raw value. Any uses of READ-SUFFIX
345 must be done inside a prefilter.
347 :PRINTER function-string-or-vector
348 A function, string, or vector which is used to print this argument.
350 :USE-LABEL
351 If non-NIL, the value of this argument is used as an address, and if
352 that address occurs inside the disassembled code, it is replaced by a
353 label. If this is a function, it is called to filter the value."
354 `(progn
355 (eval-when (:compile-toplevel)
356 (%def-inst-format
357 ',format-name ',include ,length-in-bits nil
358 ,@(mapcar (lambda (arg) `(list ',(car arg) ,@(massage-arg arg :compile)))
359 arg-specs)))
360 ,@(mapcan
361 (lambda (arg-spec)
362 (awhen (getf (cdr arg-spec) :reader)
363 `((defun ,it (dchunk dstate)
364 (declare (ignorable dchunk dstate))
365 (flet ((local-filtered-value (offset)
366 (declare (type filtered-value-index offset))
367 (aref (dstate-filtered-values dstate) offset))
368 (local-extract (bytespec)
369 (dchunk-extract dchunk bytespec)))
370 (declare (ignorable #'local-filtered-value #'local-extract)
371 (inline local-filtered-value local-extract))
372 ;; Delay ARG-FORM-VALUE call until after compile-time-too
373 ;; processing of !%DEF-INSTRUCTION-FORMAT has happened.
374 (macrolet
375 ((reader ()
376 (let* ((format-args
377 (format-args (format-or-lose ',format-name)))
378 (arg (find ',(car arg-spec) format-args
379 :key #'arg-name))
380 (funstate (make-funstate format-args))
381 (*!temp-var-counter* 0)
382 (expr (arg-value-form arg funstate :numeric)))
383 `(let* ,(make-arg-temp-bindings funstate) ,expr))))
384 (reader)))))))
385 arg-specs)
386 #-sb-xc-host ; Host doesn't need the real definition.
387 (%def-inst-format
388 ',format-name ',include ,length-in-bits ,default-printer
389 ,@(mapcar (lambda (arg) `(list ',(car arg) ,@(massage-arg arg :eval)))
390 arg-specs))))
392 (defun %def-inst-format (name inherit length printer &rest arg-specs)
393 (let ((args (if inherit (copy-list (format-args (format-or-lose inherit)))))
394 (seen))
395 (dolist (arg-spec arg-specs)
396 (let* ((arg-name (car arg-spec))
397 (properties (cdr arg-spec))
398 (cell (member arg-name args :key #'arg-name)))
399 (aver (not (memq arg-name seen)))
400 (push arg-name seen)
401 (cond ((not cell)
402 (setq args (nconc args (list (apply #'modify-arg (%make-arg arg-name)
403 length properties)))))
404 (properties
405 (rplaca cell (apply #'modify-arg (copy-structure (car cell))
406 length properties))))))
407 (setf (get name 'inst-format)
408 (make-inst-format name (bits-to-bytes length) printer args))))
410 (defun modify-arg (arg format-length
411 &key (value nil value-p)
412 (type nil type-p)
413 (prefilter nil prefilter-p)
414 (printer nil printer-p)
415 (sign-extend nil sign-extend-p)
416 (use-label nil use-label-p)
417 (field nil field-p)
418 (fields nil fields-p))
419 (when field-p
420 (if fields-p
421 (error ":FIELD and :FIELDS are mutually exclusive")
422 (setf fields (list field) fields-p t)))
423 (when type-p
424 (let ((type-arg (or (get type 'arg-type)
425 (pd-error "unknown argument type: ~S" type))))
426 (setf (arg-printer arg) (arg-printer type-arg))
427 (setf (arg-prefilter arg) (arg-prefilter type-arg))
428 (setf (arg-sign-extend-p arg) (arg-sign-extend-p type-arg))
429 (setf (arg-use-label arg) (arg-use-label type-arg))))
430 (when value-p
431 (setf (arg-value arg) value))
432 (when prefilter-p
433 (setf (arg-prefilter arg) prefilter))
434 (when sign-extend-p
435 (setf (arg-sign-extend-p arg) sign-extend))
436 (when printer-p
437 (setf (arg-printer arg) printer))
438 (when use-label-p
439 (setf (arg-use-label arg) use-label))
440 (when fields-p
441 (setf (arg-fields arg)
442 (mapcar (lambda (bytespec)
443 (when (> (+ (byte-position bytespec) (byte-size bytespec))
444 format-length)
445 (error "~@<in arg ~S: ~3I~:_~
446 The field ~S doesn't fit in an ~
447 instruction-format ~W bits wide.~:>"
448 (arg-name arg) bytespec format-length))
449 (correct-dchunk-bytespec-for-endianness
450 bytespec format-length sb!c:*backend-byte-order*))
451 fields)))
452 arg)
454 (defun arg-value-form (arg funstate
455 &optional
456 (rendering :final)
457 (allow-multiple-p (neq rendering :numeric)))
458 (let ((forms (gen-arg-forms arg rendering funstate)))
459 (when (and (not allow-multiple-p)
460 (listp forms)
461 (/= (length forms) 1))
462 (pd-error "~S must not have multiple values." arg))
463 (maybe-listify forms)))
465 (defun correct-dchunk-bytespec-for-endianness (bs unit-bits byte-order)
466 (if (eq byte-order :big-endian)
467 (byte (byte-size bs) (+ (byte-position bs) (- dchunk-bits unit-bits)))
468 bs))
470 (defun make-arg-temp-bindings (funstate)
471 (let ((bindings nil))
472 ;; Prefilters have to be called in the correct order, so reverse FUNSTATE
473 ;; because we're using PUSH in the inner loop.
474 (dolist (arg-cell (reverse funstate) bindings)
475 ;; These sublists are "backwards", so PUSH ends up being correct.
476 (dolist (rendering (cdr arg-cell))
477 (let* ((binding (cdr rendering))
478 (vars (car binding))
479 (vals (cdr binding)))
480 (if (listp vars)
481 (mapc (lambda (var val) (push `(,var ,val) bindings)) vars vals)
482 (push `(,vars ,vals) bindings)))))))
484 ;;; Return the form(s) that should be evaluated to render ARG in the chosen
485 ;;; RENDERING style, which is one of :RAW, :SIGN-EXTENDED,
486 ;;; :FILTERED, :NUMERIC, and :FINAL. Each rendering depends on the preceding
487 ;;; one, so asking for :FINAL will implicitly compute all renderings.
488 (defvar *!temp-var-counter*)
489 (defun gen-arg-forms (arg rendering funstate)
490 (labels ((tempvars (n)
491 (if (plusp n)
492 (cons (package-symbolicate
493 (load-time-value (find-package "SB!DISASSEM"))
494 ".T" (write-to-string (incf *!temp-var-counter*)))
495 (tempvars (1- n))))))
496 (let* ((arg-cell (assq arg funstate))
497 (rendering-temps (cdr (assq rendering (cdr arg-cell))))
498 (vars (car rendering-temps))
499 (forms (cdr rendering-temps)))
500 (unless forms
501 (multiple-value-bind (new-forms single-value-p)
502 (%gen-arg-forms arg rendering funstate)
503 (setq forms new-forms
504 vars (cond ((or single-value-p (atom forms))
505 (if (symbolp forms) vars (car (tempvars 1))))
506 ((every #'symbolp forms)
507 ;; just use the same as the forms
508 nil)
510 (tempvars (length forms)))))
511 (push (list* rendering vars forms) (cdr arg-cell))))
512 (or vars forms))))
514 (defun maybe-listify (forms)
515 (cond ((atom forms)
516 forms)
517 ((/= (length forms) 1)
518 `(list ,@forms))
520 (car forms))))
522 ;;; DEFINE-ARG-TYPE Name {Key Value}*
524 ;;; Define a disassembler argument type NAME (which can then be referenced in
525 ;;; another argument definition using the :TYPE argument). &KEY args are:
527 ;;; :SIGN-EXTEND boolean
528 ;;; If non-NIL, the raw value of this argument is sign-extended.
530 ;;; :TYPE arg-type-name
531 ;;; Inherit any properties of given arg-type.
533 ;;; :PREFILTER function
534 ;;; A function which is called (along with all other prefilters,
535 ;;; in the order that their arguments appear in the instruction-
536 ;;; format) before any printing is done, to filter the raw value.
537 ;;; Any uses of READ-SUFFIX must be done inside a prefilter.
539 ;;; :PRINTER function-string-or-vector
540 ;;; A function, string, or vector which is used to print an argument of
541 ;;; this type.
543 ;;; :USE-LABEL
544 ;;; If non-NIL, the value of an argument of this type is used as
545 ;;; an address, and if that address occurs inside the disassembled
546 ;;; code, it is replaced by a label. If this is a function, it is
547 ;;; called to filter the value.
548 (defmacro define-arg-type (name &rest args
549 &key ((:type inherit))
550 sign-extend prefilter printer use-label)
551 (declare (ignore sign-extend prefilter printer use-label))
552 ;; FIXME: this should be an *unevaluated* macro arg (named :INHERIT)
553 (aver (typep inherit '(or null (cons (eql quote) (cons symbol null)))))
554 (let ((pair (cons name (loop for (ind val) on args by #'cddr
555 unless (eq ind :type)
556 nconc (list ind val)))))
557 `(progn
558 (eval-when (:compile-toplevel)
559 (%def-arg-type ',name ,inherit ,@(massage-arg pair :compile)))
560 #-sb-xc-host ; Host doesn't need the real definition.
561 (%def-arg-type ',name ,inherit ,@(massage-arg pair :eval)))))
563 (defun %def-arg-type (name inherit &rest properties)
564 (setf (get name 'arg-type)
565 (apply 'modify-arg (%make-arg name) nil
566 (nconc (when inherit (list :type inherit)) properties))))
568 (defun %gen-arg-forms (arg rendering funstate)
569 (declare (type arg arg) (type list funstate))
570 (ecase rendering
571 (:raw ; just extract the bits
572 (mapcar (lambda (bytespec)
573 `(the (unsigned-byte ,(byte-size bytespec))
574 (local-extract ',bytespec)))
575 (arg-fields arg)))
576 (:sign-extended ; sign-extend, or not
577 (let ((raw-forms (gen-arg-forms arg :raw funstate)))
578 (if (and (arg-sign-extend-p arg) (listp raw-forms))
579 (mapcar (lambda (form field)
580 `(the (signed-byte ,(byte-size field))
581 (sign-extend ,form ,(byte-size field))))
582 raw-forms
583 (arg-fields arg))
584 raw-forms)))
585 (:filtered ; extract from the prefiltered value vector
586 (let ((pf (arg-prefilter arg)))
587 (if pf
588 (values `(local-filtered-value ,(arg-position arg funstate)) t)
589 (gen-arg-forms arg :sign-extended funstate))))
590 (:numeric ; pass the filtered value to the label adjuster, or not
591 (let ((filtered-forms (gen-arg-forms arg :filtered funstate))
592 (use-label (arg-use-label arg)))
593 ;; use-label = T means that the prefiltered value is already an address,
594 ;; otherwise non-nil means a function to call, and NIL means not a label.
595 ;; So only the middle case needs to call ADJUST-LABEL.
596 (if (and use-label (neq use-label t))
597 `((adjust-label ,(maybe-listify filtered-forms) ,use-label))
598 filtered-forms)))
599 (:final ; if arg is not a label, return numeric value, otherwise a string
600 (let ((numeric-forms (gen-arg-forms arg :numeric funstate)))
601 (if (arg-use-label arg)
602 `((lookup-label ,(maybe-listify numeric-forms)))
603 numeric-forms)))))
605 (defun find-printer-fun (printer-source args cache *current-instruction-flavor*)
606 (let* ((source (preprocess-printer printer-source args))
607 (funstate (make-funstate args))
608 (forms (let ((*!temp-var-counter* 0))
609 (compile-printer-list source funstate)))
610 (bindings (make-arg-temp-bindings funstate))
611 (guts `(let* ,bindings ,@forms))
612 (sub-table (assq :printer cache)))
613 (or (cdr (assoc guts (cdr sub-table) :test #'equal))
614 (let ((template
615 '(lambda (chunk inst stream dstate
616 &aux (chunk (truly-the dchunk chunk))
617 (inst (truly-the instruction inst))
618 (stream (truly-the stream stream))
619 (dstate (truly-the disassem-state dstate)))
620 (macrolet ((local-format-arg (arg fmt)
621 `(funcall (formatter ,fmt) stream ,arg)))
622 (flet ((local-tab-to-arg-column ()
623 (tab (dstate-argument-column dstate) stream))
624 (local-print-name ()
625 (princ (inst-print-name inst) stream))
626 (local-write-char (ch)
627 (write-char ch stream))
628 (local-princ (thing)
629 (princ thing stream))
630 (local-princ16 (thing)
631 (princ16 thing stream))
632 (local-call-arg-printer (arg printer)
633 (funcall printer arg stream dstate))
634 (local-call-global-printer (fun)
635 (funcall fun chunk inst stream dstate))
636 (local-filtered-value (offset)
637 (declare (type filtered-value-index offset))
638 (aref (dstate-filtered-values dstate) offset))
639 (local-extract (bytespec)
640 (dchunk-extract chunk bytespec))
641 (lookup-label (lab)
642 (or (gethash lab (dstate-label-hash dstate))
643 lab))
644 (adjust-label (val adjust-fun)
645 (funcall adjust-fun val dstate)))
646 (declare (ignorable #'local-tab-to-arg-column
647 #'local-print-name
648 #'local-princ #'local-princ16
649 #'local-write-char
650 #'local-call-arg-printer
651 #'local-call-global-printer
652 #'local-extract
653 #'local-filtered-value
654 #'lookup-label #'adjust-label)
655 (inline local-tab-to-arg-column
656 local-princ local-princ16
657 local-call-arg-printer local-call-global-printer
658 local-filtered-value local-extract
659 lookup-label adjust-label))
660 :body)))))
661 (cdar (push (cons guts (compile nil (subst guts :body template)))
662 (cdr sub-table)))))))
664 (defun preprocess-test (subj form args)
665 (multiple-value-bind (subj test)
666 (if (and (consp form) (symbolp (car form)) (not (keywordp (car form))))
667 (values (car form) (cdr form))
668 (values subj form))
669 (let ((key (if (consp test) (car test) test))
670 (body (if (consp test) (cdr test) nil)))
671 (case key
672 (:constant
673 (if (null body)
674 ;; If no supplied constant values, just any constant is ok,
675 ;; just see whether there's some constant value in the arg.
676 (not
677 (null
678 (arg-value
679 (or (find subj args :key #'arg-name)
680 (pd-error "unknown argument ~S" subj)))))
681 ;; Otherwise, defer to run-time.
682 form))
683 ((:or :and :not)
684 (sharing-cons
685 form
686 subj
687 (sharing-cons
688 test
690 (sharing-mapcar
691 (lambda (sub-test)
692 (preprocess-test subj sub-test args))
693 body))))
694 (t form)))))
696 (defun preprocess-conditionals (printer args)
697 (if (atom printer)
698 printer
699 (case (car printer)
700 (:unless
701 (preprocess-conditionals
702 `(:cond ((:not ,(nth 1 printer)) ,@(nthcdr 2 printer)))
703 args))
704 (:when
705 (preprocess-conditionals `(:cond (,(cdr printer))) args))
706 (:if
707 (preprocess-conditionals
708 `(:cond (,(nth 1 printer) ,(nth 2 printer))
709 (t ,(nth 3 printer)))
710 args))
711 (:cond
712 (sharing-cons
713 printer
714 :cond
715 (sharing-mapcar
716 (lambda (clause)
717 (let ((filtered-body
718 (sharing-mapcar
719 (lambda (sub-printer)
720 (preprocess-conditionals sub-printer args))
721 (cdr clause))))
722 (sharing-cons
723 clause
724 (preprocess-test (find-first-field-name filtered-body)
725 (car clause)
726 args)
727 filtered-body)))
728 (cdr printer))))
729 (quote printer)
731 (sharing-mapcar
732 (lambda (sub-printer)
733 (preprocess-conditionals sub-printer args))
734 printer)))))
736 ;;; Return a version of the disassembly-template PRINTER with
737 ;;; compile-time tests (e.g. :constant without a value), and any
738 ;;; :CHOOSE operators resolved properly for the args ARGS.
740 ;;; (:CHOOSE Sub*) simply returns the first Sub in which every field
741 ;;; reference refers to a valid arg.
742 (defun preprocess-printer (printer args)
743 (preprocess-conditionals (preprocess-chooses printer args) args))
745 ;;; Return the first non-keyword symbol in a depth-first search of TREE.
746 (defun find-first-field-name (tree)
747 (cond ((null tree)
748 nil)
749 ((and (symbolp tree) (not (keywordp tree)))
750 tree)
751 ((atom tree)
752 nil)
753 ((eq (car tree) 'quote)
754 nil)
756 (or (find-first-field-name (car tree))
757 (find-first-field-name (cdr tree))))))
759 (defun preprocess-chooses (printer args)
760 (cond ((atom printer)
761 printer)
762 ((eq (car printer) :choose)
763 (pick-printer-choice (cdr printer) args))
765 (sharing-mapcar (lambda (sub) (preprocess-chooses sub args))
766 printer))))
768 ;;;; some simple functions that help avoid consing when we're just
769 ;;;; recursively filtering things that usually don't change
771 (defun sharing-cons (old-cons car cdr)
772 "If CAR is eq to the car of OLD-CONS and CDR is eq to the CDR, return
773 OLD-CONS, otherwise return (cons CAR CDR)."
774 (if (and (eq car (car old-cons)) (eq cdr (cdr old-cons)))
775 old-cons
776 (cons car cdr)))
778 (defun sharing-mapcar (fun list)
779 (declare (type function fun))
780 "A simple (one list arg) mapcar that avoids consing up a new list
781 as long as the results of calling FUN on the elements of LIST are
782 eq to the original."
783 (and list
784 (sharing-cons list
785 (funcall fun (car list))
786 (sharing-mapcar fun (cdr list)))))
788 (defun all-arg-refs-relevant-p (printer args)
789 (cond ((or (null printer) (keywordp printer) (eq printer t))
791 ((symbolp printer)
792 (find printer args :key #'arg-name))
793 ((listp printer)
794 (every (lambda (x) (all-arg-refs-relevant-p x args))
795 printer))
796 (t t)))
798 (defun pick-printer-choice (choices args)
799 (dolist (choice choices
800 (pd-error "no suitable choice found in ~S" choices))
801 (when (all-arg-refs-relevant-p choice args)
802 (return choice))))
804 (defun compile-printer-list (sources funstate)
805 (when sources
806 (cons (compile-printer-body (car sources) funstate)
807 (compile-printer-list (cdr sources) funstate))))
809 (defun compile-printer-body (source funstate)
810 (cond ((null source)
811 nil)
812 ((eq source :name)
813 `(local-print-name))
814 ((eq source :tab)
815 `(local-tab-to-arg-column))
816 ((keywordp source)
817 (pd-error "unknown printer element: ~S" source))
818 ((symbolp source)
819 (compile-print source funstate))
820 ((atom source)
821 `(local-princ ',source))
822 ((eq (car source) :using)
823 (unless (or (stringp (cadr source))
824 (and (listp (cadr source))
825 (eq (caadr source) 'function)))
826 (pd-error "The first arg to :USING must be a string or #'function."))
827 ;; For (:using #'F) to be stuffed in properly, the printer as expressed
828 ;; in its DSL would have to compile-time expand into a thing that
829 ;; reconstructs it such that #'F forms don't appear inside quoted list
830 ;; structure. Lacking the ability to do that, we treat #'F as a bit of
831 ;; syntax to be evaluated manually.
832 (compile-print (caddr source) funstate
833 (let ((f (cadr source)))
834 (if (typep f '(cons (eql function) (cons symbol null)))
835 (symbol-function (second f))
836 f))))
837 ((eq (car source) :plus-integer)
838 ;; prints the given field proceed with a + or a -
839 (let ((form
840 (arg-value-form (arg-or-lose (cadr source) funstate)
841 funstate
842 :numeric)))
843 `(progn
844 (when (>= ,form 0)
845 (local-write-char #\+))
846 (local-princ ,form))))
847 ((eq (car source) 'quote)
848 `(local-princ ,source))
849 ((eq (car source) 'function)
850 `(local-call-global-printer ,source))
851 ((eq (car source) :cond)
852 `(cond ,@(mapcar (lambda (clause)
853 `(,(compile-test (find-first-field-name
854 (cdr clause))
855 (car clause)
856 funstate)
857 ,@(compile-printer-list (cdr clause)
858 funstate)))
859 (cdr source))))
860 ;; :IF, :UNLESS, and :WHEN are replaced by :COND during preprocessing
862 `(progn ,@(compile-printer-list source funstate)))))
864 (defun compile-print (arg-name funstate &optional printer)
865 (let* ((arg (arg-or-lose arg-name funstate))
866 (printer (or printer (arg-printer arg))))
867 (etypecase printer
868 (string
869 `(local-format-arg ,(arg-value-form arg funstate) ,printer))
870 (vector
871 `(local-princ (aref ,printer ,(arg-value-form arg funstate :numeric))))
872 ((or function (cons (eql function)))
873 `(local-call-arg-printer ,(arg-value-form arg funstate) ,printer))
874 (boolean
875 `(,(if (arg-use-label arg) 'local-princ16 'local-princ)
876 ,(arg-value-form arg funstate))))))
878 (defun compare-fields-form (val-form-1 val-form-2)
879 (flet ((listify-fields (fields)
880 (cond ((symbolp fields) fields)
881 ((every #'constantp fields) `',fields)
882 (t `(list ,@fields)))))
883 (cond ((or (symbolp val-form-1) (symbolp val-form-2))
884 `(equal ,(listify-fields val-form-1)
885 ,(listify-fields val-form-2)))
887 `(and ,@(mapcar (lambda (v1 v2) `(= ,v1 ,v2))
888 val-form-1 val-form-2))))))
890 (defun compile-test (subj test funstate)
891 (when (and (consp test) (symbolp (car test)) (not (keywordp (car test))))
892 (setf subj (car test)
893 test (cdr test)))
894 (let ((key (if (consp test) (car test) test))
895 (body (if (consp test) (cdr test) nil)))
896 (cond ((null key)
897 nil)
898 ((eq key t)
900 ((eq key :constant)
901 (let* ((arg (arg-or-lose subj funstate))
902 (fields (arg-fields arg))
903 (consts body))
904 (when (not (= (length fields) (length consts)))
905 (pd-error "The number of constants doesn't match number of ~
906 fields in: (~S :constant~{ ~S~})"
907 subj body))
908 (compare-fields-form (gen-arg-forms arg :numeric funstate)
909 consts)))
910 ((eq key :positive)
911 `(> ,(arg-value-form (arg-or-lose subj funstate) funstate :numeric)
913 ((eq key :negative)
914 `(< ,(arg-value-form (arg-or-lose subj funstate) funstate :numeric)
916 ((eq key :same-as)
917 (let ((arg1 (arg-or-lose subj funstate))
918 (arg2 (arg-or-lose (car body) funstate)))
919 (unless (and (= (length (arg-fields arg1))
920 (length (arg-fields arg2)))
921 (every (lambda (bs1 bs2)
922 (= (byte-size bs1) (byte-size bs2)))
923 (arg-fields arg1)
924 (arg-fields arg2)))
925 (pd-error "can't compare differently sized fields: ~
926 (~S :same-as ~S)" subj (car body)))
927 (compare-fields-form (gen-arg-forms arg1 :numeric funstate)
928 (gen-arg-forms arg2 :numeric funstate))))
929 ((eq key :or)
930 `(or ,@(mapcar (lambda (sub) (compile-test subj sub funstate))
931 body)))
932 ((eq key :and)
933 `(and ,@(mapcar (lambda (sub) (compile-test subj sub funstate))
934 body)))
935 ((eq key :not)
936 `(not ,(compile-test subj (car body) funstate)))
937 ((and (consp key) (null body))
938 (compile-test subj key funstate))
940 (pd-error "bogus test-form: ~S" test)))))
942 (defun compute-mask-id (args)
943 (let ((mask dchunk-zero)
944 (id dchunk-zero))
945 (dolist (arg args (values mask id))
946 (let ((av (arg-value arg)))
947 (when av
948 (do ((fields (arg-fields arg) (cdr fields))
949 (values (if (atom av) (list av) av) (cdr values)))
950 ((null fields))
951 (let ((field-mask (dchunk-make-mask (car fields))))
952 (when (/= (dchunk-and mask field-mask) dchunk-zero)
953 (pd-error "The field ~S in arg ~S overlaps some other field."
954 (car fields)
955 (arg-name arg)))
956 (dchunk-insertf id (car fields) (car values))
957 (dchunk-orf mask field-mask))))))))
959 #!-sb-fluid (declaim (inline bytes-to-bits))
960 (declaim (maybe-inline sign-extend aligned-p align tab tab0))
962 (defun bytes-to-bits (bytes)
963 (declare (type disassem-length bytes))
964 (* bytes sb!vm:n-byte-bits))
966 (defun bits-to-bytes (bits)
967 (declare (type disassem-length bits))
968 (multiple-value-bind (bytes rbits)
969 (truncate bits sb!vm:n-byte-bits)
970 (when (not (zerop rbits))
971 (error "~W bits is not a byte-multiple." bits))
972 bytes))
974 (defun sign-extend (int size)
975 (declare (type integer int)
976 (type (integer 0 128) size))
977 (if (logbitp (1- size) int)
978 (dpb int (byte size 0) -1)
979 int))
981 ;;; Is ADDRESS aligned on a SIZE byte boundary?
982 (defun aligned-p (address size)
983 (declare (type address address)
984 (type alignment size))
985 (zerop (logand (1- size) address)))
987 ;;; Return ADDRESS aligned *upward* to a SIZE byte boundary.
988 (defun align (address size)
989 (declare (type address address)
990 (type alignment size))
991 (logandc1 (1- size) (+ (1- size) address)))
993 (defun tab (column stream)
994 (funcall (formatter "~V,1t") stream column)
995 nil)
996 (defun tab0 (column stream)
997 (funcall (formatter "~V,0t") stream column)
998 nil)
1000 (defun princ16 (value stream)
1001 (write value :stream stream :radix t :base 16 :escape nil))
1003 (defstruct (storage-info (:copier nil))
1004 (groups nil :type list) ; alist of (name . location-group)
1005 (debug-vars #() :type vector))
1007 (defstruct (segment (:conc-name seg-)
1008 (:constructor %make-segment)
1009 (:copier nil))
1010 (sap-maker (missing-arg)
1011 :type (function () #-sb-xc-host system-area-pointer))
1012 ;; Length in bytes of the range of memory covered by this segment.
1013 (length 0 :type disassem-length)
1014 (virtual-location 0 :type address)
1015 (storage-info nil :type (or null storage-info))
1016 ;; KLUDGE: CODE-COMPONENT is not a type the host understands
1017 #-sb-xc-host (code nil :type (or null code-component))
1018 (unboxed-data-range nil :type (or null (cons fixnum fixnum)))
1019 (hooks nil :type list))
1021 ;;; All state during disassembly. We store some seemingly redundant
1022 ;;; information so that we can allow garbage collect during disassembly and
1023 ;;; not get tripped up by a code block being moved...
1024 (defstruct (disassem-state (:conc-name dstate-)
1025 (:constructor %make-dstate)
1026 (:copier nil))
1027 ;; offset of current pos in segment
1028 (cur-offs 0 :type offset)
1029 ;; offset of next position
1030 (next-offs 0 :type offset)
1031 ;; a sap pointing to our segment
1032 (segment-sap nil :type (or null #-sb-xc-host system-area-pointer))
1033 ;; the current segment
1034 (segment nil :type (or null segment))
1035 ;; to avoid buffer overrun at segment end, we might need to copy bytes
1036 ;; here first because sap-ref-dchunk reads a fixed length.
1037 (scratch-buf (make-array 8 :element-type '(unsigned-byte 8)))
1038 ;; what to align to in most cases
1039 (alignment sb!vm:n-word-bytes :type alignment)
1040 (byte-order :little-endian
1041 :type (member :big-endian :little-endian))
1042 ;; for user code to hang stuff off of
1043 (properties nil :type list)
1044 ;; for user code to hang stuff off of, cleared each time after a
1045 ;; non-prefix instruction is processed
1046 (inst-properties nil :type (or fixnum list))
1047 (filtered-values (make-array max-filtered-value-index)
1048 :type filtered-value-vector)
1049 ;; to avoid consing decoded values, a prefilter can keep a chain
1050 ;; of objects in these slots. The objects returned here
1051 ;; are reusable for the next instruction.
1052 (filtered-arg-pool-in-use)
1053 (filtered-arg-pool-free)
1054 ;; used for prettifying printing
1055 (addr-print-len nil :type (or null (integer 0 20)))
1056 (argument-column 0 :type column)
1057 ;; to make output look nicer
1058 (output-state :beginning
1059 :type (member :beginning
1060 :block-boundary
1061 nil))
1063 ;; alist of (address . label-number)
1064 (labels nil :type list)
1065 ;; same as LABELS slot data, but in a different form
1066 (label-hash (make-hash-table) :type hash-table)
1067 ;; list of function
1068 (fun-hooks nil :type list)
1070 ;; alist of (address . label-number), popped as it's used
1071 (cur-labels nil :type list)
1072 ;; OFFS-HOOKs, popped as they're used
1073 (cur-offs-hooks nil :type list)
1075 ;; for the current location
1076 (notes nil :type list)
1078 ;; currently active source variables
1079 (current-valid-locations nil :type (or null (vector bit))))
1080 (defmethod print-object ((dstate disassem-state) stream)
1081 (print-unreadable-object (dstate stream :type t)
1082 (format stream
1083 "+~W~@[ in ~S~]"
1084 (dstate-cur-offs dstate)
1085 (dstate-segment dstate))))
1087 ;;; Return the absolute address of the current instruction in DSTATE.
1088 (defun dstate-cur-addr (dstate)
1089 (the address (+ (seg-virtual-location (dstate-segment dstate))
1090 (dstate-cur-offs dstate))))
1092 ;;; Return the absolute address of the next instruction in DSTATE.
1093 (defun dstate-next-addr (dstate)
1094 (the address (+ (seg-virtual-location (dstate-segment dstate))
1095 (dstate-next-offs dstate))))
1097 ;;; Get the value of the property called NAME in DSTATE. Also SETF'able.
1099 ;;; KLUDGE: The associated run-time machinery for this is in
1100 ;;; target-disassem.lisp (much later). This is here just to make sure
1101 ;;; it's defined before it's used. -- WHN ca. 19990701
1102 (defmacro dstate-get-prop (dstate name)
1103 `(getf (dstate-properties ,dstate) ,name))
1105 ;;; Put PROPERTY into the set of instruction properties in DSTATE.
1106 ;;; PROPERTY can be a fixnum or symbol, but any given backend
1107 ;;; must exclusively use one or the other property representation.
1108 (defun dstate-put-inst-prop (dstate property)
1109 (if (fixnump property)
1110 (setf (dstate-inst-properties dstate)
1111 (logior (or (dstate-inst-properties dstate) 0) property))
1112 (push property (dstate-inst-properties dstate))))
1114 ;;; Return non-NIL if PROPERTY is in the set of instruction properties in
1115 ;;; DSTATE. As with -PUT-INST-PROP, we can have a bitmask or a plist.
1116 (defun dstate-get-inst-prop (dstate property)
1117 (if (fixnump property)
1118 (logtest (or (dstate-inst-properties dstate) 0) property)
1119 (memq property (dstate-inst-properties dstate))))
1121 (declaim (ftype function read-suffix))
1122 (defun read-signed-suffix (length dstate)
1123 (declare (type (member 8 16 32 64) length)
1124 (type disassem-state dstate)
1125 (optimize (speed 3) (safety 0)))
1126 (sign-extend (read-suffix length dstate) length))