Stop CMUCL from influencing how POSITION is used.
[sbcl.git] / src / compiler / x86-64 / memory.lisp
blob8d6603e70cd7cb8f3277d59fda66cc9186341611
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
5 ;;;; more information.
6 ;;;;
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.
13 (in-package "SB-VM")
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)))))
21 ;;; TODOs:
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))
27 ;;; ...
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))
38 #+soft-card-marks
39 (progn
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))
59 #+debug-gc-barriers
61 (flet ((encode (x)
62 (sc-case x
63 (constant
64 (load-constant nil x scratch-reg)
65 scratch-reg)
67 (let ((value (encode-value-if-immediate x)))
68 (if (integerp value)
69 (constantize value)
70 value))))))
71 (when value-tn-ref
72 (loop do
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)))
75 (inst push 1)
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)))))))
82 #-soft-card-marks
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))
90 (sc-case value
91 (immediate
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)
97 bits)
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)))))
103 (constant
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)
115 (:policy :fast-safe)
116 (:temporary (:sc unsigned-reg) val-temp)
117 (:vop-var vop)
118 (:generator 4
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))))
123 ;;; X86 special
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)
130 (:policy :fast-safe)
131 (:generator 4
132 (move result value)
133 (inst xadd :lock (object-slot-ea object offset lowtag) result)))
135 (define-vop (cell-xsub cell-xadd)
136 (:args (object)
137 (value :scs (any-reg immediate) :target result))
138 (:generator 5
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.
142 (sc-case value
143 (immediate
144 (let ((k (tn-value value)))
145 (inst mov result (fixnumize (if (= k most-negative-fixnum) k (- k))))))
147 (move result value)
148 (inst neg result)))
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.
157 (:policy :fast-safe)
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)
163 (:policy :fast-safe)
164 (:arg-types * tagged-num)
165 (:variant symbol-value-slot other-pointer-lowtag))
167 (macrolet
168 ((def-atomic (fun-name inherit slot)
169 `(progn
170 (define-vop (,(symbolicate fun-name "/FAST") ,inherit)
171 (:translate ,fun-name)
172 (:policy :fast)
173 (:arg-types * tagged-num)
174 (:variant ,slot list-pointer-lowtag))
175 (define-vop (,(symbolicate fun-name "/SAFE"))
176 (:translate ,fun-name)
177 (:policy :fast-safe)
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)
185 (:vop-var vop)
186 (:generator 10
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)
192 x (- x)))
193 `(tn-value delta)))))
194 (retry (gen-label)))
195 (loadw rax cell ,slot list-pointer-lowtag)
196 (emit-label retry)
197 (inst test rax fixnum-tag-mask)
198 (inst jmp :nz err)
199 (if const
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))))
209 (inst cmpxchg :lock
210 (object-slot-ea cell ,slot list-pointer-lowtag)
211 newval)
212 (inst jmp :ne retry)
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)))
222 (:generator 1
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)
233 ,value
234 (case ,value
235 (:unbound (unbound-marker-bits))
236 ((nil) (bug "Should not see SPLAT NIL"))
237 (t #+ubsan unwritten-vector-element-marker
238 #-ubsan 0))))
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)
244 (:policy :fast-safe)
245 (:translate splat)
246 (:args (vector :scs (descriptor-reg)))
247 (:info words value)
248 (:arg-types * (:constant (eql 1)) (:constant t))
249 (:results (result :scs (descriptor-reg)))
250 (:generator 1
251 (progn words) ; don't put it in :ignore, which gets inherited
252 (inst mov :qword
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)
260 (:generator 5
261 (let ((bits (compute-splat-bits value)))
262 (if (= bits 0)
263 (inst xorpd zero zero)
264 (inst movdqa 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)
267 vector)))
268 (multiple-value-bind (double single) (truncate words 2)
269 (dotimes (i double)
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))
282 (:info value)
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)))
291 (:generator 10
292 (inst lea rdi (ea (- (* vector-data-offset n-word-bytes) other-pointer-lowtag)
293 vector))
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)))
298 (zeroize rax)
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)))))
304 (inst rep)
305 (inst stos :qword)
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))))