0.9.12.25:
[sbcl/smoofra.git] / src / assembly / x86 / arith.lisp
blob46aa23c4c1fa53729ecde27d83fe8bc214012b5b
1 ;;;; simple cases for generic arithmetic
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.
12 (in-package "SB!VM")
14 ;;;; addition, subtraction, and multiplication
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)
28 (:res res (descriptor-reg any-reg) edx-offset)
30 (:temp eax unsigned-reg eax-offset)
31 (:temp ebx unsigned-reg ebx-offset)
32 (:temp ecx unsigned-reg ecx-offset))
34 (declare (ignorable ebx))
36 (inst test x 3) ; fixnum?
37 (inst jmp :nz DO-STATIC-FUN) ; no - do generic
38 (inst test y 3) ; fixnum?
39 (inst jmp :z DO-BODY) ; yes - doit here
41 DO-STATIC-FUN
42 (inst pop eax)
43 (inst push ebp-tn)
44 (inst lea
45 ebp-tn
46 (make-ea :dword :base esp-tn :disp n-word-bytes))
47 (inst sub esp-tn (fixnumize 2))
48 (inst push eax) ; callers return addr
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)))))
56 DO-BODY
57 ,@body)))
59 (define-generic-arith-routine (+ 10)
60 (move res x)
61 (inst add res y)
62 (inst jmp :no OKAY)
63 (inst rcr res 1) ; carry has correct sign
64 (inst sar res 1) ; remove type bits
66 (move ecx res)
68 (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
69 (storew ecx res bignum-digits-offset other-pointer-lowtag))
71 OKAY)
73 (define-generic-arith-routine (- 10)
74 (move res x)
75 (inst sub res y)
76 (inst jmp :no OKAY)
77 (inst cmc) ; carry has correct sign now
78 (inst rcr res 1)
79 (inst sar res 1) ; remove type bits
81 (move ecx res)
83 (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
84 (storew ecx res bignum-digits-offset other-pointer-lowtag))
85 OKAY)
87 (define-generic-arith-routine (* 30)
88 (move eax x) ; must use eax for 64-bit result
89 (inst sar eax 2) ; remove *4 fixnum bias
90 (inst imul y) ; result in edx:eax
91 (inst jmp :no okay) ; still fixnum
93 ;; zzz jrd changed edx to ebx in here, as edx isn't listed as a temp, above
94 ;; pfw says that loses big -- edx is target for arg x and result res
95 ;; note that 'edx' is not defined -- using x
96 (inst shrd eax x 2) ; high bits from edx
97 (inst sar x 2) ; now shift edx too
99 (move ecx x) ; save high bits from cdq
100 (inst cdq) ; edx:eax <- sign-extend of eax
101 (inst cmp x ecx)
102 (inst jmp :e SINGLE-WORD-BIGNUM)
104 (with-fixed-allocation (res bignum-widetag (+ bignum-digits-offset 2))
105 (storew eax res bignum-digits-offset other-pointer-lowtag)
106 (storew ecx res (1+ bignum-digits-offset) other-pointer-lowtag))
107 (inst jmp DONE)
109 SINGLE-WORD-BIGNUM
111 (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
112 (storew eax res bignum-digits-offset other-pointer-lowtag))
113 (inst jmp DONE)
115 OKAY
116 (move res eax)
117 DONE))
119 ;;;; negation
121 (define-assembly-routine (generic-negate
122 (:cost 10)
123 (:return-style :full-call)
124 (:policy :safe)
125 (:translate %negate)
126 (:save-p t))
127 ((:arg x (descriptor-reg any-reg) edx-offset)
128 (:res res (descriptor-reg any-reg) edx-offset)
130 (:temp eax unsigned-reg eax-offset)
131 (:temp ecx unsigned-reg ecx-offset))
132 (inst test x 3)
133 (inst jmp :z FIXNUM)
135 (inst pop eax)
136 (inst push ebp-tn)
137 (inst lea ebp-tn (make-ea :dword :base esp-tn :disp n-word-bytes))
138 (inst sub esp-tn (fixnumize 2))
139 (inst push eax)
140 (inst mov ecx (fixnumize 1)) ; arg count
141 (inst jmp (make-ea :dword
142 :disp (+ nil-value (static-fun-offset '%negate))))
144 FIXNUM
145 (move res x)
146 (inst neg res) ; (- most-negative-fixnum) is BIGNUM
147 (inst jmp :no OKAY)
148 (inst shr res 2) ; sign bit is data - remove type bits
149 (move ecx res)
151 (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
152 (storew ecx res bignum-digits-offset other-pointer-lowtag))
154 OKAY)
156 ;;;; comparison
158 (macrolet ((define-cond-assem-rtn (name translate static-fn test)
159 `(define-assembly-routine (,name
160 (:cost 10)
161 (:return-style :full-call)
162 (:policy :safe)
163 (:translate ,translate)
164 (:save-p t))
165 ((:arg x (descriptor-reg any-reg) edx-offset)
166 (:arg y (descriptor-reg any-reg) edi-offset)
168 (:res res descriptor-reg edx-offset)
170 (:temp eax unsigned-reg eax-offset)
171 (:temp ecx unsigned-reg ecx-offset))
173 ;; KLUDGE: The "3" here is a mask for the bits which will be
174 ;; zero in a fixnum. It should have a symbolic name. (Actually,
175 ;; it might already have a symbolic name which the coder
176 ;; couldn't be bothered to use..) -- WHN 19990917
177 (inst test x 3)
178 (inst jmp :nz TAIL-CALL-TO-STATIC-FN)
179 (inst test y 3)
180 (inst jmp :z INLINE-FIXNUM-COMPARE)
182 TAIL-CALL-TO-STATIC-FN
183 (inst pop eax)
184 (inst push ebp-tn)
185 (inst lea ebp-tn (make-ea :dword
186 :base esp-tn
187 :disp n-word-bytes))
188 (inst sub esp-tn (fixnumize 2)) ; FIXME: Push 2 words on stack,
189 ; weirdly?
190 (inst push eax)
191 (inst mov ecx (fixnumize 2)) ; FIXME: FIXNUMIZE and
192 ; SINGLE-FLOAT-BITS are parallel,
193 ; should be named parallelly.
194 (inst jmp (make-ea :dword
195 :disp (+ nil-value
196 (static-fun-offset ',static-fn))))
198 INLINE-FIXNUM-COMPARE
199 (inst cmp x y)
200 (inst mov res nil-value)
201 (inst jmp ,test RETURN-FALSE)
203 (load-symbol res t)
205 RETURN-FALSE
206 DONE)))
208 (define-cond-assem-rtn generic-< < two-arg-< :ge)
209 (define-cond-assem-rtn generic-> > two-arg-> :le))
211 (define-assembly-routine (generic-eql
212 (:cost 10)
213 (:return-style :full-call)
214 (:policy :safe)
215 (:translate eql)
216 (:save-p t))
217 ((:arg x (descriptor-reg any-reg) edx-offset)
218 (:arg y (descriptor-reg any-reg) edi-offset)
220 (:res res descriptor-reg edx-offset)
222 (:temp eax unsigned-reg eax-offset)
223 (:temp ecx unsigned-reg ecx-offset))
224 (inst cmp x y)
225 (inst jmp :e RETURN-T)
226 (inst test x 3)
227 (inst jmp :z RETURN-NIL)
228 (inst test y 3)
229 (inst jmp :nz DO-STATIC-FN)
231 RETURN-NIL
232 (inst mov res nil-value)
233 (inst jmp DONE)
235 DO-STATIC-FN
236 (inst pop eax)
237 (inst push ebp-tn)
238 (inst lea ebp-tn (make-ea :dword :base esp-tn :disp n-word-bytes))
239 (inst sub esp-tn (fixnumize 2))
240 (inst push eax)
241 (inst mov ecx (fixnumize 2))
242 (inst jmp (make-ea :dword
243 :disp (+ nil-value (static-fun-offset 'eql))))
245 RETURN-T
246 (load-symbol res t)
248 DONE)
250 (define-assembly-routine (generic-=
251 (:cost 10)
252 (:return-style :full-call)
253 (:policy :safe)
254 (:translate =)
255 (:save-p t))
256 ((:arg x (descriptor-reg any-reg) edx-offset)
257 (:arg y (descriptor-reg any-reg) edi-offset)
259 (:res res descriptor-reg edx-offset)
261 (:temp eax unsigned-reg eax-offset)
262 (:temp ecx unsigned-reg ecx-offset)
264 (inst test x 3) ; descriptor?
265 (inst jmp :nz DO-STATIC-FN) ; yes, do it here
266 (inst test y 3) ; descriptor?
267 (inst jmp :nz DO-STATIC-FN)
268 (inst cmp x y)
269 (inst jmp :e RETURN-T) ; ok
271 (inst mov res nil-value)
272 (inst jmp DONE)
274 DO-STATIC-FN
275 (inst pop eax)
276 (inst push ebp-tn)
277 (inst lea ebp-tn (make-ea :dword :base esp-tn :disp n-word-bytes))
278 (inst sub esp-tn (fixnumize 2))
279 (inst push eax)
280 (inst mov ecx (fixnumize 2))
281 (inst jmp (make-ea :dword
282 :disp (+ nil-value (static-fun-offset 'two-arg-=))))
284 RETURN-T
285 (load-symbol res t)
287 DONE)
290 ;;; Support for the Mersenne Twister, MT19937, random number generator
291 ;;; due to Matsumoto and Nishimura.
293 ;;; Makoto Matsumoto and T. Nishimura, "Mersenne twister: A
294 ;;; 623-dimensionally equidistributed uniform pseudorandom number
295 ;;; generator.", ACM Transactions on Modeling and Computer Simulation,
296 ;;; 1997, to appear.
298 ;;; State:
299 ;;; 0-1: Constant matrix A. [0, #x9908b0df] (not used here)
300 ;;; 2: Index; init. to 1.
301 ;;; 3-626: State.
303 ;;; This assembly routine is called from the inline VOP and updates
304 ;;; the state vector with new random numbers. The state vector is
305 ;;; passed in the EAX register.
306 #+sb-assembling ; We don't want a vop for this one.
307 (define-assembly-routine
308 (random-mt19937-update)
309 ((:temp state unsigned-reg eax-offset)
310 (:temp k unsigned-reg ebx-offset)
311 (:temp y unsigned-reg ecx-offset)
312 (:temp tmp unsigned-reg edx-offset))
314 ;; Save the temporary registers.
315 (inst push k)
316 (inst push y)
317 (inst push tmp)
319 ;; Generate a new set of results.
320 (inst xor k k)
321 LOOP1
322 (inst mov y (make-ea :dword :base state :index k :scale 4
323 :disp (- (* (+ 3 vector-data-offset)
324 n-word-bytes)
325 other-pointer-lowtag)))
326 (inst mov tmp (make-ea :dword :base state :index k :scale 4
327 :disp (- (* (+ 1 3 vector-data-offset)
328 n-word-bytes)
329 other-pointer-lowtag)))
330 (inst and y #x80000000)
331 (inst and tmp #x7fffffff)
332 (inst or y tmp)
333 (inst shr y 1)
334 (inst jmp :nc skip1)
335 (inst xor y #x9908b0df)
336 SKIP1
337 (inst xor y (make-ea :dword :base state :index k :scale 4
338 :disp (- (* (+ 397 3 vector-data-offset)
339 n-word-bytes)
340 other-pointer-lowtag)))
341 (inst mov (make-ea :dword :base state :index k :scale 4
342 :disp (- (* (+ 3 vector-data-offset)
343 n-word-bytes)
344 other-pointer-lowtag))
346 (inst inc k)
347 (inst cmp k (- 624 397))
348 (inst jmp :b loop1)
349 LOOP2
350 (inst mov y (make-ea :dword :base state :index k :scale 4
351 :disp (- (* (+ 3 vector-data-offset)
352 n-word-bytes)
353 other-pointer-lowtag)))
354 (inst mov tmp (make-ea :dword :base state :index k :scale 4
355 :disp (- (* (+ 1 3 vector-data-offset)
356 n-word-bytes)
357 other-pointer-lowtag)))
358 (inst and y #x80000000)
359 (inst and tmp #x7fffffff)
360 (inst or y tmp)
361 (inst shr y 1)
362 (inst jmp :nc skip2)
363 (inst xor y #x9908b0df)
364 SKIP2
365 (inst xor y (make-ea :dword :base state :index k :scale 4
366 :disp (- (* (+ (- 397 624) 3 vector-data-offset)
367 n-word-bytes)
368 other-pointer-lowtag)))
369 (inst mov (make-ea :dword :base state :index k :scale 4
370 :disp (- (* (+ 3 vector-data-offset)
371 n-word-bytes)
372 other-pointer-lowtag))
374 (inst inc k)
375 (inst cmp k (- 624 1))
376 (inst jmp :b loop2)
378 (inst mov y (make-ea :dword :base state
379 :disp (- (* (+ (- 624 1) 3 vector-data-offset)
380 n-word-bytes)
381 other-pointer-lowtag)))
382 (inst mov tmp (make-ea :dword :base state
383 :disp (- (* (+ 0 3 vector-data-offset)
384 n-word-bytes)
385 other-pointer-lowtag)))
386 (inst and y #x80000000)
387 (inst and tmp #x7fffffff)
388 (inst or y tmp)
389 (inst shr y 1)
390 (inst jmp :nc skip3)
391 (inst xor y #x9908b0df)
392 SKIP3
393 (inst xor y (make-ea :dword :base state
394 :disp (- (* (+ (- 397 1) 3 vector-data-offset)
395 n-word-bytes)
396 other-pointer-lowtag)))
397 (inst mov (make-ea :dword :base state
398 :disp (- (* (+ (- 624 1) 3 vector-data-offset)
399 n-word-bytes)
400 other-pointer-lowtag))
403 ;; Restore the temporary registers and return.
404 (inst pop tmp)
405 (inst pop y)
406 (inst pop k)
407 (inst ret))