b6a1934371472d09b31b1ece949b6cf0afe3888d
[sbcl.git] / src / assembly / x86-64 / 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) rdx-offset)
24                  (:arg y (descriptor-reg any-reg)
25                        ;; this seems wrong esi-offset -- FIXME: What's it mean?
26                        rdi-offset)
27
28                  (:res res (descriptor-reg any-reg) rdx-offset)
29
30                  (:temp rax unsigned-reg rax-offset)
31                  (:temp rcx unsigned-reg rcx-offset))
32
33                 (inst mov rcx x)
34                 (inst or rcx y)
35                 (inst test rcx 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 rbp-tn)
44                 (inst lea rbp-tn (make-ea :qword
45                                           :base rsp-tn
46                                           :disp (* 2 n-word-bytes)))
47                 (inst sub rsp-tn (fixnumize 1))
48                 (inst push (make-ea :qword :base rbp-tn :disp (- n-word-bytes)))
49                 (inst mov rcx (fixnumize 2)) ; arg count
50                 (inst jmp
51                       (make-ea :qword
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 2)                  ; remove type bits
62
63     (move rcx res)
64
65     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
66       (storew rcx 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 2)                  ; remove type bits
77
78     (move rcx res)
79
80     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
81       (storew rcx res bignum-digits-offset other-pointer-lowtag))
82     OKAY)
83
84   (define-generic-arith-routine (* 30)
85     (move rax x)                     ; must use eax for 64-bit result
86     (inst sar rax n-fixnum-tag-bits) ; remove *8 fixnum bias
87     (inst imul y)                    ; result in edx:eax
88     (inst jmp :no OKAY)              ; still fixnum
89
90     (inst shrd rax x n-fixnum-tag-bits) ; high bits from edx
91     (inst sar x n-fixnum-tag-bits)      ; now shift edx too
92
93     (move rcx x)                   ; save high bits from cqo
94     (inst cqo)                     ; edx:eax <- sign-extend of eax
95     (inst cmp x rcx)
96     (inst jmp :e SINGLE-WORD-BIGNUM)
97
98     (with-fixed-allocation (res bignum-widetag (+ bignum-digits-offset 2))
99       (storew rax res bignum-digits-offset other-pointer-lowtag)
100       (storew rcx res (1+ bignum-digits-offset) other-pointer-lowtag))
101     (inst jmp DONE)
102
103     SINGLE-WORD-BIGNUM
104
105     (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
106       (storew rax res bignum-digits-offset other-pointer-lowtag))
107     (inst jmp DONE)
108
109     OKAY
110     (move res rax)
111     DONE))
112 \f
113 ;;;; negation
114
115 (define-assembly-routine (generic-negate
116                           (:cost 10)
117                           (:return-style :full-call)
118                           (:policy :safe)
119                           (:translate %negate)
120                           (:save-p t))
121                          ((:arg x (descriptor-reg any-reg) rdx-offset)
122                           (:res res (descriptor-reg any-reg) rdx-offset)
123
124                           (:temp rax unsigned-reg rax-offset)
125                           (:temp rcx unsigned-reg rcx-offset))
126   (inst test x fixnum-tag-mask)
127   (inst jmp :z FIXNUM)
128
129   (inst push rbp-tn)
130   (inst lea rbp-tn (make-ea :qword :base rsp-tn :disp (* 2 n-word-bytes)))
131   (inst sub rsp-tn (fixnumize 1))
132   (inst push (make-ea :qword :base rbp-tn :disp (- n-word-bytes)))
133   (inst mov rcx (fixnumize 1))    ; arg count
134   (inst jmp (make-ea :qword
135                      :disp (+ nil-value (static-fun-offset '%negate))))
136
137   FIXNUM
138   (move res x)
139   (inst neg res)                        ; (- most-negative-fixnum) is BIGNUM
140   (inst jmp :no OKAY)
141   (inst shr res n-fixnum-tag-bits)      ; sign bit is data - remove type bits
142   (move rcx res)
143
144   (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset))
145     (storew rcx res bignum-digits-offset other-pointer-lowtag))
146
147   OKAY)
148 \f
149 ;;;; comparison
150
151 (macrolet ((define-cond-assem-rtn (name translate static-fn test)
152                (declare (ignorable translate static-fn))
153              #+sb-assembling
154              `(define-assembly-routine (,name
155                                         (:return-style :none))
156                   ((:arg x (descriptor-reg any-reg) rdx-offset)
157                    (:arg y (descriptor-reg any-reg) rdi-offset)
158
159                    (:temp rcx unsigned-reg rcx-offset))
160
161                 (inst mov rcx x)
162                 (inst or rcx y)
163                 (inst test rcx fixnum-tag-mask)
164                 (inst jmp :nz DO-STATIC-FUN)  ; are both fixnums?
165
166                 (inst cmp x y)
167                 (inst ret)
168
169                 DO-STATIC-FUN
170                 (move rcx rsp-tn)
171                 (inst sub rsp-tn (fixnumize 3))
172                 (inst mov (make-ea :qword
173                                    :base rcx
174                                    :disp (frame-byte-offset ocfp-save-offset))
175                       rbp-tn)
176                 (move rbp-tn rcx)
177                 (inst mov rcx (fixnumize 2))
178                 (inst call (make-ea :qword
179                                     :disp (+ nil-value
180                                              (static-fun-offset ',static-fn))))
181                 ;; HACK: We depend on NIL having the lowest address of all
182                 ;; static symbols (including T)
183                 ,@(ecase test
184                     (:l `((inst mov y (1+ nil-value))
185                           (inst cmp y x)))
186                     (:g `((inst cmp x (1+ nil-value)))))
187                 (inst ret))
188              #-sb-assembling
189              `(define-vop (,name)
190                 (:translate ,translate)
191                 (:policy :safe)
192                 (:save-p t)
193                 (:args (x :scs (descriptor-reg any-reg) :target rdx)
194                        (y :scs (descriptor-reg any-reg) :target rdi))
195
196                 (:temporary (:sc unsigned-reg :offset rdx-offset
197                                  :from (:argument 0))
198                             rdx)
199                 (:temporary (:sc unsigned-reg :offset rdi-offset
200                                  :from (:argument 1))
201                             rdi)
202
203                 (:temporary (:sc unsigned-reg :offset rcx-offset
204                                  :from :eval)
205                             rcx)
206                 (:conditional ,test)
207                 (:generator 10
208                    (move rdx x)
209                    (move rdi y)
210                    (inst lea rcx (make-ea :qword
211                                           :disp (make-fixup ',name :assembly-routine)))
212                    (inst call rcx)))))
213
214   (define-cond-assem-rtn generic-< < two-arg-< :l)
215   (define-cond-assem-rtn generic-> > two-arg-> :g))
216
217 #+sb-assembling
218 (define-assembly-routine (generic-eql
219                           (:return-style :none))
220                          ((:arg x (descriptor-reg any-reg) rdx-offset)
221                           (:arg y (descriptor-reg any-reg) rdi-offset)
222
223                           (:temp rcx unsigned-reg rcx-offset))
224
225   (inst mov rcx x)
226   (inst and rcx y)
227   (inst test rcx fixnum-tag-mask)
228   (inst jmp :nz DO-STATIC-FUN)
229
230   ;; At least one fixnum
231   (inst cmp x y)
232   (inst ret)
233
234   DO-STATIC-FUN
235   (move rcx rsp-tn)
236   (inst sub rsp-tn (fixnumize 3))
237   (inst mov (make-ea :qword
238                      :base rcx
239                      :disp (frame-byte-offset ocfp-save-offset))
240         rbp-tn)
241   (move rbp-tn rcx)
242   (inst mov rcx (fixnumize 2))
243   (inst call (make-ea :qword
244                       :disp (+ nil-value (static-fun-offset 'eql))))
245   (load-symbol y t)
246   (inst cmp x y)
247   (inst ret))
248
249 #-sb-assembling
250 (define-vop (generic-eql)
251   (:translate eql)
252   (:policy :safe)
253   (:save-p t)
254   (:args (x :scs (descriptor-reg any-reg) :target rdx)
255          (y :scs (descriptor-reg any-reg) :target rdi))
256
257   (:temporary (:sc unsigned-reg :offset rdx-offset
258                :from (:argument 0))
259               rdx)
260   (:temporary (:sc unsigned-reg :offset rdi-offset
261                :from (:argument 1))
262               rdi)
263
264   (:temporary (:sc unsigned-reg :offset rcx-offset
265                :from :eval)
266               rcx)
267   (:conditional :e)
268   (:generator 10
269     (move rdx x)
270     (move rdi y)
271     (inst lea rcx (make-ea :qword
272                            :disp (make-fixup 'generic-eql :assembly-routine)))
273     (inst call rcx)))
274
275 #+sb-assembling
276 (define-assembly-routine (generic-=
277                           (:return-style :none))
278                          ((:arg x (descriptor-reg any-reg) rdx-offset)
279                           (:arg y (descriptor-reg any-reg) rdi-offset)
280
281                           (:temp rcx unsigned-reg rcx-offset))
282   (inst mov rcx x)
283   (inst or rcx y)
284   (inst test rcx fixnum-tag-mask)
285   (inst jmp :nz DO-STATIC-FUN)
286
287   ;; Both fixnums
288   (inst cmp x y)
289   (inst ret)
290
291   DO-STATIC-FUN
292   (move rcx rsp-tn)
293   (inst sub rsp-tn (fixnumize 3))
294   (inst mov (make-ea :qword
295                      :base rcx
296                      :disp (frame-byte-offset ocfp-save-offset))
297         rbp-tn)
298   (move rbp-tn rcx)
299   (inst mov rcx (fixnumize 2))
300   (inst call (make-ea :qword
301                       :disp (+ nil-value (static-fun-offset 'two-arg-=))))
302   (load-symbol y t)
303   (inst cmp x y)
304   (inst ret))
305
306 #-sb-assembling
307 (define-vop (generic-=)
308   (:translate =)
309   (:policy :safe)
310   (:save-p t)
311   (:args (x :scs (descriptor-reg any-reg) :target rdx)
312          (y :scs (descriptor-reg any-reg) :target rdi))
313
314   (:temporary (:sc unsigned-reg :offset rdx-offset
315                :from (:argument 0))
316               rdx)
317   (:temporary (:sc unsigned-reg :offset rdi-offset
318                :from (:argument 1))
319               rdi)
320
321   (:temporary (:sc unsigned-reg :offset rcx-offset
322                :from :eval)
323               rcx)
324   (:conditional :e)
325   (:generator 10
326     (move rdx x)
327     (move rdi y)
328     (inst lea rcx (make-ea :qword
329                            :disp (make-fixup 'generic-= :assembly-routine)))
330     (inst call rcx)))