1 ;;;; x86 VM definitions of various system hacking operations
3 ;;;; This software is part of the SBCL system. See the README file for
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.
14 ;;;; type frobbing VOPs
16 (define-vop (lowtag-of)
17 (:translate lowtag-of
)
19 (:args
(object :scs
(any-reg descriptor-reg control-stack
)
21 (:results
(result :scs
(unsigned-reg)))
22 (:result-types positive-fixnum
)
25 (inst and result lowtag-mask
)))
27 (define-vop (widetag-of)
28 (:translate widetag-of
)
30 (:args
(object :scs
(descriptor-reg)))
31 (:temporary
(:sc unsigned-reg
:offset rax-offset
:target result
33 (:results
(result :scs
(unsigned-reg)))
34 (:result-types positive-fixnum
)
36 (inst movzx rax
(reg-in-size object
:byte
))
37 (inst and al-tn lowtag-mask
)
38 (inst cmp al-tn other-pointer-lowtag
)
39 (inst jmp
:e OTHER-PTR
)
40 (inst cmp al-tn fun-pointer-lowtag
)
41 (inst jmp
:e FUNCTION-PTR
)
44 (inst test al-tn fixnum-tag-mask
)
47 ;; Pick off structures and list pointers.
51 ;; must be an other immediate
52 (inst movzx rax
(reg-in-size object
:byte
))
56 (load-type rax object
(- fun-pointer-lowtag
))
60 (load-type rax object
(- other-pointer-lowtag
))
65 (define-vop (%other-pointer-widetag
)
66 (:translate %other-pointer-widetag
)
68 (:args
(object :scs
(descriptor-reg)))
69 (:results
(result :scs
(unsigned-reg)))
70 (:result-types positive-fixnum
)
72 (load-type result object
(- other-pointer-lowtag
))))
74 (define-vop (fun-subtype)
75 (:translate fun-subtype
)
77 (:args
(function :scs
(descriptor-reg)))
78 (:results
(result :scs
(unsigned-reg)))
79 (:result-types positive-fixnum
)
81 (load-type result function
(- fun-pointer-lowtag
))))
83 (define-vop (get-header-data)
84 (:translate get-header-data
)
86 (:args
(x :scs
(descriptor-reg)))
87 (:results
(res :scs
(unsigned-reg)))
88 (:result-types positive-fixnum
)
90 (loadw res x
0 other-pointer-lowtag
)
91 (inst shr res n-widetag-bits
)))
93 (define-vop (get-closure-length)
94 (:translate get-closure-length
)
96 (:args
(x :scs
(descriptor-reg)))
97 (:results
(res :scs
(unsigned-reg)))
98 (:result-types positive-fixnum
)
100 (loadw res x
0 fun-pointer-lowtag
)
101 (inst shr res n-widetag-bits
)
102 ;; In case there are closures in immobile space.
103 #!+immobile-space
(inst and res short-header-max-words
)))
105 (define-vop (set-header-data)
106 (:translate set-header-data
)
108 (:args
(x :scs
(descriptor-reg) :target res
:to
(:result
0))
109 (data :scs
(any-reg) :target eax
))
110 (:arg-types
* positive-fixnum
)
111 (:results
(res :scs
(descriptor-reg)))
112 (:temporary
(:sc unsigned-reg
:offset eax-offset
113 :from
(:argument
1) :to
(:result
0)) eax
)
116 (inst shl eax
(- n-widetag-bits n-fixnum-tag-bits
))
117 (inst mov al-tn
(make-ea :byte
:base x
:disp
(- other-pointer-lowtag
)))
118 (storew eax x
0 other-pointer-lowtag
)
121 (define-vop (pointer-hash)
122 (:translate pointer-hash
)
123 (:args
(ptr :scs
(any-reg descriptor-reg
) :target res
))
124 (:results
(res :scs
(any-reg descriptor-reg
)))
128 ;; Mask the lowtag, and shift the whole address into a positive
130 (inst and res
(lognot lowtag-mask
))
135 (define-vop (dynamic-space-free-pointer)
136 (:results
(int :scs
(sap-reg)))
137 (:result-types system-area-pointer
)
138 (:translate dynamic-space-free-pointer
)
141 (load-symbol-value int
*allocation-pointer
*)))
143 (define-vop (binding-stack-pointer-sap)
144 (:results
(int :scs
(sap-reg)))
145 (:result-types system-area-pointer
)
146 (:translate binding-stack-pointer-sap
)
149 (load-binding-stack-pointer int
)))
151 (defknown (setf binding-stack-pointer-sap
)
152 (system-area-pointer) system-area-pointer
())
154 (define-vop (set-binding-stack-pointer-sap)
155 (:args
(new-value :scs
(sap-reg) :target int
))
156 (:arg-types system-area-pointer
)
157 (:results
(int :scs
(sap-reg)))
158 (:result-types system-area-pointer
)
159 (:translate
(setf binding-stack-pointer-sap
))
162 (store-binding-stack-pointer new-value
)
163 (move int new-value
)))
165 (define-vop (control-stack-pointer-sap)
166 (:results
(int :scs
(sap-reg)))
167 (:result-types system-area-pointer
)
168 (:translate control-stack-pointer-sap
)
173 ;;;; code object frobbing
175 (define-vop (code-instructions)
176 (:translate code-instructions
)
178 (:args
(code :scs
(descriptor-reg) :to
(:result
0)))
179 (:results
(sap :scs
(sap-reg) :from
(:argument
0)))
180 (:result-types system-area-pointer
)
183 (inst mov
(reg-in-size sap
:word
)
184 (make-ea :word
:base code
:disp
(- 1 other-pointer-lowtag
)))
185 (inst lea sap
(make-ea :byte
:base code
:index sap
187 :disp
(- other-pointer-lowtag
)))))
189 (define-vop (compute-fun)
190 (:args
(code :scs
(descriptor-reg) :to
(:result
0))
191 (offset :scs
(signed-reg unsigned-reg
) :to
(:result
0)))
192 (:arg-types
* positive-fixnum
)
193 (:results
(func :scs
(descriptor-reg) :from
(:argument
0)))
196 (inst mov
(reg-in-size func
:word
)
197 (make-ea :word
:base code
:disp
(- 1 other-pointer-lowtag
)))
199 (make-ea :byte
:base offset
:index func
201 :disp
(- fun-pointer-lowtag other-pointer-lowtag
)))
202 (inst add func code
)))
204 (define-vop (%simple-fun-self
)
206 (:translate %simple-fun-self
)
207 (:args
(function :scs
(descriptor-reg)))
208 (:results
(result :scs
(descriptor-reg)))
210 (loadw result function simple-fun-self-slot fun-pointer-lowtag
)
212 (make-ea :byte
:base result
213 :disp
(- fun-pointer-lowtag
214 (* simple-fun-code-offset n-word-bytes
))))))
216 ;;; The closure function slot is a pointer to raw code on X86 instead
217 ;;; of a pointer to the code function object itself. This VOP is used
218 ;;; to reference the function object given the closure object.
219 (define-source-transform %closure-fun
(closure)
220 `(%simple-fun-self
,closure
))
222 (define-vop (%set-fun-self
)
224 (:translate
(setf %simple-fun-self
))
225 (:args
(new-self :scs
(descriptor-reg) :target result
:to
:result
)
226 (function :scs
(descriptor-reg) :to
:result
))
227 (:temporary
(:sc any-reg
:from
(:argument
0) :to
:result
) temp
)
228 (:results
(result :scs
(descriptor-reg)))
231 (make-ea :byte
:base new-self
232 :disp
(- (ash simple-fun-code-offset word-shift
)
233 fun-pointer-lowtag
)))
234 (storew temp function simple-fun-self-slot fun-pointer-lowtag
)
235 (move result new-self
)))
239 ;; only define if the feature is enabled to test building without it
242 (define-vop (symbol-info-vector)
244 (:translate symbol-info-vector
)
245 (:args
(x :scs
(descriptor-reg)))
246 (:results
(res :scs
(descriptor-reg)))
247 (:temporary
(:sc unsigned-reg
:offset rax-offset
) rax
)
249 (loadw res x symbol-info-slot other-pointer-lowtag
)
250 ;; If RES has list-pointer-lowtag, take its CDR. If not, use it as-is.
251 ;; This CMOV safely reads from memory when it does not move, because if
252 ;; there is an info-vector in the slot, it has at least one element.
253 ;; This would compile to almost the same code without a VOP,
254 ;; but using a jmp around a mov instead.
255 (inst lea rax
(make-ea :dword
:base res
:disp
(- list-pointer-lowtag
)))
256 (inst test
(reg-in-size rax
:byte
) lowtag-mask
)
258 (make-ea-for-object-slot res cons-cdr-slot list-pointer-lowtag
))))
259 (define-vop (symbol-plist)
261 (:translate symbol-plist
)
262 (:args
(x :scs
(descriptor-reg)))
263 (:results
(res :scs
(descriptor-reg)))
264 (:temporary
(:sc unsigned-reg
) temp
)
266 (loadw res x symbol-info-slot other-pointer-lowtag
)
267 ;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
268 ;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
269 (loadw res res cons-car-slot list-pointer-lowtag
)
270 (inst mov temp nil-value
)
271 (inst test
(reg-in-size res
:byte
) fixnum-tag-mask
)
272 (inst cmov
:e res temp
))))
274 ;;;; other miscellaneous VOPs
276 (defknown sb
!unix
::receive-pending-interrupt
() (values))
277 (define-vop (sb!unix
::receive-pending-interrupt
)
279 (:translate sb
!unix
::receive-pending-interrupt
)
281 (inst break pending-interrupt-trap
)))
284 (define-vop (insert-safepoint)
286 (:translate sb
!kernel
::gc-safepoint
)
291 ;; 28 unsigned bits is the max before shifting left by 3 that fits in the
292 ;; 'displacement' of an EA. This is hugely generous. The largest offset
293 ;; you'd ever supply is THREAD-NONPOINTER-DATA-SLOT + interrupt depth.
294 (defknown current-thread-offset-sap
((unsigned-byte 28))
295 system-area-pointer
(flushable))
299 (define-vop (current-thread-offset-sap/c
)
300 (:results
(sap :scs
(sap-reg)))
301 (:result-types system-area-pointer
)
302 (:translate current-thread-offset-sap
)
304 (:arg-types
(:constant unsigned-byte
))
307 (inst mov sap
(make-ea :qword
:base thread-base-tn
:disp
(ash n
3)))))
308 (define-vop (current-thread-offset-sap)
309 (:results
(sap :scs
(sap-reg)))
310 (:result-types system-area-pointer
)
311 (:translate current-thread-offset-sap
)
312 (:args
(n :scs
(any-reg) :target sap
))
313 (:arg-types tagged-num
)
317 (make-ea :qword
:base thread-base-tn
:index n
318 :scale
(ash 1 (- word-shift n-fixnum-tag-bits
)))))))
322 (inst break halt-trap
)))
324 (defknown float-wait
() (values))
325 (define-vop (float-wait)
327 (:translate float-wait
)
329 (:save-p
:compute-only
)
331 (note-next-instruction vop
:internal-error
)
336 ;;; the RDTSC instruction (present on Pentium processors and
337 ;;; successors) allows you to access the time-stamp counter, a 64-bit
338 ;;; model-specific register that counts executed cycles. The
339 ;;; instruction returns the low cycle count in EAX and high cycle
342 ;;; In order to obtain more significant results on out-of-order
343 ;;; processors (such as the Pentium II and later), we issue a
344 ;;; serializing CPUID instruction before and after reading the cycle
345 ;;; counter. This instruction is used for its side effect of emptying
346 ;;; the processor pipeline, to ensure that the RDTSC instruction is
347 ;;; executed once all pending instructions have been completed and
348 ;;; before any others. CPUID writes to EBX and ECX in addition to EAX
349 ;;; and EDX, so they need to be added as temporaries.
351 ;;; Note that cache effects mean that the cycle count can vary for
352 ;;; different executions of the same code (it counts cycles, not
353 ;;; retired instructions). Furthermore, the results are per-processor
354 ;;; and not per-process, so are unreliable on multiprocessor machines
355 ;;; where processes can migrate between processors.
357 ;;; This method of obtaining a cycle count has the advantage of being
358 ;;; very fast (around 20 cycles), and of not requiring a system call.
359 ;;; However, you need to know your processor's clock speed to translate
360 ;;; this into real execution time.
362 ;;; FIXME: This about the WITH-CYCLE-COUNTER interface a bit, and then
363 ;;; perhaps export it from SB-SYS.
365 (defknown %read-cycle-counter
() (values (unsigned-byte 32) (unsigned-byte 32)) ())
367 (define-vop (%read-cycle-counter
)
369 (:translate %read-cycle-counter
)
370 (:temporary
(:sc unsigned-reg
:offset eax-offset
:target lo
) eax
)
371 (:temporary
(:sc unsigned-reg
:offset edx-offset
:target hi
) edx
)
372 (:temporary
(:sc unsigned-reg
:offset ebx-offset
) ebx
)
373 (:temporary
(:sc unsigned-reg
:offset ecx-offset
) ecx
)
375 (:results
(hi :scs
(unsigned-reg))
376 (lo :scs
(unsigned-reg)))
377 (:result-types unsigned-num unsigned-num
)
380 ;; Intel docs seem quite consistent on only using CPUID before RDTSC,
381 ;; not both before and after. Go figure.
387 (defmacro with-cycle-counter
(&body body
)
389 "Returns the primary value of BODY as the primary value, and the
390 number of CPU cycles elapsed as secondary value. EXPERIMENTAL."
391 (with-unique-names (hi0 hi1 lo0 lo1
)
392 `(multiple-value-bind (,hi0
,lo0
) (%read-cycle-counter
)
393 (values (locally ,@body
)
394 (multiple-value-bind (,hi1
,lo1
) (%read-cycle-counter
)
395 (+ (ash (- ,hi1
,hi0
) 32)
399 (define-vop (count-me)
400 (:args
(count-vector :scs
(descriptor-reg)))
403 (inst inc
(make-ea :qword
:base count-vector
404 :disp
(- (* (+ vector-data-offset index
) n-word-bytes
)
405 other-pointer-lowtag
)))))
407 ;;;; Memory barrier support
409 #!+memory-barrier-vops
410 (define-vop (%compiler-barrier
)
412 (:translate %compiler-barrier
)
415 #!+memory-barrier-vops
416 (define-vop (%memory-barrier
)
418 (:translate %memory-barrier
)
422 #!+memory-barrier-vops
423 (define-vop (%read-barrier
)
425 (:translate %read-barrier
)
428 #!+memory-barrier-vops
429 (define-vop (%write-barrier
)
431 (:translate %write-barrier
)
434 #!+memory-barrier-vops
435 (define-vop (%data-dependency-barrier
)
437 (:translate %data-dependency-barrier
)
441 (:translate spin-loop-hint
)
448 (defknown %cons-cas-pair
(cons t t t t
) (values t t
))
449 ;; These unsafely permits cmpxchg on any kind of vector, boxed or unboxed
450 ;; and the same goes for instances.
451 (defknown %vector-cas-pair
(simple-array index t t t t
) (values t t
))
452 (defknown %instance-cas-pair
(instance index t t t t
) (values t t
))
454 ;; 32-bit register names here are not an accident - it's a deliberate attempt
455 ;; to keep this exactly in sync with 32-bit code in the hope that somebody
456 ;; will invent a way to share things in common.
458 ((define-cmpxchg-vop (name memory-operand more-stuff
&optional index-arg
)
462 (:args
(data :scs
(descriptor-reg) :to
:eval
)
464 (expected-old-lo :scs
(descriptor-reg any-reg
) :target eax
)
465 (expected-old-hi :scs
(descriptor-reg any-reg
) :target edx
)
466 (new-lo :scs
(descriptor-reg any-reg
) :target ebx
)
467 (new-hi :scs
(descriptor-reg any-reg
) :target ecx
))
468 (:results
(result-lo :scs
(descriptor-reg any-reg
))
469 (result-hi :scs
(descriptor-reg any-reg
)))
470 (:temporary
(:sc unsigned-reg
:offset eax-offset
471 :from
(:argument
2) :to
(:result
0)) eax
)
472 (:temporary
(:sc unsigned-reg
:offset edx-offset
473 :from
(:argument
3) :to
(:result
0)) edx
)
474 (:temporary
(:sc unsigned-reg
:offset ebx-offset
475 :from
(:argument
4) :to
(:result
0)) ebx
)
476 (:temporary
(:sc unsigned-reg
:offset ecx-offset
477 :from
(:argument
5) :to
(:result
0)) ecx
)
479 (move eax expected-old-lo
)
480 (move edx expected-old-hi
)
483 (inst cmpxchg16b
,memory-operand
:lock
)
484 ;; EDX:EAX hold the actual old contents of memory.
485 ;; Manually analyze result lifetimes to avoid clobbering.
486 (cond ((and (location= result-lo edx
) (location= result-hi eax
))
487 (inst xchg eax edx
)) ; unlikely, but possible
488 ((location= result-lo edx
) ; result-hi is not eax
489 (move result-hi edx
) ; move high part first
490 (move result-lo eax
))
491 (t ; result-lo is not edx
492 (move result-lo eax
) ; move low part first
493 (move result-hi edx
)))))))
494 (define-cmpxchg-vop compare-and-exchange-pair
495 (make-ea :dword
:base data
:disp
(- list-pointer-lowtag
))
496 ((:translate %cons-cas-pair
)))
497 (define-cmpxchg-vop compare-and-exchange-pair-indexed
498 (make-ea :dword
:base data
:disp offset
:index index
499 :scale
(ash n-word-bytes
(- n-fixnum-tag-bits
)))
500 ((:variant-vars offset
))
501 ((index :scs
(descriptor-reg any-reg
) :to
:eval
))))
503 ;; The CPU requires 16-byte alignment for the memory operand.
504 ;; A vector's data portion starts on a 16-byte boundary,
505 ;; so any even numbered index is OK.
506 (define-vop (%vector-cas-pair compare-and-exchange-pair-indexed
)
507 (:translate %vector-cas-pair
)
508 (:variant
(- (* n-word-bytes vector-data-offset
) other-pointer-lowtag
)))
510 ;; Here you specify an odd numbered slot, otherwise get a bus error.
511 ;; An instance's first user-visible slot at index 1 is 16-byte-aligned.
512 (define-vop (%instance-cas-pair compare-and-exchange-pair-indexed
)
513 (:translate %instance-cas-pair
)
514 (:variant
(- (* n-word-bytes instance-slots-offset
) instance-pointer-lowtag
)))
516 (defknown %cpu-identification
((unsigned-byte 32) (unsigned-byte 32))
517 (values (unsigned-byte 32) (unsigned-byte 32)
518 (unsigned-byte 32) (unsigned-byte 32)))
520 ;; This instruction does in fact not utilize all bits of the full width (Rxx)
521 ;; regs so it would be wonderful to share this verbatim with x86 32-bit.
522 (define-vop (%cpu-identification
)
524 (:translate %cpu-identification
)
525 (:args
(function :scs
(unsigned-reg) :target eax
)
526 (subfunction :scs
(unsigned-reg) :target ecx
))
527 (:arg-types unsigned-num unsigned-num
)
528 (:results
(a :scs
(unsigned-reg))
529 (b :scs
(unsigned-reg))
530 (c :scs
(unsigned-reg))
531 (d :scs
(unsigned-reg)))
532 (:result-types unsigned-num unsigned-num unsigned-num unsigned-num
)
533 (:temporary
(:sc unsigned-reg
:from
(:argument
0) :to
(:result
0)
534 :offset eax-offset
) eax
)
535 (:temporary
(:sc unsigned-reg
:from
(:argument
1) :to
(:result
2)
536 :offset ecx-offset
) ecx
)
537 (:temporary
(:sc unsigned-reg
:from
:eval
:to
(:result
3)
538 :offset edx-offset
) edx
)
539 (:temporary
(:sc unsigned-reg
:from
:eval
:to
(:result
1)
540 :offset ebx-offset
) ebx
)
543 (move ecx subfunction
)