53b16bfb7127d0a5dacd72077289f26edd1f56c2
[sbcl.git] / src / compiler / x86-64 / move.lisp
1 ;;;; the x86-64 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   (:temporary (:sc unsigned-reg) temp)
74   (:effects)
75   (:affected)
76   (:generator 0
77     (if (and (sc-is x immediate)
78              (sc-is y any-reg descriptor-reg control-stack))
79         (let ((val (tn-value x)))
80           (etypecase val
81             (integer
82              (if (and (zerop val) (sc-is y any-reg descriptor-reg))
83                  (inst xor y y)
84                  (move-immediate y (fixnumize val) temp)))
85             (symbol
86              (inst mov y (+ nil-value (static-symbol-offset val))))
87             (character
88              (inst mov y (logior (ash (char-code val) n-widetag-bits)
89                                  character-widetag)))))
90         (move y x))))
91
92 (define-move-vop move :move
93   (any-reg descriptor-reg immediate)
94   (any-reg descriptor-reg))
95
96 ;;; Make MOVE the check VOP for T so that type check generation
97 ;;; doesn't think it is a hairy type. This also allows checking of a
98 ;;; few of the values in a continuation to fall out.
99 (primitive-type-vop move (:check) t)
100
101 (defun move-immediate (target val &optional tmp-tn)
102   (cond
103     ;; If target is a register, we can just mov it there directly
104     ((and (tn-p target)
105           (sc-is target signed-reg unsigned-reg descriptor-reg any-reg))
106      (inst mov target val))
107     ;; Likewise if the value is small enough.
108     ((typep val '(signed-byte 31))
109      (inst mov target val))
110     ;; Otherwise go through the temporary register
111     (tmp-tn
112      (inst mov tmp-tn val)
113      (inst mov target tmp-tn))
114     (t
115      (error "~A is not a register, no temporary given, and immediate ~A too large" target val))))
116
117 ;;; The MOVE-ARG VOP is used for moving descriptor values into
118 ;;; another frame for argument or known value passing.
119 ;;;
120 ;;; Note: It is not going to be possible to move a constant directly
121 ;;; to another frame, except if the destination is a register and in
122 ;;; this case the loading works out.
123 (define-vop (move-arg)
124   (:args (x :scs (any-reg descriptor-reg immediate) :target y
125             :load-if (not (and (sc-is y any-reg descriptor-reg)
126                                (sc-is x control-stack))))
127          (fp :scs (any-reg)
128              :load-if (not (sc-is y any-reg descriptor-reg))))
129   (:results (y))
130   (:generator 0
131     (sc-case y
132       ((any-reg descriptor-reg)
133        (if (sc-is x immediate)
134            (let ((val (tn-value x)))
135              (etypecase val
136                ((integer 0 0)
137                 (inst xor y y))
138                ((or (signed-byte 29) (unsigned-byte 29))
139                 (inst mov y (fixnumize val)))
140                (integer
141                 (move-immediate y (fixnumize val)))
142                (symbol
143                 (load-symbol y val))
144                (character
145                 (inst mov y (logior (ash (char-code val) n-widetag-bits)
146                                     character-widetag)))))
147            (move y x)))
148       ((control-stack)
149        (if (sc-is x immediate)
150            (let ((val (tn-value x)))
151              (if (= (tn-offset fp) esp-offset)
152                  ;; C-call
153                  (etypecase val
154                    (integer
155                     (storew (fixnumize val) fp (tn-offset y)))
156                    (symbol
157                     (storew (+ nil-value (static-symbol-offset val))
158                             fp (tn-offset y)))
159                    (character
160                     (storew (logior (ash (char-code val) n-widetag-bits)
161                                     character-widetag)
162                             fp (tn-offset y))))
163                ;; Lisp stack
164                (etypecase val
165                  (integer
166                   (storew (fixnumize val) fp (- (1+ (tn-offset y)))))
167                  (symbol
168                   (storew (+ nil-value (static-symbol-offset val))
169                           fp (- (1+ (tn-offset y)))))
170                  (character
171                   (storew (logior (ash (char-code val) n-widetag-bits)
172                                   character-widetag)
173                           fp (- (1+ (tn-offset y))))))))
174          (if (= (tn-offset fp) esp-offset)
175              ;; C-call
176              (storew x fp (tn-offset y))
177            ;; Lisp stack
178            (storew x fp (- (1+ (tn-offset y))))))))))
179
180 (define-move-vop move-arg :move-arg
181   (any-reg descriptor-reg)
182   (any-reg descriptor-reg))
183 \f
184 ;;;; ILLEGAL-MOVE
185
186 ;;; This VOP exists just to begin the lifetime of a TN that couldn't
187 ;;; be written legally due to a type error. An error is signalled
188 ;;; before this VOP is so we don't need to do anything (not that there
189 ;;; would be anything sensible to do anyway.)
190 (define-vop (illegal-move)
191   (:args (x) (type))
192   (:results (y))
193   (:ignore y)
194   (:vop-var vop)
195   (:save-p :compute-only)
196   (:generator 666
197     (error-call vop object-not-type-error x type)))
198 \f
199 ;;;; moves and coercions
200
201 ;;; These MOVE-TO-WORD VOPs move a tagged integer to a raw full-word
202 ;;; representation. Similarly, the MOVE-FROM-WORD VOPs converts a raw
203 ;;; integer to a tagged bignum or fixnum.
204
205 ;;; Arg is a fixnum, so just shift it. We need a type restriction
206 ;;; because some possible arg SCs (control-stack) overlap with
207 ;;; possible bignum arg SCs.
208 (define-vop (move-to-word/fixnum)
209   (:args (x :scs (any-reg descriptor-reg) :target y
210             :load-if (not (location= x y))))
211   (:results (y :scs (signed-reg unsigned-reg)
212                :load-if (not (location= x y))))
213   (:arg-types tagged-num)
214   (:note "fixnum untagging")
215   (:generator 1
216     (move y x)
217     (inst sar y  (1- n-lowtag-bits))))
218 (define-move-vop move-to-word/fixnum :move
219   (any-reg descriptor-reg) (signed-reg unsigned-reg))
220
221 ;;; Arg is a non-immediate constant, load it.
222 (define-vop (move-to-word-c)
223   (:args (x :scs (constant)))
224   (:results (y :scs (signed-reg unsigned-reg)))
225   (:note "constant load")
226   (:generator 1
227     (inst mov y (tn-value x))))
228 (define-move-vop move-to-word-c :move
229   (constant) (signed-reg unsigned-reg))
230
231
232 ;;; Arg is a fixnum or bignum, figure out which and load if necessary.
233 (define-vop (move-to-word/integer)
234   (:args (x :scs (descriptor-reg) :target eax))
235   (:results (y :scs (signed-reg unsigned-reg)))
236   (:note "integer to untagged word coercion")
237   (:temporary (:sc unsigned-reg :offset eax-offset
238                    :from (:argument 0) :to (:result 0) :target y) eax)
239   (:generator 4
240     (move eax x)
241     (inst test al-tn 7)                 ; a symbolic constant for this
242     (inst jmp :z FIXNUM)                ; would be nice
243     (loadw y eax bignum-digits-offset other-pointer-lowtag)
244     (inst jmp DONE)
245     FIXNUM
246     (inst sar eax (1- n-lowtag-bits))
247     (move y eax)
248     DONE))
249 (define-move-vop move-to-word/integer :move
250   (descriptor-reg) (signed-reg unsigned-reg))
251
252
253 ;;; Result is a fixnum, so we can just shift. We need the result type
254 ;;; restriction because of the control-stack ambiguity noted above.
255 (define-vop (move-from-word/fixnum)
256   (:args (x :scs (signed-reg unsigned-reg) :target y
257             :load-if (not (location= x y))))
258   (:results (y :scs (any-reg descriptor-reg)
259                :load-if (not (location= x y))))
260   (:result-types tagged-num)
261   (:note "fixnum tagging")
262   (:generator 1
263     (cond ((and (sc-is x signed-reg unsigned-reg)
264                 (not (location= x y)))
265            ;; Uses 7 bytes, but faster on the Pentium
266            (inst lea y (make-ea :qword :index x :scale 8)))
267           (t
268            ;; Uses: If x is a reg 2 + 3; if x = y uses only 3 bytes
269            (move y x)
270            (inst shl y (1- n-lowtag-bits))))))
271 (define-move-vop move-from-word/fixnum :move
272   (signed-reg unsigned-reg) (any-reg descriptor-reg))
273
274 ;;; Result may be a bignum, so we have to check. Use a worst-case cost
275 ;;; to make sure people know they may be number consing.
276 (define-vop (move-from-signed)
277   (:args (x :scs (signed-reg unsigned-reg) :to :result))
278   (:results (y :scs (any-reg descriptor-reg) :from :argument))
279   (:note "signed word to integer coercion")
280   (:node-var node)
281   (:generator 20
282      (aver (not (location= x y)))
283      (let ((bignum (gen-label))
284            (done (gen-label)))
285        (inst mov y x)
286        ;; We can't do the overflow check with SHL Y, 3, since the
287        ;; state of the overflow flag is only reliably set when
288        ;; shifting by 1. There used to be code here for doing "shift
289        ;; by one, check whether it overflowed" three times. But on all
290        ;; x86-64 processors IMUL is a reasonably fast instruction, so
291        ;; we can just do a straight multiply instead of trying to
292        ;; optimize it to a shift. This is both faster and smaller.
293        ;; -- JES, 2006-07-08
294        (inst imul y 8)
295        (inst jmp :o bignum)
296        (emit-label done)
297
298        (assemble (*elsewhere*)
299           (emit-label bignum)
300           (with-fixed-allocation
301               (y bignum-widetag (+ bignum-digits-offset 1) node)
302             (storew x y bignum-digits-offset other-pointer-lowtag))
303           (inst jmp done)))))
304 (define-move-vop move-from-signed :move
305   (signed-reg) (descriptor-reg))
306
307 ;;; Check for fixnum, and possibly allocate one or two word bignum
308 ;;; result. Use a worst-case cost to make sure people know they may be
309 ;;; number consing.
310
311 (define-vop (move-from-unsigned)
312   (:args (x :scs (signed-reg unsigned-reg) :to :save))
313   (:temporary (:sc unsigned-reg) alloc)
314   (:results (y :scs (any-reg descriptor-reg)))
315   (:node-var node)
316   (:note "unsigned word to integer coercion")
317   (:generator 20
318     (aver (not (location= x y)))
319     (aver (not (location= x alloc)))
320     (aver (not (location= y alloc)))
321     (let ((bignum (gen-label))
322           (done (gen-label))
323           (one-word-bignum (gen-label))
324           (L1 (gen-label)))
325       (inst bsr y x)                    ;find msb
326       (inst cmov :z y x)
327       (inst cmp y 60)
328       (inst jmp :ae bignum)
329       (inst lea y (make-ea :qword :index x :scale 8))
330       (emit-label done)
331       (assemble (*elsewhere*)
332          (emit-label bignum)
333          ;; Note: As on the mips port, space for a two word bignum is
334          ;; always allocated and the header size is set to either one
335          ;; or two words as appropriate.
336          (inst cmp y 63)
337          (inst jmp :l one-word-bignum)
338          ;; two word bignum
339          (inst mov y (logior (ash (1- (+ bignum-digits-offset 2))
340                                   n-widetag-bits)
341                              bignum-widetag))
342          (inst jmp L1)
343          (emit-label one-word-bignum)
344          (inst mov y (logior (ash (1- (+ bignum-digits-offset 1))
345                                   n-widetag-bits)
346                              bignum-widetag))
347          (emit-label L1)
348          (pseudo-atomic
349           (allocation alloc (pad-data-block (+ bignum-digits-offset 2)) node)
350           (storew y alloc)
351           (inst lea y (make-ea :byte :base alloc :disp other-pointer-lowtag))
352           (storew x y bignum-digits-offset other-pointer-lowtag))
353          (inst jmp done)))))
354 (define-move-vop move-from-unsigned :move
355   (unsigned-reg) (descriptor-reg))
356
357 ;;; Move untagged numbers.
358 (define-vop (word-move)
359   (:args (x :scs (signed-reg unsigned-reg) :target y
360             :load-if (not (location= x y))))
361   (:results (y :scs (signed-reg unsigned-reg)
362                :load-if
363                (not (or (location= x y)
364                         (and (sc-is x signed-reg unsigned-reg)
365                              (sc-is y signed-stack unsigned-stack))))))
366   (:effects)
367   (:affected)
368   (:note "word integer move")
369   (:generator 0
370     (move y x)))
371 (define-move-vop word-move :move
372   (signed-reg unsigned-reg) (signed-reg unsigned-reg))
373
374 ;;; Move untagged number arguments/return-values.
375 (define-vop (move-word-arg)
376   (:args (x :scs (signed-reg unsigned-reg) :target y)
377          (fp :scs (any-reg) :load-if (not (sc-is y sap-reg))))
378   (:results (y))
379   (:note "word integer argument move")
380   (:generator 0
381     (sc-case y
382       ((signed-reg unsigned-reg)
383        (move y x))
384       ((signed-stack unsigned-stack)
385        (if (= (tn-offset fp) esp-offset)
386            (storew x fp (tn-offset y))  ; c-call
387            (storew x fp (- (1+ (tn-offset y)))))))))
388 (define-move-vop move-word-arg :move-arg
389   (descriptor-reg any-reg signed-reg unsigned-reg) (signed-reg unsigned-reg))
390
391 ;;; Use standard MOVE-ARG and coercion to move an untagged number
392 ;;; to a descriptor passing location.
393 (define-move-vop move-arg :move-arg
394   (signed-reg unsigned-reg) (any-reg descriptor-reg))