;;;; simple cases for generic arithmetic ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; ;;;; This software is derived from the CMU CL system, which was ;;;; written at Carnegie Mellon University and released into the ;;;; public domain. The software is in the public domain and is ;;;; provided with absolutely no warranty. See the COPYING and CREDITS ;;;; files for more information. (in-package "SB!VM") ;;;; addition, subtraction, and multiplication (macrolet ((define-generic-arith-routine ((fun cost) &body body) `(define-assembly-routine (,(symbolicate "GENERIC-" fun) (:cost ,cost) (:return-style :full-call) (:translate ,fun) (:policy :safe) (:save-p t)) ((:arg x (descriptor-reg any-reg) rdx-offset) (:arg y (descriptor-reg any-reg) rdi-offset) (:res res (descriptor-reg any-reg) rdx-offset) (:temp rax unsigned-reg rax-offset) (:temp rcx unsigned-reg rcx-offset)) (inst mov rcx x) (inst or rcx y) (inst test rcx fixnum-tag-mask) ; both fixnums? (inst jmp :nz DO-STATIC-FUN) ; no - do generic ,@body (inst clc) ; single-value return (inst ret) DO-STATIC-FUN ;; Same as: (inst enter (* n-word-bytes 1)) (inst push rbp-tn) (inst mov rbp-tn rsp-tn) (inst sub rsp-tn (* n-word-bytes 1)) (inst push (make-ea :qword :base rbp-tn :disp (frame-byte-offset return-pc-save-offset))) (inst mov rcx (fixnumize 2)) ; arg count (inst jmp (make-ea :qword :disp (+ nil-value (static-fun-offset ',(symbolicate "TWO-ARG-" fun)))))))) #.` (define-generic-arith-routine (+ 10) (move res x) (inst add res y) (inst jmp :no OKAY) ;; Unbox the overflowed result, recovering the correct sign from ;; the carry flag, then re-box as a bignum. (inst rcr res 1) ,@(when (> n-fixnum-tag-bits 1) ; don't shift by 0 '((inst sar res (1- n-fixnum-tag-bits)))) (move rcx res) (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset)) (storew rcx res bignum-digits-offset other-pointer-lowtag)) OKAY) #.` (define-generic-arith-routine (- 10) (move res x) (inst sub res y) (inst jmp :no OKAY) ;; Unbox the overflowed result, recovering the correct sign from ;; the carry flag, then re-box as a bignum. (inst cmc) ; carry has correct sign now (inst rcr res 1) ,@(when (> n-fixnum-tag-bits 1) ; don't shift by 0 '((inst sar res (1- n-fixnum-tag-bits)))) (move rcx res) (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset)) (storew rcx res bignum-digits-offset other-pointer-lowtag)) OKAY) (define-generic-arith-routine (* 30) (move rax x) ; must use eax for 64-bit result (inst sar rax n-fixnum-tag-bits) ; remove *8 fixnum bias (inst imul y) ; result in edx:eax (inst jmp :no OKAY) ; still fixnum (inst shrd rax x n-fixnum-tag-bits) ; high bits from edx (inst sar x n-fixnum-tag-bits) ; now shift edx too (move rcx x) ; save high bits from cqo (inst cqo) ; edx:eax <- sign-extend of eax (inst cmp x rcx) (inst jmp :e SINGLE-WORD-BIGNUM) (with-fixed-allocation (res bignum-widetag (+ bignum-digits-offset 2)) (storew rax res bignum-digits-offset other-pointer-lowtag) (storew rcx res (1+ bignum-digits-offset) other-pointer-lowtag)) (inst jmp DONE) SINGLE-WORD-BIGNUM (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset)) (storew rax res bignum-digits-offset other-pointer-lowtag)) (inst jmp DONE) OKAY (move res rax) DONE)) ;;;; negation (define-assembly-routine (generic-negate (:cost 10) (:return-style :full-call) (:policy :safe) (:translate %negate) (:save-p t)) ((:arg x (descriptor-reg any-reg) rdx-offset) (:res res (descriptor-reg any-reg) rdx-offset) (:temp rax unsigned-reg rax-offset) (:temp rcx unsigned-reg rcx-offset)) (inst test x fixnum-tag-mask) (inst jmp :z FIXNUM) (inst push rbp-tn) (inst mov rbp-tn rsp-tn) (inst sub rsp-tn (* n-word-bytes 1)) (inst push (make-ea :qword :base rbp-tn :disp (frame-byte-offset return-pc-save-offset))) (inst mov rcx (fixnumize 1)) ; arg count (inst jmp (make-ea :qword :disp (+ nil-value (static-fun-offset '%negate)))) FIXNUM (move res x) (inst neg res) ; (- most-negative-fixnum) is BIGNUM (inst jmp :no OKAY) (inst shr res n-fixnum-tag-bits) ; sign bit is data - remove type bits (move rcx res) (with-fixed-allocation (res bignum-widetag (1+ bignum-digits-offset)) (storew rcx res bignum-digits-offset other-pointer-lowtag)) OKAY) ;;;; comparison (macrolet ((define-cond-assem-rtn (name translate static-fn test) (declare (ignorable translate static-fn)) #+sb-assembling `(define-assembly-routine (,name (:return-style :none)) ((:arg x (descriptor-reg any-reg) rdx-offset) (:arg y (descriptor-reg any-reg) rdi-offset) (:temp rcx unsigned-reg rcx-offset)) (inst mov rcx x) (inst or rcx y) (inst test rcx fixnum-tag-mask) (inst jmp :nz DO-STATIC-FUN) ; are both fixnums? (inst cmp x y) (inst ret) DO-STATIC-FUN (inst push rbp-tn) (inst mov rbp-tn rsp-tn) (inst sub rsp-tn (* n-word-bytes 3)) (inst mov (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset))) rbp-tn) (inst lea rbp-tn (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset)))) (inst mov rcx (fixnumize 2)) (inst call (make-ea :qword :disp (+ nil-value (static-fun-offset ',static-fn)))) ;; HACK: We depend on NIL having the lowest address of all ;; static symbols (including T) ,@(ecase test (:l `((inst mov y (1+ nil-value)) (inst cmp y x))) (:g `((inst cmp x (1+ nil-value))))) (inst pop rbp-tn) (inst ret)) #-sb-assembling `(define-vop (,name) (:translate ,translate) (:policy :safe) (:save-p t) (:args (x :scs (descriptor-reg any-reg) :target rdx) (y :scs (descriptor-reg any-reg) :target rdi)) (:temporary (:sc unsigned-reg :offset rdx-offset :from (:argument 0)) rdx) (:temporary (:sc unsigned-reg :offset rdi-offset :from (:argument 1)) rdi) (:temporary (:sc unsigned-reg :offset rcx-offset :from :eval) rcx) (:conditional ,test) (:generator 10 (move rdx x) (move rdi y) (inst mov rcx (make-fixup ',name :assembly-routine)) (inst call rcx))))) (define-cond-assem-rtn generic-< < two-arg-< :l) (define-cond-assem-rtn generic-> > two-arg-> :g)) #+sb-assembling (define-assembly-routine (generic-eql (:return-style :none)) ((:arg x (descriptor-reg any-reg) rdx-offset) (:arg y (descriptor-reg any-reg) rdi-offset) (:temp rcx unsigned-reg rcx-offset)) (inst mov rcx x) (inst and rcx y) (inst test rcx fixnum-tag-mask) (inst jmp :nz DO-STATIC-FUN) ;; At least one fixnum (inst cmp x y) (inst ret) DO-STATIC-FUN (inst push rbp-tn) (inst mov rbp-tn rsp-tn) (inst sub rsp-tn (* n-word-bytes 3)) (inst mov (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset))) rbp-tn) (inst lea rbp-tn (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset)))) (inst mov rcx (fixnumize 2)) (inst call (make-ea :qword :disp (+ nil-value (static-fun-offset 'eql)))) (load-symbol y t) (inst cmp x y) (inst pop rbp-tn) (inst ret)) #-sb-assembling (define-vop (generic-eql) (:translate eql) (:policy :safe) (:save-p t) (:args (x :scs (descriptor-reg any-reg) :target rdx) (y :scs (descriptor-reg any-reg) :target rdi)) (:temporary (:sc unsigned-reg :offset rdx-offset :from (:argument 0)) rdx) (:temporary (:sc unsigned-reg :offset rdi-offset :from (:argument 1)) rdi) (:temporary (:sc unsigned-reg :offset rcx-offset :from :eval) rcx) (:conditional :e) (:generator 10 (move rdx x) (move rdi y) (inst mov rcx (make-fixup 'generic-eql :assembly-routine)) (inst call rcx))) #+sb-assembling (define-assembly-routine (generic-= (:return-style :none)) ((:arg x (descriptor-reg any-reg) rdx-offset) (:arg y (descriptor-reg any-reg) rdi-offset) (:temp rcx unsigned-reg rcx-offset)) (inst mov rcx x) (inst or rcx y) (inst test rcx fixnum-tag-mask) (inst jmp :nz DO-STATIC-FUN) ;; Both fixnums (inst cmp x y) (inst ret) DO-STATIC-FUN (inst push rbp-tn) (inst mov rbp-tn rsp-tn) (inst sub rsp-tn (* n-word-bytes 3)) (inst mov (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset))) rbp-tn) (inst lea rbp-tn (make-ea :qword :base rsp-tn :disp (frame-byte-offset (+ sp->fp-offset -3 ocfp-save-offset)))) (inst mov rcx (fixnumize 2)) (inst call (make-ea :qword :disp (+ nil-value (static-fun-offset 'two-arg-=)))) (load-symbol y t) (inst cmp x y) (inst pop rbp-tn) (inst ret)) #-sb-assembling (define-vop (generic-=) (:translate =) (:policy :safe) (:save-p t) (:args (x :scs (descriptor-reg any-reg) :target rdx) (y :scs (descriptor-reg any-reg) :target rdi)) (:temporary (:sc unsigned-reg :offset rdx-offset :from (:argument 0)) rdx) (:temporary (:sc unsigned-reg :offset rdi-offset :from (:argument 1)) rdi) (:temporary (:sc unsigned-reg :offset rcx-offset :from :eval) rcx) (:conditional :e) (:generator 10 (move rdx x) (move rdi y) (inst mov rcx (make-fixup 'generic-= :assembly-routine)) (inst call rcx)))