1 ;;;; target-only stuff from CMU CL's src/compiler/x86/insts.lisp
3 ;;;; i.e. stuff which was in CMU CL's insts.lisp file, but which in
4 ;;;; the SBCL build process can't be compiled into code for the
5 ;;;; cross-compilation host
7 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; This software is derived from the CMU CL system, which was
11 ;;;; written at Carnegie Mellon University and released into the
12 ;;;; public domain. The software is in the public domain and is
13 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
14 ;;;; files for more information.
16 (in-package "SB!X86-64-ASM")
18 ;;; Print to STREAM the name of the general-purpose register encoded by
19 ;;; VALUE and of size WIDTH. For robustness, the high byte registers
20 ;;; (AH, BH, CH, DH) are correctly detected, too, although the compiler
21 ;;; does not use them.
22 (defun print-reg-with-width (value width stream dstate
)
23 (declare (type full-reg value
)
25 (type disassem-state dstate
))
26 (princ (if (and (eq width
:byte
)
28 (not (dstate-get-inst-prop dstate
+rex
+)))
29 (aref *high-byte-reg-names
* (- value
4))
31 (:byte
*byte-reg-names
*)
32 (:word
*word-reg-names
*)
33 (:dword
*dword-reg-names
*)
34 (:qword
*qword-reg-names
*))
37 ;; XXX plus should do some source-var notes
40 (defun print-reg (value stream dstate
)
41 (declare (type full-reg value
)
43 (type disassem-state dstate
))
44 (print-reg-with-width value
45 (inst-operand-size dstate
)
49 (defun print-reg-default-qword (value stream dstate
)
50 (declare (type full-reg value
)
52 (type disassem-state dstate
))
53 (print-reg-with-width value
54 (inst-operand-size-default-qword dstate
)
58 ;; Print a reg that can only be a :DWORD or :QWORD.
59 ;; Avoid use of INST-OPERAND-SIZE because it's wrong for this type of operand.
60 (defun print-d/q-word-reg
(value stream dstate
)
61 (declare (type full-reg value
)
63 (type disassem-state dstate
))
64 (print-reg-with-width value
65 (if (dstate-get-inst-prop dstate
+rex-w
+) :qword
:dword
)
69 (defun print-byte-reg (value stream dstate
)
70 (declare (type full-reg value
)
72 (type disassem-state dstate
))
73 (print-reg-with-width value
:byte stream dstate
))
75 (defun print-addr-reg (value stream dstate
)
76 (declare (type full-reg value
)
78 (type disassem-state dstate
))
79 (print-reg-with-width value
+default-address-size
+ stream dstate
))
81 ;;; Print a register or a memory reference of the given WIDTH.
82 ;;; If SIZED-P is true, add an explicit size indicator for memory
84 (defun print-reg/mem-with-width
(value width sized-p stream dstate
)
85 (declare (type (or list full-reg
) value
)
86 (type (member :byte
:word
:dword
:qword
) width
)
87 (type boolean sized-p
)
89 (type disassem-state dstate
))
90 (if (typep value
'full-reg
)
91 (print-reg-with-width value width stream dstate
)
92 (print-mem-ref (if sized-p
:sized-ref
:ref
) value width stream dstate
)))
94 ;;; Print a register or a memory reference. The width is determined by
95 ;;; calling INST-OPERAND-SIZE.
96 (defun print-reg/mem
(value stream dstate
)
97 (declare (type (or list full-reg
) value
)
99 (type disassem-state dstate
))
100 (print-reg/mem-with-width
101 value
(inst-operand-size dstate
) nil stream dstate
))
103 ;; Same as print-reg/mem, but prints an explicit size indicator for
104 ;; memory references.
105 (defun print-sized-reg/mem
(value stream dstate
)
106 (declare (type (or list full-reg
) value
)
108 (type disassem-state dstate
))
109 (print-reg/mem-with-width
110 value
(inst-operand-size dstate
) t stream dstate
))
112 ;;; Same as print-sized-reg/mem, but with a default operand size of
114 (defun print-sized-reg/mem-default-qword
(value stream dstate
)
115 (declare (type (or list full-reg
) value
)
117 (type disassem-state dstate
))
118 (print-reg/mem-with-width
119 value
(inst-operand-size-default-qword dstate
) t stream dstate
))
121 (defun print-sized-byte-reg/mem
(value stream dstate
)
122 (declare (type (or list full-reg
) value
)
124 (type disassem-state dstate
))
125 (print-reg/mem-with-width value
:byte t stream dstate
))
127 (defun print-sized-word-reg/mem
(value stream dstate
)
128 (declare (type (or list full-reg
) value
)
130 (type disassem-state dstate
))
131 (print-reg/mem-with-width value
:word t stream dstate
))
133 (defun print-sized-dword-reg/mem
(value stream dstate
)
134 (declare (type (or list full-reg
) value
)
136 (type disassem-state dstate
))
137 (print-reg/mem-with-width value
:dword t stream dstate
))
139 (defun print-label (value stream dstate
)
140 (declare (ignore dstate
))
141 (princ16 value stream
))
143 (defun print-xmmreg (value stream dstate
)
144 (declare (type xmmreg value
) (type stream stream
) (ignore dstate
))
145 (format stream
"XMM~d" value
))
147 (defun print-xmmreg/mem
(value stream dstate
)
148 (declare (type (or list xmmreg
) value
)
150 (type disassem-state dstate
))
151 (if (typep value
'xmmreg
)
152 (print-xmmreg value stream dstate
)
153 (print-mem-ref :ref value nil stream dstate
)))
155 (defun print-imm/asm-routine
(value stream dstate
)
156 (maybe-note-assembler-routine value nil dstate
)
157 (maybe-note-static-symbol value dstate
)
158 (princ value stream
))
160 ;;; Prints a memory reference to STREAM. VALUE is a list of
161 ;;; (BASE-REG OFFSET INDEX-REG INDEX-SCALE), where any component may be
162 ;;; missing or nil to indicate that it's not used or has the obvious
163 ;;; default value (e.g., 1 for the index-scale). BASE-REG can be the
164 ;;; symbol RIP or a full register, INDEX-REG a full register. If WIDTH
165 ;;; is non-nil it should be one of the symbols :BYTE, :WORD, :DWORD or
166 ;;; :QWORD; a corresponding size indicator is printed if MODE is :SIZED-REF.
167 ;;; The rationale for supplying WIDTH while eliding a pointer-size qualifier
168 ;;; is that proper dereferencing of RIP-relative constants requires a size,
169 ;;; but in other cases would only add clutter, since a source/destination
170 ;;; register implies a size.
172 (defun print-mem-ref (mode value width stream dstate
)
173 ;; :COMPUTE is used for the LEA instruction - it informs this function
174 ;; that the address is not a memory reference below which is confined
175 ;; the disassembly - the heuristic for detecting the start of unboxed data.
176 ;; LEA is sometimes used to compute the start of a local function for
177 ;; allocate-closures, and it points to valid instructions, not data.
178 (declare (type (member :ref
:sized-ref
:compute
) mode
)
180 (type (member nil
:byte
:word
:dword
:qword
) width
)
182 (type disassem-state dstate
))
183 (when (and width
(eq mode
:sized-ref
))
185 (princ '| PTR | stream
))
186 (write-char #\
[ stream
)
187 (let ((firstp t
) (rip-p nil
))
188 (macrolet ((pel ((var val
) &body body
)
189 ;; Print an element of the address, maybe with
190 ;; a leading separator.
192 ;; Compiler knows that FIRSTP is T in first call to PEL.
194 (declare (muffle-conditions code-deletion-note
))
197 (write-char #\
+ stream
))
199 (setq firstp nil
)))))
200 (pel (base-reg (first value
))
201 (cond ((eql 'rip base-reg
)
203 (princ base-reg stream
))
205 (print-addr-reg base-reg stream dstate
))))
206 (pel (index-reg (third value
))
207 (print-addr-reg index-reg stream dstate
)
208 (let ((index-scale (fourth value
)))
209 (when (and index-scale
(not (= index-scale
1)))
210 (write-char #\
* stream
)
211 (princ index-scale stream
))))
212 (let ((offset (second value
)))
213 (when (and offset
(or firstp
(not (zerop offset
))))
214 (unless (or firstp
(minusp offset
))
215 (write-char #\
+ stream
))
218 (princ offset stream
)
219 (unless (eq mode
:compute
)
220 (let ((addr (+ offset
(dstate-next-addr dstate
))))
221 ;; The origin is zero when disassembling into a trace-file.
222 ;; Don't crash on account of it.
225 1 (note-code-constant-absolute addr dstate width
))
226 (maybe-note-assembler-routine addr nil dstate
)
227 ;; Show the absolute address and maybe the contents.
228 (note (format nil
"[#x~x]~@[ = ~x~]"
232 (unboxed-constant-ref
234 (+ (dstate-next-offs dstate
) offset
)))))
237 (princ16 offset stream
)
239 (nth-value 1 (note-code-constant-absolute offset dstate
))
240 (maybe-note-assembler-routine offset nil dstate
)
241 ;; Static symbols coming frorm CELL-REF
242 (maybe-note-static-symbol (+ offset
(- other-pointer-lowtag
246 (princ offset stream
)))))))
247 (write-char #\
] stream
)
249 (let ((disp (second value
)))
250 (when (and (eql (first value
) #.
(ash (tn-offset sb
!vm
::thread-base-tn
) -
1))
251 (not (third value
)) ; no index
252 (typep disp
'(integer 0 *)) ; positive displacement
253 (seg-code (dstate-segment dstate
)))
254 ;; Try to reverse-engineer which thread-local binding this is
255 (let* ((code (seg-code (dstate-segment dstate
)))
257 (ash (sap-ref-word (int-sap (get-lisp-obj-address code
))
258 (- other-pointer-lowtag
)) -
8))
259 (tls-index (ash disp
(- n-fixnum-tag-bits
))))
260 (loop for word-num from code-constants-offset below header-n-words
261 for obj
= (code-header-ref code word-num
)
262 when
(and (symbolp obj
) (= (symbol-tls-index obj
) tls-index
))
263 do
(return-from print-mem-ref
264 (note (lambda (stream) (format stream
"tls: ~S" obj
))
266 ;; Or maybe we're looking at the 'struct thread' itself
267 (when (< disp max-interrupts
)
270 (primitive-object-slots
271 (find 'sb
!vm
::thread
*primitive-objects
*
272 :key
#'primitive-object-name
)) t
))
273 (slot (find (ash disp
(- word-shift
)) thread-slots
274 :key
#'slot-offset
)))
276 (return-from print-mem-ref
277 (note (lambda (stream)
278 (format stream
"thread.~(~A~)" (slot-name slot
)))
281 ;; Figure out whether LEA should print its EA with just the stuff in brackets,
282 ;; or additionally show the EA as either a label or a hex literal.
283 (defun lea-print-ea (value stream dstate
)
284 (let ((width (inst-operand-size dstate
))
289 ;; Indicate to PRINT-MEM-REF that this is not a memory access.
290 (print-mem-ref :compute value width stream dstate
)
291 (when (eq (first value
) 'rip
)
292 (setq addr
(+ (dstate-next-addr dstate
) (second value
)))))
294 ;; We're robust in allowing VALUE to be an integer (a register),
295 ;; though LEA Rx,Ry is an illegal instruction.
296 ;; Test this before INTEGER since the types overlap.
298 (print-reg-with-width value width stream dstate
))
301 ;; A label for the EA should not print as itself, but as the decomposed
302 ;; addressing mode so that [ADDR] and [RIP+disp] are unmistakable.
303 ;; We can see an INTEGER here because LEA-COMPUTE-LABEL is always called
304 ;; on the operand to LEA, and it will compute an absolute address based
305 ;; off RIP when possible. If :use-labels NIL was specified, there is
306 ;; no hashtable of address to string, so we get the address.
307 ;; But ordinarily we get the string. Either way, the r/m arg reveals the
308 ;; EA calculation. DCHUNK-ZERO is a meaningless value - any would do -
309 ;; because the EA was computed in a prefilter.
310 (print-mem-ref :compute
(reg-r/m-inst-r
/m-arg dchunk-zero dstate
)
313 (when (stringp value
) (setq fmt
"= ~A"))))
315 (note (lambda (s) (format s fmt addr
)) dstate
))))
317 (defun unboxed-constant-ref (dstate segment-offset
)
318 (let* ((seg (dstate-segment dstate
))
320 (sb!disassem
::segment-offs-to-code-offs segment-offset seg
))
321 (unboxed-range (sb!disassem
::seg-unboxed-data-range seg
)))
323 (<= (car unboxed-range
) code-offset
(cdr unboxed-range
))
324 (sap-ref-int (dstate-segment-sap dstate
)
325 segment-offset n-word-bytes
326 (dstate-byte-order dstate
)))))
328 ;;;; interrupt instructions
330 (defun break-control (chunk inst stream dstate
)
331 (declare (ignore inst
))
332 (flet ((nt (x) (if stream
(note x dstate
))))
333 (case #!-ud2-breakpoints
(byte-imm-code chunk dstate
)
334 #!+ud2-breakpoints
(word-imm-code chunk dstate
)
337 (handle-break-args #'snarf-error-junk stream dstate
))
340 (handle-break-args #'snarf-error-junk stream dstate
))
342 (nt "breakpoint trap"))
343 (#.pending-interrupt-trap
344 (nt "pending interrupt trap"))
347 (#.fun-end-breakpoint-trap
348 (nt "function end breakpoint trap"))
349 (#.single-step-around-trap
350 (nt "single-step trap (around)"))
351 (#.single-step-before-trap
352 (nt "single-step trap (before)"))
353 (#.invalid-arg-count-trap
354 (nt "Invalid argument count trap")))))