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