1.0.4.63: Don't zeroize dynamic-extent simple-unboxed-arrays on x86 and x86-64
[sbcl.git] / src / compiler / x86 / move.lisp
1 ;;;; the x86 VM definition of operand loading/saving and the MOVE vop
2
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.
11
12 (in-package "SB!VM")
13
14 (define-move-fun (load-immediate 1) (vop x y)
15   ((immediate)
16    (any-reg descriptor-reg))
17   (let ((val (tn-value x)))
18     (etypecase val
19       (integer
20        (if (zerop val)
21            (inst xor y y)
22          (inst mov y (fixnumize val))))
23       (symbol
24        (load-symbol y val))
25       (character
26        (inst mov y (logior (ash (char-code val) n-widetag-bits)
27                            character-widetag))))))
28
29 (define-move-fun (load-number 1) (vop x y)
30   ((immediate) (signed-reg unsigned-reg))
31   (let ((val (tn-value x)))
32     (if (zerop val)
33         (inst xor y y)
34         (inst mov y val))))
35
36 (define-move-fun (load-character 1) (vop x y)
37   ((immediate) (character-reg))
38   (inst mov y (char-code (tn-value x))))
39
40 (define-move-fun (load-system-area-pointer 1) (vop x y)
41   ((immediate) (sap-reg))
42   (inst mov y (sap-int (tn-value x))))
43
44 (define-move-fun (load-constant 5) (vop x y)
45   ((constant) (descriptor-reg any-reg))
46   (inst mov y x))
47
48 (define-move-fun (load-stack 5) (vop x y)
49   ((control-stack) (any-reg descriptor-reg)
50    (character-stack) (character-reg)
51    (sap-stack) (sap-reg)
52    (signed-stack) (signed-reg)
53    (unsigned-stack) (unsigned-reg))
54   (inst mov y x))
55
56 (define-move-fun (store-stack 5) (vop x y)
57   ((any-reg descriptor-reg) (control-stack)
58    (character-reg) (character-stack)
59    (sap-reg) (sap-stack)
60    (signed-reg) (signed-stack)
61    (unsigned-reg) (unsigned-stack))
62   (inst mov y x))
63 \f
64 ;;;; the MOVE VOP
65 (define-vop (move)
66   (:args (x :scs (any-reg descriptor-reg immediate) :target y
67             :load-if (not (location= x y))))
68   (:results (y :scs (any-reg descriptor-reg)
69                :load-if
70                (not (or (location= x y)
71                         (and (sc-is x any-reg descriptor-reg immediate)
72                              (sc-is y control-stack))))))
73   (:effects)
74   (:affected)
75   (:generator 0
76     (if (and (sc-is x immediate)
77              (sc-is y any-reg descriptor-reg control-stack))
78         (let ((val (tn-value x)))
79           (etypecase val
80             (integer
81              (if (and (zerop val) (sc-is y any-reg descriptor-reg))
82                  (inst xor y y)
83                (inst mov y (fixnumize val))))
84             (symbol
85              (inst mov y (+ nil-value (static-symbol-offset val))))
86             (character
87              (inst mov y (logior (ash (char-code val) n-widetag-bits)
88                                  character-widetag)))))
89       (move y x))))
90
91 (define-move-vop move :move
92   (any-reg descriptor-reg immediate)
93   (any-reg descriptor-reg))
94
95 ;;; Make MOVE the check VOP for T so that type check generation
96 ;;; doesn't think it is a hairy type. This also allows checking of a
97 ;;; few of the values in a continuation to fall out.
98 (primitive-type-vop move (:check) t)
99
100 ;;; The MOVE-ARG VOP is used for moving descriptor values into
101 ;;; another frame for argument or known value passing.
102 ;;;
103 ;;; Note: It is not going to be possible to move a constant directly
104 ;;; to another frame, except if the destination is a register and in
105 ;;; this case the loading works out.
106 (define-vop (move-arg)
107   (:args (x :scs (any-reg descriptor-reg immediate) :target y
108             :load-if (not (and (sc-is y any-reg descriptor-reg)
109                                (sc-is x control-stack))))
110          (fp :scs (any-reg)
111              :load-if (not (sc-is y any-reg descriptor-reg))))
112   (:results (y))
113   (:generator 0
114     (sc-case y
115       ((any-reg descriptor-reg)
116        (if (sc-is x immediate)
117            (let ((val (tn-value x)))
118              (etypecase val
119               (integer
120                (if (zerop val)
121                    (inst xor y y)
122                  (inst mov y (fixnumize val))))
123               (symbol
124                (load-symbol y val))
125               (character
126                (inst mov y (logior (ash (char-code val) n-widetag-bits)
127                                    character-widetag)))))
128          (move y x)))
129       ((control-stack)
130        (let ((frame-offset (if (= (tn-offset fp) esp-offset)
131                                ;; C-call
132                                (tn-offset y)
133                                ;; Lisp stack
134                                (frame-word-offset (tn-offset y)))))
135          (if (sc-is x immediate)
136              (let ((val (tn-value x)))
137                (etypecase val
138                  (integer
139                   (storew (fixnumize val) fp frame-offset))
140                  (symbol
141                   (storew (+ nil-value (static-symbol-offset val))
142                           fp frame-offset))
143                  (character
144                   (storew (logior (ash (char-code val) n-widetag-bits)
145                                   character-widetag)
146                           fp frame-offset))))
147              (storew x fp frame-offset)))))))
148
149 (define-move-vop move-arg :move-arg
150   (any-reg descriptor-reg)
151   (any-reg descriptor-reg))
152 \f
153 ;;;; ILLEGAL-MOVE
154
155 ;;; This VOP exists just to begin the lifetime of a TN that couldn't
156 ;;; be written legally due to a type error. An error is signalled
157 ;;; before this VOP is so we don't need to do anything (not that there
158 ;;; would be anything sensible to do anyway.)
159 (define-vop (illegal-move)
160   (:args (x) (type))
161   (:results (y))
162   (:ignore y)
163   (:vop-var vop)
164   (:save-p :compute-only)
165   (:generator 666
166     (error-call vop object-not-type-error x type)))
167 \f
168 ;;;; moves and coercions
169
170 ;;; These MOVE-TO-WORD VOPs move a tagged integer to a raw full-word
171 ;;; representation. Similarly, the MOVE-FROM-WORD VOPs converts a raw
172 ;;; integer to a tagged bignum or fixnum.
173
174 ;;; Arg is a fixnum, so just shift it. We need a type restriction
175 ;;; because some possible arg SCs (control-stack) overlap with
176 ;;; possible bignum arg SCs.
177 (define-vop (move-to-word/fixnum)
178   (:args (x :scs (any-reg descriptor-reg) :target y
179             :load-if (not (location= x y))))
180   (:results (y :scs (signed-reg unsigned-reg)
181                :load-if (not (location= x y))))
182   (:arg-types tagged-num)
183   (:note "fixnum untagging")
184   (:generator 1
185     (move y x)
186     (inst sar y 2)))
187 (define-move-vop move-to-word/fixnum :move
188   (any-reg descriptor-reg) (signed-reg unsigned-reg))
189
190 ;;; Arg is a non-immediate constant, load it.
191 (define-vop (move-to-word-c)
192   (:args (x :scs (constant)))
193   (:results (y :scs (signed-reg unsigned-reg)))
194   (:note "constant load")
195   (:generator 1
196     (inst mov y (tn-value x))))
197 (define-move-vop move-to-word-c :move
198   (constant) (signed-reg unsigned-reg))
199
200
201 ;;; Arg is a fixnum or bignum, figure out which and load if necessary.
202 (define-vop (move-to-word/integer)
203   (:args (x :scs (descriptor-reg) :target eax))
204   (:results (y :scs (signed-reg unsigned-reg)))
205   (:note "integer to untagged word coercion")
206   (:temporary (:sc unsigned-reg :offset eax-offset
207                    :from (:argument 0) :to (:result 0) :target y) eax)
208   (:generator 4
209     (move eax x)
210     (inst test al-tn 3)
211     (inst jmp :z fixnum)
212     (loadw y eax bignum-digits-offset other-pointer-lowtag)
213     (inst jmp done)
214     FIXNUM
215     (inst sar eax 2)
216     (move y eax)
217     DONE))
218 (define-move-vop move-to-word/integer :move
219   (descriptor-reg) (signed-reg unsigned-reg))
220
221
222 ;;; Result is a fixnum, so we can just shift. We need the result type
223 ;;; restriction because of the control-stack ambiguity noted above.
224 (define-vop (move-from-word/fixnum)
225   (:args (x :scs (signed-reg unsigned-reg) :target y
226             :load-if (not (location= x y))))
227   (:results (y :scs (any-reg descriptor-reg)
228                :load-if (not (location= x y))))
229   (:result-types tagged-num)
230   (:note "fixnum tagging")
231   (:generator 1
232     (cond ((and (sc-is x signed-reg unsigned-reg)
233                 (not (location= x y)))
234            ;; Uses 7 bytes, but faster on the Pentium
235            (inst lea y (make-ea :dword :index x :scale 4)))
236           (t
237            ;; Uses: If x is a reg 2 + 3; if x = y uses only 3 bytes
238            (move y x)
239            (inst shl y 2)))))
240 (define-move-vop move-from-word/fixnum :move
241   (signed-reg unsigned-reg) (any-reg descriptor-reg))
242
243 ;;; Result may be a bignum, so we have to check. Use a worst-case cost
244 ;;; to make sure people know they may be number consing.
245 ;;;
246 ;;; KLUDGE: I assume this is suppressed in favor of the "faster inline
247 ;;; version" below. (See also mysterious comment "we don't want a VOP
248 ;;; on this one" on DEFINE-ASSEMBLY-ROUTINE (MOVE-FROM-SIGNED) in
249 ;;; "src/assembly/x86/alloc.lisp".) -- WHN 19990916
250 #+nil
251 (define-vop (move-from-signed)
252   (:args (x :scs (signed-reg unsigned-reg) :target eax))
253   (:temporary (:sc unsigned-reg :offset eax-offset :from (:argument 0)) eax)
254   (:temporary (:sc unsigned-reg :offset ebx-offset :to (:result 0) :target y)
255               ebx)
256   (:temporary (:sc unsigned-reg :offset ecx-offset
257                    :from (:argument 0) :to (:result 0)) ecx)
258   (:ignore ecx)
259   (:results (y :scs (any-reg descriptor-reg)))
260   (:note "signed word to integer coercion")
261   (:generator 20
262     (move eax x)
263     (inst call (make-fixup 'move-from-signed :assembly-routine))
264     (move y ebx)))
265 ;;; Faster inline version,
266 ;;; KLUDGE: Do we really want the faster inline version? It's sorta big.
267 ;;; It is nice that it doesn't use any temporaries, though. -- WHN 19990916
268 (define-vop (move-from-signed)
269   (:args (x :scs (signed-reg unsigned-reg) :to :result))
270   (:results (y :scs (any-reg descriptor-reg) :from :argument))
271   (:note "signed word to integer coercion")
272   (:node-var node)
273   (:generator 20
274      (aver (not (location= x y)))
275      (let ((bignum (gen-label))
276            (done (gen-label)))
277        (inst mov y x)
278        (inst shl y 1)
279        (inst jmp :o bignum)
280        (inst shl y 1)
281        (inst jmp :o bignum)
282        (emit-label done)
283        ;; KLUDGE: The sequence above leaves a DESCRIPTOR-REG Y in a
284        ;; non-descriptor state for a while. Does that matter? Does it
285        ;; matter in GENGC but not in GENCGC? Is this written down
286        ;; anywhere?
287        ;;   -- WHN 19990916
288        ;;
289        ;; Also, the sequence above seems rather twisty. Why not something
290        ;; more obvious along the lines of
291        ;;   inst move y x
292        ;;   inst tst x #xc0000000
293        ;;   inst jmp :nz bignum
294        ;;   inst shl y 2
295        ;;   emit-label done
296
297        (assemble (*elsewhere*)
298           (emit-label bignum)
299           (with-fixed-allocation
300               (y bignum-widetag (+ bignum-digits-offset 1) node)
301             (storew x y bignum-digits-offset other-pointer-lowtag))
302           (inst jmp done)))))
303 (define-move-vop move-from-signed :move
304   (signed-reg) (descriptor-reg))
305
306 ;;; Check for fixnum, and possibly allocate one or two word bignum
307 ;;; result. Use a worst-case cost to make sure people know they may be
308 ;;; number consing.
309 #+nil
310 (define-vop (move-from-unsigned)
311   (:args (x :scs (signed-reg unsigned-reg) :target eax))
312   (:temporary (:sc unsigned-reg :offset eax-offset :from (:argument 0)) eax)
313   (:temporary (:sc unsigned-reg :offset ebx-offset :to (:result 0) :target y)
314               ebx)
315   (:temporary (:sc unsigned-reg :offset ecx-offset
316                    :from (:argument 0) :to (:result 0)) ecx)
317   (:ignore ecx)
318   (:results (y :scs (any-reg descriptor-reg)))
319   (:note "unsigned word to integer coercion")
320   (:generator 20
321     (move eax x)
322     (inst call (make-fixup 'move-from-unsigned :assembly-routine))
323     (move y ebx)))
324 ;;; Faster inline version.
325 ;;; KLUDGE: Do we really want the faster inline version? It seems awfully big..
326 ;;; If we really want speed, most likely it's only important in the non-consing
327 ;;; case, so how about about making the *ELSEWHERE* stuff into a subroutine? --
328 ;;; WHN 19990916
329 (define-vop (move-from-unsigned)
330   (:args (x :scs (signed-reg unsigned-reg) :to :save))
331   (:temporary (:sc unsigned-reg) alloc)
332   (:results (y :scs (any-reg descriptor-reg)))
333   (:node-var node)
334   (:note "unsigned word to integer coercion")
335   (:generator 20
336     (aver (not (location= x y)))
337     (aver (not (location= x alloc)))
338     (aver (not (location= y alloc)))
339     (let ((bignum (gen-label))
340           (done (gen-label))
341           (one-word-bignum (gen-label))
342           (L1 (gen-label)))
343       (inst test x #xe0000000)
344       (inst jmp :nz bignum)
345       ;; Fixnum.
346       (inst lea y (make-ea :dword :index x :scale 4)) ; Faster but bigger.
347       ;(inst mov y x)
348       ;(inst shl y 2)
349       (emit-label done)
350
351       (assemble (*elsewhere*)
352          (emit-label bignum)
353          ;; Note: As on the mips port, space for a two word bignum is
354          ;; always allocated and the header size is set to either one
355          ;; or two words as appropriate.
356          (inst jmp :ns one-word-bignum)
357          ;; two word bignum
358          (inst mov y (logior (ash (1- (+ bignum-digits-offset 2))
359                                   n-widetag-bits)
360                              bignum-widetag))
361          (inst jmp L1)
362          (emit-label one-word-bignum)
363          (inst mov y (logior (ash (1- (+ bignum-digits-offset 1))
364                                   n-widetag-bits)
365                              bignum-widetag))
366          (emit-label L1)
367          (pseudo-atomic
368           (allocation alloc (pad-data-block (+ bignum-digits-offset 2)) node)
369           (storew y alloc)
370           (inst lea y (make-ea :byte :base alloc :disp other-pointer-lowtag))
371           (storew x y bignum-digits-offset other-pointer-lowtag))
372          (inst jmp done)))))
373 (define-move-vop move-from-unsigned :move
374   (unsigned-reg) (descriptor-reg))
375
376 ;;; Move untagged numbers.
377 (define-vop (word-move)
378   (:args (x :scs (signed-reg unsigned-reg) :target y
379             :load-if (not (location= x y))))
380   (:results (y :scs (signed-reg unsigned-reg)
381                :load-if
382                (not (or (location= x y)
383                         (and (sc-is x signed-reg unsigned-reg)
384                              (sc-is y signed-stack unsigned-stack))))))
385   (:effects)
386   (:affected)
387   (:note "word integer move")
388   (:generator 0
389     (move y x)))
390 (define-move-vop word-move :move
391   (signed-reg unsigned-reg) (signed-reg unsigned-reg))
392
393 ;;; Move untagged number arguments/return-values.
394 (define-vop (move-word-arg)
395   (:args (x :scs (signed-reg unsigned-reg) :target y)
396          (fp :scs (any-reg) :load-if (not (sc-is y sap-reg))))
397   (:results (y))
398   (:note "word integer argument move")
399   (:generator 0
400     (sc-case y
401       ((signed-reg unsigned-reg)
402        (move y x))
403       ((signed-stack unsigned-stack)
404        (if (= (tn-offset fp) esp-offset)
405            (storew x fp (tn-offset y))  ; c-call
406            (storew x fp (frame-word-offset (tn-offset y))))))))
407 (define-move-vop move-word-arg :move-arg
408   (descriptor-reg any-reg signed-reg unsigned-reg) (signed-reg unsigned-reg))
409
410 ;;; Use standard MOVE-ARG and coercion to move an untagged number
411 ;;; to a descriptor passing location.
412 (define-move-vop move-arg :move-arg
413   (signed-reg unsigned-reg) (any-reg descriptor-reg))