0.9.15.22: check for error, do not aver, do not collect $200
[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        ;; We can't do the overflow check with SHL Y, 3, since the
286        ;; state of the overflow flag is only reliably set when
287        ;; shifting by 1. There used to be code here for doing "shift
288        ;; by one, check whether it overflowed" three times. But on all
289        ;; x86-64 processors IMUL is a reasonably fast instruction, so
290        ;; we can just do a straight multiply instead of trying to
291        ;; optimize it to a shift. This is both faster and smaller.
292        ;; -- JES, 2006-07-08
293        (inst imul y x (ash 1 n-fixnum-tag-bits))
294        (inst jmp :o bignum)
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
310 (define-vop (move-from-unsigned)
311   (:args (x :scs (signed-reg unsigned-reg) :to :save))
312   (:temporary (:sc unsigned-reg) alloc)
313   (:results (y :scs (any-reg descriptor-reg)))
314   (:node-var node)
315   (:note "unsigned word to integer coercion")
316   (:generator 20
317     (aver (not (location= x y)))
318     (aver (not (location= x alloc)))
319     (aver (not (location= y alloc)))
320     (let ((bignum (gen-label))
321           (done (gen-label))
322           (one-word-bignum (gen-label))
323           (L1 (gen-label)))
324       (inst bsr y x)                    ;find msb
325       (inst cmov :z y x)
326       (inst cmp y 60)
327       (inst jmp :ae bignum)
328       (inst lea y (make-ea :qword :index x :scale 8))
329       (emit-label done)
330       (assemble (*elsewhere*)
331          (emit-label bignum)
332          ;; Note: As on the mips port, space for a two word bignum is
333          ;; always allocated and the header size is set to either one
334          ;; or two words as appropriate.
335          (inst cmp y 63)
336          (inst jmp :l one-word-bignum)
337          ;; two word bignum
338          (inst mov y (logior (ash (1- (+ bignum-digits-offset 2))
339                                   n-widetag-bits)
340                              bignum-widetag))
341          (inst jmp L1)
342          (emit-label one-word-bignum)
343          (inst mov y (logior (ash (1- (+ bignum-digits-offset 1))
344                                   n-widetag-bits)
345                              bignum-widetag))
346          (emit-label L1)
347          (pseudo-atomic
348           (allocation alloc (pad-data-block (+ bignum-digits-offset 2)) node)
349           (storew y alloc)
350           (inst lea y (make-ea :byte :base alloc :disp other-pointer-lowtag))
351           (storew x y bignum-digits-offset other-pointer-lowtag))
352          (inst jmp done)))))
353 (define-move-vop move-from-unsigned :move
354   (unsigned-reg) (descriptor-reg))
355
356 ;;; Move untagged numbers.
357 (define-vop (word-move)
358   (:args (x :scs (signed-reg unsigned-reg) :target y
359             :load-if (not (location= x y))))
360   (:results (y :scs (signed-reg unsigned-reg)
361                :load-if
362                (not (or (location= x y)
363                         (and (sc-is x signed-reg unsigned-reg)
364                              (sc-is y signed-stack unsigned-stack))))))
365   (:effects)
366   (:affected)
367   (:note "word integer move")
368   (:generator 0
369     (move y x)))
370 (define-move-vop word-move :move
371   (signed-reg unsigned-reg) (signed-reg unsigned-reg))
372
373 ;;; Move untagged number arguments/return-values.
374 (define-vop (move-word-arg)
375   (:args (x :scs (signed-reg unsigned-reg) :target y)
376          (fp :scs (any-reg) :load-if (not (sc-is y sap-reg))))
377   (:results (y))
378   (:note "word integer argument move")
379   (:generator 0
380     (sc-case y
381       ((signed-reg unsigned-reg)
382        (move y x))
383       ((signed-stack unsigned-stack)
384        (if (= (tn-offset fp) esp-offset)
385            (storew x fp (tn-offset y))  ; c-call
386            (storew x fp (- (1+ (tn-offset y)))))))))
387 (define-move-vop move-word-arg :move-arg
388   (descriptor-reg any-reg signed-reg unsigned-reg) (signed-reg unsigned-reg))
389
390 ;;; Use standard MOVE-ARG and coercion to move an untagged number
391 ;;; to a descriptor passing location.
392 (define-move-vop move-arg :move-arg
393   (signed-reg unsigned-reg) (any-reg descriptor-reg))