1.0.27.13: more RET on x86oids
[sbcl.git] / src / assembly / x86 / arith.lisp
1 ;;;; simple cases for generic arithmetic
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 \f
14 ;;;; addition, subtraction, and multiplication
15
16 (macrolet ((define-generic-arith-routine ((fun cost) &body body)
17              `(define-assembly-routine (,(symbolicate "GENERIC-" fun)
18                                         (:cost ,cost)
19                                         (:return-style :full-call)
20                                         (:translate ,fun)
21                                         (:policy :safe)
22                                         (:save-p t))
23                 ((:arg x (descriptor-reg any-reg) edx-offset)
24                  (:arg y (descriptor-reg any-reg)
25                        ;; this seems wrong esi-offset -- FIXME: What's it mean?
26                        edi-offset)
27
28                  (:res res (descriptor-reg any-reg) edx-offset)
29
30                  (:temp eax unsigned-reg eax-offset)
31                  (:temp ecx unsigned-reg ecx-offset))
32
33                 (inst mov ecx x)
34                 (inst or ecx y)
35                 (inst test ecx fixnum-tag-mask)  ; both fixnums?
36                 (inst jmp :nz DO-STATIC-FUN)     ; no - do generic
37
38                 ,@body
39                 (inst clc) ; single-value return
40                 (inst ret)
41
42                 DO-STATIC-FUN
43                 (inst push ebp-tn)
44                 (inst lea ebp-tn (make-ea :dword
45                                           :base esp-tn
46                                           :disp (* 2 n-word-bytes)))
47                 (inst sub esp-tn (fixnumize 1))
48                 (inst push (make-ea :dword :base ebp-tn :disp (- n-word-bytes)))
49                 (inst mov ecx (fixnumize 2)) ; arg count
50                 (inst jmp
51                       (make-ea :dword
52                                :disp (+ nil-value
53                                         (static-fun-offset
54                                          ',(symbolicate "TWO-ARG-" fun))))))))
55
56   (define-generic-arith-routine (+ 10)
57     (move res x)
58     (inst add res y)
59     (inst jmp :no OKAY)
60     (inst rcr res 1)                  ; carry has correct sign
61     (inst sar res 1)                  ; remove type bits
62
63     (move ecx res)
64
65     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
66       (storew ecx res bignum-digits-offset other-pointer-lowtag))
67
68     OKAY)
69
70   (define-generic-arith-routine (- 10)
71     (move res x)
72     (inst sub res y)
73     (inst jmp :no OKAY)
74     (inst cmc)                        ; carry has correct sign now
75     (inst rcr res 1)
76     (inst sar res 1)                  ; remove type bits
77
78     (move ecx res)
79
80     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
81       (storew ecx res bignum-digits-offset other-pointer-lowtag))
82     OKAY)
83
84   (define-generic-arith-routine (* 30)
85     (move eax x)                          ; must use eax for 64-bit result
86     (inst sar eax n-fixnum-tag-bits)      ; remove *4 fixnum bias
87     (inst imul y)                         ; result in edx:eax
88     (inst jmp :no OKAY)                   ; still fixnum
89
90     ;; zzz jrd changed edx to ebx in here, as edx isn't listed as a temp, above
91     ;;     pfw says that loses big -- edx is target for arg x and result res
92     ;;     note that 'edx' is not defined -- using x
93     (inst shrd eax x n-fixnum-tag-bits)    ; high bits from edx
94     (inst sar x n-fixnum-tag-bits)         ; now shift edx too
95
96     (move ecx x)                           ; save high bits from cdq
97     (inst cdq)                             ; edx:eax <- sign-extend of eax
98     (inst cmp x ecx)
99     (inst jmp :e SINGLE-WORD-BIGNUM)
100
101     (with-fixed-allocation (res bignum-widetag (+ bignum-digits-offset 2))
102       (storew eax res bignum-digits-offset other-pointer-lowtag)
103       (storew ecx res (1+ bignum-digits-offset) other-pointer-lowtag))
104     (inst jmp DONE)
105
106     SINGLE-WORD-BIGNUM
107
108     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
109       (storew eax res bignum-digits-offset other-pointer-lowtag))
110     (inst jmp DONE)
111
112     OKAY
113     (move res eax)
114     DONE))
115 \f
116 ;;;; negation
117
118 (define-assembly-routine (generic-negate
119                           (:cost 10)
120                           (:return-style :full-call)
121                           (:policy :safe)
122                           (:translate %negate)
123                           (:save-p t))
124                          ((:arg x (descriptor-reg any-reg) edx-offset)
125                           (:res res (descriptor-reg any-reg) edx-offset)
126
127                           (:temp eax unsigned-reg eax-offset)
128                           (:temp ecx unsigned-reg ecx-offset))
129   (inst test x fixnum-tag-mask)
130   (inst jmp :z FIXNUM)
131
132   (inst push ebp-tn)
133   (inst lea ebp-tn (make-ea :dword :base esp-tn :disp (* 2 n-word-bytes)))
134   (inst sub esp-tn (fixnumize 1))
135   (inst push (make-ea :dword :base ebp-tn :disp (- n-word-bytes)))
136   (inst mov ecx (fixnumize 1))    ; arg count
137   (inst jmp (make-ea :dword
138                      :disp (+ nil-value (static-fun-offset '%negate))))
139
140   FIXNUM
141   (move res x)
142   (inst neg res)                        ; (- most-negative-fixnum) is BIGNUM
143   (inst jmp :no OKAY)
144   (inst shr res n-fixnum-tag-bits)      ; sign bit is data - remove type bits
145   (move ecx res)
146
147   (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
148     (storew ecx res bignum-digits-offset other-pointer-lowtag))
149
150   OKAY)
151 \f
152 ;;;; comparison
153
154 (macrolet ((define-cond-assem-rtn (name translate static-fn test)
155              #+sb-assembling
156              `(define-assembly-routine (,name
157                                         (:return-style :none))
158                 ((:arg x (descriptor-reg any-reg) edx-offset)
159                  (:arg y (descriptor-reg any-reg) edi-offset)
160
161                  (:temp ecx unsigned-reg ecx-offset))
162
163                 (inst mov ecx x)
164                 (inst or ecx y)
165                 (inst test ecx fixnum-tag-mask)
166                 (inst jmp :nz DO-STATIC-FUN)  ; are both fixnums?
167
168                 (inst cmp x y)
169                 (inst ret)
170
171                 DO-STATIC-FUN
172                 (move ecx esp-tn)
173                 (inst sub esp-tn (fixnumize 3))
174                 (inst mov (make-ea :dword
175                                    :base ecx
176                                    :disp (frame-byte-offset ocfp-save-offset))
177                       ebp-tn)
178                 (move ebp-tn ecx)
179                 (inst mov ecx (fixnumize 2))
180                 (inst call (make-ea :dword
181                                     :disp (+ nil-value
182                                              (static-fun-offset ',static-fn))))
183                 ;; HACK: We depend on NIL having the lowest address of all
184                 ;; static symbols (including T)
185                 ,@(ecase test
186                     (:l `((inst mov y (1+ nil-value))
187                           (inst cmp y x)))
188                     (:g `((inst cmp x (1+ nil-value)))))
189                 (inst ret))
190              #-sb-assembling
191              `(define-vop (,name)
192                 (:translate ,translate)
193                 (:policy :safe)
194                 (:save-p t)
195                 (:args (x :scs (descriptor-reg any-reg) :target edx)
196                        (y :scs (descriptor-reg any-reg) :target edi))
197
198                 (:temporary (:sc unsigned-reg :offset edx-offset
199                                  :from (:argument 0))
200                             edx)
201                 (:temporary (:sc unsigned-reg :offset edi-offset
202                                  :from (:argument 1))
203                             edi)
204
205                 (:temporary (:sc unsigned-reg :offset ecx-offset
206                                  :from :eval)
207                             ecx)
208                 (:conditional ,test)
209                 (:generator 10
210                    (move edx x)
211                    (move edi y)
212                    (inst lea ecx (make-ea :dword
213                                           :disp (make-fixup ',name :assembly-routine)))
214                    (inst call ecx)))))
215
216   (define-cond-assem-rtn generic-< < two-arg-< :l)
217   (define-cond-assem-rtn generic-> > two-arg-> :g))
218
219 #+sb-assembling
220 (define-assembly-routine (generic-eql
221                           (:return-style :none))
222                          ((:arg x (descriptor-reg any-reg) edx-offset)
223                           (:arg y (descriptor-reg any-reg) edi-offset)
224
225                           (:temp ecx unsigned-reg ecx-offset))
226   (inst mov ecx x)
227   (inst and ecx y)
228   (inst and ecx lowtag-mask)
229   (inst cmp ecx other-pointer-lowtag)
230   (inst jmp :e DO-STATIC-FUN)
231
232   ;; Not both other pointers
233   (inst cmp x y)
234   RET
235   (inst ret)
236
237   DO-STATIC-FUN
238   ;; Might as well fast path that...
239   (inst cmp x y)
240   (inst jmp :e RET)
241
242   (move ecx esp-tn)
243   (inst sub esp-tn (fixnumize 3))
244   (inst mov (make-ea :dword
245                      :base ecx
246                      :disp (frame-byte-offset ocfp-save-offset))
247         ebp-tn)
248   (move ebp-tn ecx)
249   (inst mov ecx (fixnumize 2))
250   (inst call (make-ea :dword
251                       :disp (+ nil-value (static-fun-offset 'eql))))
252   (load-symbol y t)
253   (inst cmp x y)
254   (inst ret))
255
256 #-sb-assembling
257 (define-vop (generic-eql)
258   (:translate eql)
259   (:policy :safe)
260   (:save-p t)
261   (:args (x :scs (descriptor-reg any-reg) :target edx)
262          (y :scs (descriptor-reg any-reg) :target edi))
263
264   (:temporary (:sc unsigned-reg :offset edx-offset
265                :from (:argument 0))
266               edx)
267   (:temporary (:sc unsigned-reg :offset edi-offset
268                :from (:argument 1))
269               edi)
270
271   (:temporary (:sc unsigned-reg :offset ecx-offset
272                :from :eval)
273               ecx)
274   (:conditional :e)
275   (:generator 10
276     (move edx x)
277     (move edi y)
278     (inst lea ecx (make-ea :dword
279                            :disp (make-fixup 'generic-eql :assembly-routine)))
280     (inst call ecx)))
281
282 #+sb-assembling
283 (define-assembly-routine (generic-=
284                           (:return-style :none))
285                          ((:arg x (descriptor-reg any-reg) edx-offset)
286                           (:arg y (descriptor-reg any-reg) edi-offset)
287
288                           (:temp ecx unsigned-reg ecx-offset))
289   (inst mov ecx x)
290   (inst or ecx y)
291   (inst test ecx fixnum-tag-mask)
292   (inst jmp :nz DO-STATIC-FUN)
293
294   ;; Both fixnums
295   (inst cmp x y)
296   (inst ret)
297
298   DO-STATIC-FUN
299   (move ecx esp-tn)
300   (inst sub esp-tn (fixnumize 3))
301   (inst mov (make-ea :dword
302                      :base ecx
303                      :disp (frame-byte-offset ocfp-save-offset))
304         ebp-tn)
305   (move ebp-tn ecx)
306   (inst mov ecx (fixnumize 2))
307   (inst call (make-ea :dword
308                       :disp (+ nil-value (static-fun-offset 'two-arg-=))))
309   (load-symbol y t)
310   (inst cmp x y)
311   (inst ret))
312
313 #-sb-assembling
314 (define-vop (generic-=)
315   (:translate =)
316   (:policy :safe)
317   (:save-p t)
318   (:args (x :scs (descriptor-reg any-reg) :target edx)
319          (y :scs (descriptor-reg any-reg) :target edi))
320
321   (:temporary (:sc unsigned-reg :offset edx-offset
322                :from (:argument 0))
323               edx)
324   (:temporary (:sc unsigned-reg :offset edi-offset
325                :from (:argument 1))
326               edi)
327
328   (:temporary (:sc unsigned-reg :offset ecx-offset
329                :from :eval)
330               ecx)
331   (:conditional :e)
332   (:generator 10
333     (move edx x)
334     (move edi y)
335     (inst lea ecx (make-ea :dword
336                            :disp (make-fixup 'generic-= :assembly-routine)))
337     (inst call ecx)))
338
339 \f
340 ;;; Support for the Mersenne Twister, MT19937, random number generator
341 ;;; due to Matsumoto and Nishimura.
342 ;;;
343 ;;; Makoto Matsumoto and T. Nishimura, "Mersenne twister: A
344 ;;; 623-dimensionally equidistributed uniform pseudorandom number
345 ;;; generator.", ACM Transactions on Modeling and Computer Simulation,
346 ;;; 1997, to appear.
347 ;;;
348 ;;; State:
349 ;;;  0-1:   Constant matrix A. [0, #x9908b0df] (not used here)
350 ;;;  2:     Index; init. to 1.
351 ;;;  3-626: State.
352
353 ;;; This assembly routine is called from the inline VOP and updates
354 ;;; the state vector with new random numbers. The state vector is
355 ;;; passed in the EAX register.
356 #+sb-assembling ; We don't want a vop for this one.
357 (define-assembly-routine
358     (random-mt19937-update)
359     ((:temp state unsigned-reg eax-offset)
360      (:temp k unsigned-reg ebx-offset)
361      (:temp y unsigned-reg ecx-offset)
362      (:temp tmp unsigned-reg edx-offset))
363
364   ;; Save the temporary registers.
365   (inst push k)
366   (inst push y)
367   (inst push tmp)
368
369   ;; Generate a new set of results.
370   (inst xor k k)
371   LOOP1
372   (inst mov y (make-ea-for-vector-data state :index k :offset 3))
373   (inst mov tmp (make-ea-for-vector-data state :index k :offset (+ 1 3)))
374   (inst and y #x80000000)
375   (inst and tmp #x7fffffff)
376   (inst or y tmp)
377   (inst shr y 1)
378   (inst jmp :nc skip1)
379   (inst xor y #x9908b0df)
380   SKIP1
381   (inst xor y (make-ea-for-vector-data state :index k :offset (+ 397 3)))
382   (inst mov (make-ea-for-vector-data state :index k :offset 3) y)
383   (inst inc k)
384   (inst cmp k (- 624 397))
385   (inst jmp :b loop1)
386   LOOP2
387   (inst mov y (make-ea-for-vector-data state :index k :offset 3))
388   (inst mov tmp (make-ea-for-vector-data state :index k :offset (+ 1 3)))
389   (inst and y #x80000000)
390   (inst and tmp #x7fffffff)
391   (inst or y tmp)
392   (inst shr y 1)
393   (inst jmp :nc skip2)
394   (inst xor y #x9908b0df)
395   SKIP2
396   (inst xor y (make-ea-for-vector-data state :index k :offset (+ (- 397 624) 3)))
397   (inst mov (make-ea-for-vector-data state :index k :offset 3) y)
398   (inst inc k)
399   (inst cmp k (- 624 1))
400   (inst jmp :b loop2)
401
402   (inst mov y (make-ea-for-vector-data state :offset (+ (- 624 1) 3)))
403   (inst mov tmp (make-ea-for-vector-data state :offset (+ 0 3)))
404   (inst and y #x80000000)
405   (inst and tmp #x7fffffff)
406   (inst or y tmp)
407   (inst shr y 1)
408   (inst jmp :nc skip3)
409   (inst xor y #x9908b0df)
410   SKIP3
411   (inst xor y (make-ea-for-vector-data state :offset (+ (- 397 1) 3)))
412   (inst mov (make-ea-for-vector-data state :offset (+ (- 624 1) 3)) y)
413
414   ;; Restore the temporary registers and return.
415   (inst pop tmp)
416   (inst pop y)
417   (inst pop k)
418   (inst ret))