1 ;;;; the x86 definitions of some general purpose memory reference VOPs
2 ;;;; inherited by basic memory reference operations
4 ;;;; This software is part of the SBCL system. See the README file for
7 ;;;; This software is derived from the CMU CL system, which was
8 ;;;; written at Carnegie Mellon University and released into the
9 ;;;; public domain. The software is in the public domain and is
10 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
11 ;;;; files for more information.
15 (defun symbol-slot-ea (symbol slot
)
16 (ea (let ((offset (- (* slot n-word-bytes
) other-pointer-lowtag
)))
17 (if (static-symbol-p symbol
)
18 (+ nil-value
(static-symbol-offset symbol
) offset
)
19 (make-fixup symbol
:immobile-symbol offset
)))))
22 ;;; 1. Sometimes people write constructors like
23 ;;; (defun make-foo (&key a b c)
24 ;;; (let ((new-foo (really-make-foo)))
25 ;;; (when should-set-a (setf (foo-a new-foo) a))
26 ;;; (when should-set-b (setf (foo-b new-foo) b))
28 ;;; In this case, the asssignments are constructor-like. Even though
29 ;;; they look mutating, the store barrier can be omitted.
30 ;;; I think the general idea is that if a slot of a newly
31 ;;; constructed thing receives the value of an incoming
32 ;;; argument, the object in that argument can't possibly
33 ;;; be younger than the newly constructed thing.
34 ;;; 2. hash-table k/v pair should mark once only.
35 ;;; (the vector elements are certainly on the same card)
36 (defun emit-gengc-barrier (object cell-address scratch-reg
&optional value-tn-ref allocator
)
37 #-soft-card-marks
(declare (ignore object cell-address scratch-reg value-tn-ref allocator
))
40 (when (sc-is object constant immediate
)
41 (aver (symbolp (tn-value object
))))
42 (cond ((or (eq value-tn-ref t
)
43 (require-gengc-barrier-p object value-tn-ref allocator
))
44 (if cell-address
; for SIMPLE-VECTOR, the page holding the specific element index gets marked
45 (inst lea scratch-reg cell-address
)
46 ;; OBJECT could be a symbol in immobile space
47 (inst mov scratch-reg
(encode-value-if-immediate object
)))
48 (inst shr scratch-reg gencgc-card-shift
)
49 ;; gc_allocate_ptes() asserts mask to be < 32 bits, which is hugely generous.
50 (inst and
:dword scratch-reg card-index-mask
)
51 ;; I wanted to use thread-tn as the source of the store, but it isn't 256-byte-aligned
52 ;; due to presence of negatively indexed thread header slots.
53 ;; Probably word-alignment is enough, because we can just check the lowest bit,
54 ;; borrowing upon the idea from PSEUDO-ATOMIC which uses RBP-TN as the source.
55 ;; I'd like to measure to see if using a register is actually better.
56 ;; If all threads store 0, it might be easier on the CPU's store buffer.
57 ;; Otherwise, it has to remember who "wins". 0 makes it indifferent.
58 (inst mov
:byte
(ea gc-card-table-reg-tn scratch-reg
) CARD-MARKED
))
64 (load-constant nil x scratch-reg
)
67 (let ((value (encode-value-if-immediate x
)))
73 (unless (and (sc-is (tn-ref-tn value-tn-ref
) immediate
)
74 (typep (tn-value (tn-ref-tn value-tn-ref
)) '(or integer boolean
)))
76 (inst push
(encode object
))
77 (inst push
(encode (tn-ref-tn value-tn-ref
)))
78 (invoke-asm-routine 'call
'check-barrier sb-assem
::*current-vop
*))
79 (setf value-tn-ref
(tn-ref-across value-tn-ref
))
80 while value-tn-ref
)))))))
83 (defun emit-code-page-gengc-barrier (object scratch-reg
)
84 (inst mov scratch-reg object
)
85 (inst shr scratch-reg gencgc-card-shift
)
86 (inst and
:dword scratch-reg card-index-mask
)
87 (inst mov
:byte
(ea gc-card-table-reg-tn scratch-reg
) CARD-MARKED
))
89 (defun emit-store (ea value val-temp
&optional
(tag-immediate t
))
92 (let ((bits (encode-value-if-immediate value tag-immediate
)))
93 ;; Try to move imm-to-mem if BITS fits
94 (acond ((or (and (fixup-p bits
)
95 ;; immobile-object fixups must fit in 32 bits
96 (eq (fixup-flavor bits
) :immobile-symbol
)
98 (plausible-signed-imm32-operand-p bits
))
99 (inst mov
:qword ea it
))
101 (inst mov val-temp bits
)
102 (inst mov ea val-temp
)))))
104 (inst mov val-temp value
)
105 (inst mov
:qword ea val-temp
))
107 (inst mov
:qword ea value
))))
109 ;; This vop's sole purpose is to provide the implementation of value-cell-set.
110 ;; It could be removed, for x86-64 anyway.
111 (define-vop (cell-set)
112 (:args
(object :scs
(descriptor-reg))
113 (value :scs
(descriptor-reg any-reg immediate
)))
114 (:variant-vars offset lowtag
)
116 (:temporary
(:sc unsigned-reg
) val-temp
)
119 (emit-gengc-barrier object nil val-temp
(vop-nth-arg 1 vop
))
120 (let ((ea (object-slot-ea object offset lowtag
)))
121 (emit-store ea value val-temp
))))
124 (define-vop (cell-xadd)
125 (:args
(object :scs
(descriptor-reg) :to
:result
)
126 (value :scs
(any-reg) :target result
))
127 (:results
(result :scs
(any-reg) :from
(:argument
1)))
128 (:result-types tagged-num
)
129 (:variant-vars offset lowtag
)
133 (inst xadd
:lock
(object-slot-ea object offset lowtag
) result
)))
135 (define-vop (cell-xsub cell-xadd
)
137 (value :scs
(any-reg immediate
) :target result
))
139 ;; For constant delta we can avoid a mov followed by neg
140 ;; but if 'delta' is most-negative-fixnum, don't negate it.
141 ;; Decrementing by most-negative-fixnum is the same as incrementing.
144 (let ((k (tn-value value
)))
145 (inst mov result
(fixnumize (if (= k most-negative-fixnum
) k
(- k
))))))
149 (inst xadd
:lock
(object-slot-ea object offset lowtag
) result
)))
151 (define-vop (atomic-inc-symbol-global-value cell-xadd
)
152 (:translate %atomic-inc-symbol-global-value
)
153 ;; The function which this vop translates will not
154 ;; be used unless the variable is proclaimed as fixnum.
155 ;; All stores are checked in a safe policy, so this
156 ;; vop is safe because it increments a known fixnum.
158 (:arg-types
* tagged-num
)
159 (:variant symbol-value-slot other-pointer-lowtag
))
161 (define-vop (atomic-dec-symbol-global-value cell-xsub
)
162 (:translate %atomic-dec-symbol-global-value
)
164 (:arg-types
* tagged-num
)
165 (:variant symbol-value-slot other-pointer-lowtag
))
168 ((def-atomic (fun-name inherit slot
)
170 (define-vop (,(symbolicate fun-name
"/FAST") ,inherit
)
171 (:translate
,fun-name
)
173 (:arg-types
* tagged-num
)
174 (:variant
,slot list-pointer-lowtag
))
175 (define-vop (,(symbolicate fun-name
"/SAFE"))
176 (:translate
,fun-name
)
178 (:args
(cell :scs
(descriptor-reg))
179 (delta :scs
(any-reg immediate
)))
180 (:results
(result :scs
(any-reg)))
181 (:temporary
(:sc descriptor-reg
:offset rax-offset
) rax
)
182 (:temporary
(:sc any-reg
) newval
)
183 (:arg-types
* tagged-num
)
184 (:result-types tagged-num
)
187 (let ((err (generate-error-code vop
'object-not-fixnum-error rax
))
188 (const (if (sc-is delta immediate
)
189 (fixnumize ,(if (eq inherit
'cell-xsub
)
190 `(let ((x (tn-value delta
)))
191 (if (= x most-negative-fixnum
)
193 `(tn-value delta
)))))
195 (loadw rax cell
,slot list-pointer-lowtag
)
197 (inst test rax fixnum-tag-mask
)
200 (cond ((typep const
'(signed-byte 32))
201 (inst lea newval
(ea const rax
)))
203 (inst mov newval const
)
204 (inst add newval rax
)))
205 ,(if (eq inherit
'cell-xsub
)
206 `(progn (move newval rax
)
207 (inst sub newval delta
))
208 `(inst lea newval
(ea rax delta
))))
210 (object-slot-ea cell
,slot list-pointer-lowtag
)
213 (inst mov result rax
)))))))
214 (def-atomic %atomic-inc-car cell-xadd cons-car-slot
)
215 (def-atomic %atomic-inc-cdr cell-xadd cons-cdr-slot
)
216 (def-atomic %atomic-dec-car cell-xsub cons-car-slot
)
217 (def-atomic %atomic-dec-cdr cell-xsub cons-cdr-slot
))
219 ;; Atomically set a bit of an instance header word
220 (define-vop (set-instance-hashed)
221 (:args
(x :scs
(descriptor-reg)))
223 (inst or
:lock
:byte
(ea (- 1 instance-pointer-lowtag
) x
)
224 ;; Bit index is 0-based. Subtract 8 since we're using the EA
225 ;; to select byte 1 of the header word.
226 (ash 1 (- stable-hash-required-flag
8)))))
228 (defmacro compute-splat-bits
(value)
229 ;; :SAFE-DEFAULT means any unspecific value that is safely a default.
230 ;; Heap allocation uses 0 since that costs nothing.
231 ;; If the user wanted a specific value, it could have been explicitly given.
232 `(if (typep ,value
'sb-vm
:word
)
235 (:unbound
(unbound-marker-bits))
236 ((nil) (bug "Should not see SPLAT NIL"))
237 (t #+ubsan unwritten-vector-element-marker
240 ;;; This logic was formerly in ALLOCATE-VECTOR-ON-STACK.
241 ;;; Choosing amongst 3 vops gets potentially better register allocation
242 ;;; by not wasting registers in the cases that don't use them.
243 (define-vop (splat-word)
246 (:args
(vector :scs
(descriptor-reg)))
248 (:arg-types
* (:constant
(eql 1)) (:constant t
))
249 (:results
(result :scs
(descriptor-reg)))
251 (progn words
) ; don't put it in :ignore, which gets inherited
253 (ea (- (* vector-data-offset n-word-bytes
) other-pointer-lowtag
) vector
)
254 (compute-splat-bits value
))
255 (move result vector
)))
257 (define-vop (splat-small splat-word
)
258 (:arg-types
* (:constant
(integer 2 10)) (:constant t
))
259 (:temporary
(:sc complex-double-reg
) zero
)
261 (let ((bits (compute-splat-bits value
)))
263 (inst xorpd zero zero
)
265 (register-inline-constant :oword
(logior (ash bits
64) bits
)))))
266 (let ((data-addr (ea (- (* vector-data-offset n-word-bytes
) other-pointer-lowtag
)
268 (multiple-value-bind (double single
) (truncate words
2)
270 (inst movapd data-addr zero
)
271 (setf data-addr
(ea (+ (ea-disp data-addr
) (* n-word-bytes
2))
272 (ea-base data-addr
))))
273 (unless (zerop single
)
274 (inst movaps data-addr zero
))))
275 (move result vector
)))
277 (define-vop (splat-any splat-word
)
278 ;; vector has to conflict with everything so that a tagged pointer
279 ;; corresponding to RDI always exists
280 (:args
(vector :scs
(descriptor-reg) :to
(:result
0))
281 (words :scs
(unsigned-reg immediate
) :target rcx
))
283 (:arg-types
* positive-fixnum
(:constant t
))
284 (:temporary
(:sc any-reg
:offset rdi-offset
:from
(:argument
0)
285 :to
(:result
0)) rdi
)
286 (:temporary
(:sc any-reg
:offset rcx-offset
:from
(:argument
1)
287 :to
(:result
0)) rcx
)
288 (:temporary
(:sc any-reg
:offset rax-offset
:from
:eval
289 :to
(:result
0)) rax
)
290 (:results
(result :scs
(descriptor-reg)))
292 (inst lea rdi
(ea (- (* vector-data-offset n-word-bytes
) other-pointer-lowtag
)
294 (let ((bits (compute-splat-bits value
)))
295 (cond ((and (= bits
0)
296 (constant-tn-p words
)
297 (typep (tn-value words
) '(unsigned-byte 7)))
299 (inst lea
:dword rcx
(ea (tn-value words
) rax
))) ; smaller encoding
301 ;; words could be in RAX, so read it first, then zeroize
302 (inst mov rcx
(or (and (constant-tn-p words
) (tn-value words
)) words
))
303 (if (= bits
0) (zeroize rax
) (inst mov rax bits
)))))
306 (move result vector
)))
308 (dolist (name '(splat-word splat-small splat-any
))
309 ;; It wants a function, not a symbol
310 (setf (sb-c::vop-info-optimizer
(template-or-lose name
))
311 (lambda (vop) (sb-c::elide-zero-fill vop
))))