;;;; the VM definition arithmetic VOPs for the Alpha

;;;; 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")

;;;; unary operations

(define-vop (fixnum-unop)
  (:args (x :scs (any-reg)))
  (:results (res :scs (any-reg)))
  (:note "inline fixnum arithmetic")
  (:arg-types tagged-num)
  (:result-types tagged-num)
  (:policy :fast-safe))

(define-vop (signed-unop)
  (:args (x :scs (signed-reg)))
  (:results (res :scs (signed-reg)))
  (:note "inline (signed-byte 64) arithmetic")
  (:arg-types signed-num)
  (:result-types signed-num)
  (:policy :fast-safe))

(define-vop (fast-negate/fixnum fixnum-unop)
  (:translate %negate)
  (:generator 1
    (inst subq zero-tn x res)))

(define-vop (fast-negate/signed signed-unop)
  (:translate %negate)
  (:generator 2
    (inst subq zero-tn x res)))

(define-vop (fast-lognot/fixnum fixnum-unop)
  (:translate lognot)
  (:generator 1
    (inst eqv x fixnum-tag-mask res)))

(define-vop (fast-lognot/signed signed-unop)
  (:translate lognot)
  (:generator 2
    (inst not x res)))

;;;; binary fixnum operations

;;; Assume that any constant operand is the second arg...

(define-vop (fast-fixnum-binop)
  (:args (x :target r :scs (any-reg))
         (y :target r :scs (any-reg)))
  (:arg-types tagged-num tagged-num)
  (:results (r :scs (any-reg)))
  (:result-types tagged-num)
  (:note "inline fixnum arithmetic")
  (:effects)
  (:affected)
  (:policy :fast-safe))

(define-vop (fast-unsigned-binop)
  (:args (x :target r :scs (unsigned-reg))
         (y :target r :scs (unsigned-reg)))
  (:arg-types unsigned-num unsigned-num)
  (:results (r :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:note "inline (unsigned-byte 64) arithmetic")
  (:effects)
  (:affected)
  (:policy :fast-safe))

(define-vop (fast-signed-binop)
  (:args (x :target r :scs (signed-reg))
         (y :target r :scs (signed-reg)))
  (:arg-types signed-num signed-num)
  (:results (r :scs (signed-reg)))
  (:result-types signed-num)
  (:note "inline (signed-byte 64) arithmetic")
  (:effects)
  (:affected)
  (:policy :fast-safe))

(define-vop (fast-fixnum-c-binop fast-fixnum-binop)
  (:args (x :target r :scs (any-reg)))
  (:info y)
  (:arg-types tagged-num (:constant integer)))

(define-vop (fast-signed-c-binop fast-signed-binop)
  (:args (x :target r :scs (signed-reg)))
  (:info y)
  (:arg-types signed-num (:constant integer)))

(define-vop (fast-unsigned-c-binop fast-unsigned-binop)
  (:args (x :target r :scs (unsigned-reg)))
  (:info y)
  (:arg-types unsigned-num (:constant integer)))

(defmacro define-binop (translate cost untagged-cost op
                        tagged-type untagged-type
                        &optional arg-swap restore-fixnum-mask)
  `(progn
     (define-vop (,(symbolicate "FAST-" translate "/FIXNUM=>FIXNUM")
                  fast-fixnum-binop)
       ,@(when restore-fixnum-mask
           `((:temporary (:sc non-descriptor-reg) temp)))
       (:args (x ,@(unless restore-fixnum-mask `(:target r)) :scs (any-reg))
              (y ,@(unless restore-fixnum-mask `(:target r)) :scs (any-reg)))
       (:translate ,translate)
       (:generator ,(1+ cost)
         ,(if arg-swap
              `(inst ,op y x ,(if restore-fixnum-mask 'temp 'r))
              `(inst ,op x y ,(if restore-fixnum-mask 'temp 'r)))
         ,@(when restore-fixnum-mask
             `((inst bic temp #.(ash lowtag-mask -1) r)))))
     (define-vop (,(symbolicate "FAST-" translate "/SIGNED=>SIGNED")
                  fast-signed-binop)
       (:args (x :target r :scs (signed-reg))
              (y :target r :scs (signed-reg)))
       (:translate ,translate)
       (:generator ,(1+ untagged-cost)
         ,(if arg-swap
              `(inst ,op y x r)
              `(inst ,op x y r))))
     (define-vop (,(symbolicate "FAST-" translate "/UNSIGNED=>UNSIGNED")
                  fast-unsigned-binop)
       (:args (x :target r :scs (unsigned-reg))
              (y :target r :scs (unsigned-reg)))
       (:translate ,translate)
       (:generator ,(1+ untagged-cost)
         ,(if arg-swap
              `(inst ,op y x r)
              `(inst ,op x y r))))
     ,@(when (and tagged-type (not arg-swap))
         `((define-vop (,(symbolicate "FAST-" translate "-C/FIXNUM=>FIXNUM")
                        fast-fixnum-c-binop)
             (:args (x ,@(unless restore-fixnum-mask `(:target r))
                       :scs (any-reg)))
             (:arg-types tagged-num (:constant ,tagged-type))
             ,@(when restore-fixnum-mask
                 `((:temporary (:sc non-descriptor-reg) temp)))
             (:translate ,translate)
             (:generator ,cost
                (inst ,op x (fixnumize y) ,(if restore-fixnum-mask 'temp 'r))
                ,@(when restore-fixnum-mask
                    `((inst bic temp #.(ash lowtag-mask -1) r)))))))
     ,@(when (and untagged-type (not arg-swap))
         `((define-vop (,(symbolicate "FAST-" translate "-C/SIGNED=>SIGNED")
                        fast-signed-c-binop)
             (:arg-types signed-num (:constant ,untagged-type))
             (:translate ,translate)
             (:generator ,untagged-cost
                (inst ,op x y r)))
           (define-vop (,(symbolicate "FAST-" translate
                                      "-C/UNSIGNED=>UNSIGNED")
                        fast-unsigned-c-binop)
             (:arg-types unsigned-num (:constant ,untagged-type))
             (:translate ,translate)
             (:generator ,untagged-cost
                (inst ,op x y r)))))))

(define-binop + 1 5 addq (unsigned-byte 6) (unsigned-byte 8))
(define-binop - 1 5 subq (unsigned-byte 6) (unsigned-byte 8))
(define-binop logand 1 3 and (unsigned-byte 6) (unsigned-byte 8))
(define-binop logandc1 1 3 bic (unsigned-byte 6) (unsigned-byte 8) t)
(define-binop logandc2 1 3 bic (unsigned-byte 6) (unsigned-byte 8))
(define-binop logior 1 3 bis (unsigned-byte 6) (unsigned-byte 8))
(define-binop logorc1 1 3 ornot (unsigned-byte 6) (unsigned-byte 8) t t)
(define-binop logorc2 1 3 ornot (unsigned-byte 6) (unsigned-byte 8) nil t)
(define-binop logxor 1 3 xor (unsigned-byte 6) (unsigned-byte 8))
(define-binop logeqv 1 3 eqv (unsigned-byte 6) (unsigned-byte 8) nil t)

;;; special cases for LOGAND where we can use a mask operation
(define-vop (fast-logand-c-mask/unsigned=>unsigned fast-unsigned-c-binop)
  (:translate logand)
  (:arg-types unsigned-num
              (:constant (or (integer #xffffffff #xffffffff)
                             (integer #xffffffff00000000 #xffffffff00000000))))
  (:generator 1
    (ecase y
      (#xffffffff (inst mskll x 4 r))
      (#xffffffff00000000 (inst mskll x 0 r)))))

;;;; shifting

(define-vop (fast-ash/unsigned=>unsigned)
  (:note "inline ASH")
  (:args (number :scs (unsigned-reg) :to :save)
         (amount :scs (signed-reg)))
  (:arg-types unsigned-num signed-num)
  (:results (result :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:translate ash)
  (:policy :fast-safe)
  (:temporary (:sc non-descriptor-reg) ndesc)
  (:temporary (:sc non-descriptor-reg) temp)
  (:generator 3
    (inst bge amount positive)
    (inst subq zero-tn amount ndesc)
    (inst cmplt ndesc 64 temp)
    (inst srl number ndesc result)
    ;; FIXME: this looks like a candidate for a conditional move --
    ;; CSR, 2003-09-10
    (inst bne temp done)
    (move zero-tn result)
    (inst br zero-tn done)

    POSITIVE
    (inst sll number amount result)

    DONE))

(define-vop (fast-ash/signed=>signed)
  (:note "inline ASH")
  (:args (number :scs (signed-reg) :to :save)
         (amount :scs (signed-reg)))
  (:arg-types signed-num signed-num)
  (:results (result :scs (signed-reg)))
  (:result-types signed-num)
  (:translate ash)
  (:policy :fast-safe)
  (:temporary (:sc non-descriptor-reg) ndesc)
  (:temporary (:sc non-descriptor-reg) temp)
  (:generator 3
    (inst bge amount positive)
    (inst subq zero-tn amount ndesc)
    (inst cmplt ndesc 63 temp)
    (inst sra number ndesc result)
    (inst bne temp done)
    (inst sra number 63 result)
    (inst br zero-tn done)

    POSITIVE
    (inst sll number amount result)

    DONE))

(define-vop (fast-ash-c/signed=>signed)
  (:policy :fast-safe)
  (:translate ash)
  (:note nil)
  (:args (number :scs (signed-reg)))
  (:info count)
  (:arg-types signed-num (:constant integer))
  (:results (result :scs (signed-reg)))
  (:result-types signed-num)
  (:generator 1
    (cond
      ((< count 0) (inst sra number (min 63 (- count)) result))
      ((> count 0) (inst sll number (min 63 count) result))
      (t (bug "identity ASH not transformed away")))))

(define-vop (fast-ash-c/unsigned=>unsigned)
  (:policy :fast-safe)
  (:translate ash)
  (:note nil)
  (:args (number :scs (unsigned-reg)))
  (:info count)
  (:arg-types unsigned-num (:constant integer))
  (:results (result :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:generator 1
    (cond
      ((< count -63) (move zero-tn result))
      ((< count 0) (inst sra number (- count) result))
      ((> count 0) (inst sll number (min 63 count) result))
      (t (bug "identity ASH not transformed away")))))

(macrolet ((def (name sc-type type result-type cost)
             `(define-vop (,name)
                (:note "inline ASH")
                (:translate ash)
                (:args (number :scs (,sc-type))
                       (amount :scs (signed-reg unsigned-reg immediate)))
                (:arg-types ,type positive-fixnum)
                (:results (result :scs (,result-type)))
                (:result-types ,type)
                (:policy :fast-safe)
                (:generator ,cost
                   (sc-case amount
                     ((signed-reg unsigned-reg)
                      (inst sll number amount result))
                     (immediate
                      (let ((amount (tn-value amount)))
                        (aver (> amount 0))
                        (inst sll number amount result))))))))
  (def fast-ash-left/fixnum=>fixnum any-reg tagged-num any-reg 2)
  (def fast-ash-left/signed=>signed signed-reg signed-num signed-reg 3)
  (def fast-ash-left/unsigned=>unsigned unsigned-reg unsigned-num unsigned-reg 3))

(define-vop (signed-byte-64-len)
  (:translate integer-length)
  (:note "inline (signed-byte 64) integer-length")
  (:policy :fast-safe)
  (:args (arg :scs (signed-reg) :to (:argument 1)))
  (:arg-types signed-num)
  (:results (res :scs (any-reg)))
  (:result-types positive-fixnum)
  (:temporary (:scs (non-descriptor-reg) :from (:argument 0)) shift)
  (:generator 30
    (inst not arg shift)
    (inst cmovge arg arg shift)
    (inst subq zero-tn (fixnumize 1) res)
    (inst sll shift 1 shift)
    LOOP
    (inst addq res (fixnumize 1) res)
    (inst srl shift 1 shift)
    (inst bne shift loop)))

(define-vop (unsigned-byte-64-count)
  (:translate logcount)
  (:note "inline (unsigned-byte 64) logcount")
  (:policy :fast-safe)
  (:args (arg :scs (unsigned-reg)))
  (:arg-types unsigned-num)
  (:results (res :scs (unsigned-reg)))
  (:result-types positive-fixnum)
  (:guard (member :cix *backend-subfeatures*))
  (:generator 1
    (inst ctpop zero-tn arg res)))

(define-vop (unsigned-byte-64-count)
  (:translate logcount)
  (:note "inline (unsigned-byte 64) logcount")
  (:policy :fast-safe)
  (:args (arg :scs (unsigned-reg) :target num))
  (:arg-types unsigned-num)
  (:results (res :scs (unsigned-reg)))
  (:result-types positive-fixnum)
  (:temporary (:scs (non-descriptor-reg) :from (:argument 0) :to (:result 0)
                    :target res) num)
  (:temporary (:scs (non-descriptor-reg)) mask temp)
  (:generator 60
    ;; FIXME: now this looks expensive, what with these 64bit loads.
    ;; Maybe a loop and count would be faster?  -- CSR, 2003-09-10
    (inst li #x5555555555555555 mask)
    (inst srl arg 1 temp)
    (inst and arg mask num)
    (inst and temp mask temp)
    (inst addq num temp num)
    (inst li #x3333333333333333 mask)
    (inst srl num 2 temp)
    (inst and num mask num)
    (inst and temp mask temp)
    (inst addq num temp num)
    (inst li #x0f0f0f0f0f0f0f0f mask)
    (inst srl num 4 temp)
    (inst and num mask num)
    (inst and temp mask temp)
    (inst addq num temp num)
    (inst li #x00ff00ff00ff00ff mask)
    (inst srl num 8 temp)
    (inst and num mask num)
    (inst and temp mask temp)
    (inst addq num temp num)
    (inst li #x0000ffff0000ffff mask)
    (inst srl num 16 temp)
    (inst and num mask num)
    (inst and temp mask temp)
    (inst addq num temp num)
    (inst li #x00000000ffffffff mask)
    (inst srl num 32 temp)
    (inst and num mask num)
    (inst and temp mask temp)
    (inst addq num temp res)))

;;;; multiplying

(define-vop (fast-*/fixnum=>fixnum fast-fixnum-binop)
  (:temporary (:scs (non-descriptor-reg)) temp)
  (:translate *)
  (:generator 4
    (inst sra y n-fixnum-tag-bits temp)
    (inst mulq x temp r)))

(define-vop (fast-*/signed=>signed fast-signed-binop)
  (:translate *)
  (:generator 3
    (inst mulq x y r)))

(define-vop (fast-*/unsigned=>unsigned fast-unsigned-binop)
  (:translate *)
  (:generator 3
    (inst mulq x y r)))

;;;; Modular functions:
(define-modular-fun lognot-mod64 (x) lognot :untagged nil 64)
(define-vop (lognot-mod64/unsigned=>unsigned)
  (:translate lognot-mod64)
  (:args (x :scs (unsigned-reg)))
  (:arg-types unsigned-num)
  (:results (res :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:policy :fast-safe)
  (:generator 1
    (inst not x res)))

(define-vop (fast-ash-left-mod64-c/unsigned=>unsigned
             fast-ash-c/unsigned=>unsigned)
  (:translate ash-left-mod64))
(define-vop (fast-ash-left-mod64/unsigned=>unsigned
             fast-ash-left/unsigned=>unsigned))
(deftransform ash-left-mod64 ((integer count)
                              ((unsigned-byte 64) (unsigned-byte 6)))
  (when (sb!c::constant-lvar-p count)
    (sb!c::give-up-ir1-transform))
  '(%primitive fast-ash-left-mod64/unsigned=>unsigned integer count))

(macrolet
    ((define-modular-backend (fun &optional constantp)
       (let ((mfun-name (symbolicate fun '-mod64))
             (modvop (symbolicate 'fast- fun '-mod64/unsigned=>unsigned))
             (modcvop (symbolicate 'fast- fun '-mod64-c/unsigned=>unsigned))
             (vop (symbolicate 'fast- fun '/unsigned=>unsigned))
             (cvop (symbolicate 'fast- fun '-c/unsigned=>unsigned)))
         `(progn
            (define-modular-fun ,mfun-name (x y) ,fun :untagged nil 64)
            (define-vop (,modvop ,vop)
              (:translate ,mfun-name))
            ,@(when constantp
                `((define-vop (,modcvop ,cvop)
                    (:translate ,mfun-name))))))))
  (define-modular-backend + t)
  (define-modular-backend - t)
  (define-modular-backend logeqv t)
  (define-modular-backend logandc1)
  (define-modular-backend logandc2 t)
  (define-modular-backend logorc1)
  (define-modular-backend logorc2 t))

(define-source-transform lognand (x y)
  `(lognot (logand ,x ,y)))
(define-source-transform lognor (x y)
  `(lognot (logior ,x ,y)))

;;;; binary conditional VOPs

(define-vop (fast-conditional)
  (:conditional)
  (:info target not-p)
  (:effects)
  (:affected)
  (:temporary (:scs (non-descriptor-reg)) temp)
  (:policy :fast-safe))

(define-vop (fast-conditional/fixnum fast-conditional)
  (:args (x :scs (any-reg))
         (y :scs (any-reg)))
  (:arg-types tagged-num tagged-num)
  (:note "inline fixnum comparison"))

(define-vop (fast-conditional-c/fixnum fast-conditional/fixnum)
  (:args (x :scs (any-reg)))
  (:arg-types tagged-num (:constant (unsigned-byte-with-a-bite-out 6 4)))
  (:info target not-p y))

(define-vop (fast-conditional/signed fast-conditional)
  (:args (x :scs (signed-reg))
         (y :scs (signed-reg)))
  (:arg-types signed-num signed-num)
  (:note "inline (signed-byte 64) comparison"))

(define-vop (fast-conditional-c/signed fast-conditional/signed)
  (:args (x :scs (signed-reg)))
  (:arg-types signed-num (:constant (unsigned-byte-with-a-bite-out 8 1)))
  (:info target not-p y))

(define-vop (fast-conditional/unsigned fast-conditional)
  (:args (x :scs (unsigned-reg))
         (y :scs (unsigned-reg)))
  (:arg-types unsigned-num unsigned-num)
  (:note "inline (unsigned-byte 64) comparison"))

(define-vop (fast-conditional-c/unsigned fast-conditional/unsigned)
  (:args (x :scs (unsigned-reg)))
  (:arg-types unsigned-num (:constant (unsigned-byte-with-a-bite-out 8 1)))
  (:info target not-p y))


(defmacro define-conditional-vop (translate &rest generator)
  `(progn
     ,@(mapcar (lambda (suffix cost signed)
                 (unless (and (member suffix '(/fixnum -c/fixnum))
                              (eq translate 'eql))
                   `(define-vop (,(intern (format nil "~:@(FAST-IF-~A~A~)"
                                                  translate suffix))
                                 ,(intern
                                   (format nil "~:@(FAST-CONDITIONAL~A~)"
                                           suffix)))
                      (:translate ,translate)
                      (:generator ,cost
                                  (let* ((signed ,signed)
                                         (-c/fixnum ,(eq suffix '-c/fixnum))
                                         (y (if -c/fixnum (fixnumize y) y)))
                                    ,@generator)))))
               '(/fixnum -c/fixnum /signed -c/signed /unsigned -c/unsigned)
               '(3 2 5 4 5 4)
               '(t t t t nil nil))))

(define-conditional-vop <
  (cond ((and signed (eql y 0))
         (if not-p
             (inst bge x target)
             (inst blt x target)))
        (t
         (if signed
             (inst cmplt x y temp)
             (inst cmpult x y temp))
         (if not-p
             (inst beq temp target)
             (inst bne temp target)))))

(define-conditional-vop >
  (cond ((and signed (eql y 0))
         (if not-p
             (inst ble x target)
             (inst bgt x target)))
        ((integerp y)
         (let ((y (+ y (if -c/fixnum (fixnumize 1) 1))))
           (if signed
               (inst cmplt x y temp)
               (inst cmpult x y temp))
           (if not-p
               (inst bne temp target)
               (inst beq temp target))))
        (t
         (if signed
             (inst cmplt y x temp)
             (inst cmpult y x temp))
         (if not-p
             (inst beq temp target)
             (inst bne temp target)))))

;;; EQL/FIXNUM is funny because the first arg can be of any type, not
;;; just a known fixnum.

(define-conditional-vop eql
  (declare (ignore signed))
  (when (integerp y)
    (inst li y temp)
    (setf y temp))
  (inst cmpeq x y temp)
  (if not-p
      (inst beq temp target)
      (inst bne temp target)))

;;; These versions specify a fixnum restriction on their first arg. We
;;; have also generic-eql/fixnum VOPs which are the same, but have no
;;; restriction on the first arg and a higher cost. The reason for
;;; doing this is to prevent fixnum specific operations from being
;;; used on word integers, spuriously consing the argument.
(define-vop (fast-eql/fixnum fast-conditional)
  (:args (x :scs (any-reg))
         (y :scs (any-reg)))
  (:arg-types tagged-num tagged-num)
  (:note "inline fixnum comparison")
  (:translate eql)
  (:generator 3
    (cond ((equal y zero-tn)
           (if not-p
               (inst bne x target)
               (inst beq x target)))
          (t
           (inst cmpeq x y temp)
           (if not-p
               (inst beq temp target)
               (inst bne temp target))))))

;;;
(define-vop (generic-eql/fixnum fast-eql/fixnum)
  (:args (x :scs (any-reg descriptor-reg))
         (y :scs (any-reg)))
  (:arg-types * tagged-num)
  (:variant-cost 7))

(define-vop (fast-eql-c/fixnum fast-conditional/fixnum)
  (:args (x :scs (any-reg)))
  (:arg-types tagged-num (:constant (signed-byte 6)))
  (:temporary (:scs (non-descriptor-reg)) temp)
  (:info target not-p y)
  (:translate eql)
  (:generator 2
    (let ((y (cond ((eql y 0) zero-tn)
                   (t
                    (inst li (fixnumize y) temp)
                    temp))))
      (inst cmpeq x y temp)
      (if not-p
          (inst beq temp target)
          (inst bne temp target)))))
;;;
(define-vop (generic-eql-c/fixnum fast-eql-c/fixnum)
  (:args (x :scs (any-reg descriptor-reg)))
  (:arg-types * (:constant (signed-byte 6)))
  (:variant-cost 6))


;;;; 32-bit logical operations

(define-vop (shift-towards-someplace)
  (:policy :fast-safe)
  (:args (num :scs (unsigned-reg))
         (amount :scs (signed-reg)))
  (:arg-types unsigned-num tagged-num)
  (:results (r :scs (unsigned-reg)))
  (:result-types unsigned-num))

(define-vop (shift-towards-start shift-towards-someplace)
  (:translate shift-towards-start)
  (:note "SHIFT-TOWARDS-START")
  (:temporary (:sc non-descriptor-reg) temp)
  (:generator 1
    (inst and amount #x1f temp)
    (inst srl num temp r)))

(define-vop (shift-towards-end shift-towards-someplace)
  (:translate shift-towards-end)
  (:note "SHIFT-TOWARDS-END")
  (:temporary (:sc non-descriptor-reg) temp)
  (:generator 1
    (inst and amount #x1f temp)
    (inst sll num temp r)))

;;;; bignum stuff

(define-vop (bignum-length get-header-data)
  (:translate sb!bignum:%bignum-length)
  (:policy :fast-safe))

(define-vop (bignum-set-length set-header-data)
  (:translate sb!bignum:%bignum-set-length)
  (:policy :fast-safe))

(define-full-reffer bignum-ref * bignum-digits-offset other-pointer-lowtag
  (unsigned-reg) unsigned-num sb!bignum:%bignum-ref)

(define-full-setter bignum-set * bignum-digits-offset other-pointer-lowtag
  (unsigned-reg) unsigned-num sb!bignum:%bignum-set #!+gengc nil)

(define-vop (digit-0-or-plus)
  (:translate sb!bignum:%digit-0-or-plusp)
  (:policy :fast-safe)
  (:args (digit :scs (unsigned-reg)))
  (:arg-types unsigned-num)
  (:temporary (:sc non-descriptor-reg) temp)
  (:conditional)
  (:info target not-p)
  (:generator 2
    (inst sll digit 32 temp)
    (if not-p
        (inst blt temp target)
        (inst bge temp target))))

(define-vop (add-w/carry)
  (:translate sb!bignum:%add-with-carry)
  (:policy :fast-safe)
  (:args (a :scs (unsigned-reg))
         (b :scs (unsigned-reg))
         (c :scs (unsigned-reg)))
  (:arg-types unsigned-num unsigned-num positive-fixnum)
  (:results (result :scs (unsigned-reg) :from :load)
            (carry :scs (unsigned-reg) :from :eval))
  (:result-types unsigned-num positive-fixnum)
  (:generator 5
    (inst addq a b result)
    (inst addq result c result)
    (inst sra result 32 carry)
    (inst mskll result 4 result)))

(define-vop (sub-w/borrow)
  (:translate sb!bignum:%subtract-with-borrow)
  (:policy :fast-safe)
  (:args (a :scs (unsigned-reg))
         (b :scs (unsigned-reg))
         (c :scs (unsigned-reg)))
  (:arg-types unsigned-num unsigned-num positive-fixnum)
  (:results (result :scs (unsigned-reg) :from :load)
            (borrow :scs (unsigned-reg) :from :eval))
  (:result-types unsigned-num positive-fixnum)
  (:generator 4
    (inst xor c 1 result)
    (inst subq a result result)
    (inst subq result b result)
    (inst srl result 63 borrow)
    (inst xor borrow 1 borrow)
    (inst mskll result 4 result)))

(define-vop (bignum-mult-and-add-3-arg)
  (:translate sb!bignum:%multiply-and-add)
  (:policy :fast-safe)
  (:args (x :scs (unsigned-reg))
         (y :scs (unsigned-reg))
         (carry-in :scs (unsigned-reg) :to :save))
  (:arg-types unsigned-num unsigned-num unsigned-num)
  (:results (hi :scs (unsigned-reg))
            (lo :scs (unsigned-reg)))
  (:result-types unsigned-num unsigned-num)
  (:generator 6
    (inst mulq x y lo)
    (inst addq lo carry-in lo)
    (inst srl lo 32 hi)
    (inst mskll lo 4 lo)))


(define-vop (bignum-mult-and-add-4-arg)
  (:translate sb!bignum:%multiply-and-add)
  (:policy :fast-safe)
  (:args (x :scs (unsigned-reg))
         (y :scs (unsigned-reg))
         (prev :scs (unsigned-reg))
         (carry-in :scs (unsigned-reg) :to :save))
  (:arg-types unsigned-num unsigned-num unsigned-num unsigned-num)
  (:results (hi :scs (unsigned-reg))
            (lo :scs (unsigned-reg)))
  (:result-types unsigned-num unsigned-num)
  (:generator 9
    (inst mulq x y lo)
    (inst addq lo prev lo)
    (inst addq lo carry-in lo)
    (inst srl lo 32 hi)
    (inst mskll lo 4 lo)))

(define-vop (bignum-mult)
  (:translate sb!bignum:%multiply)
  (:policy :fast-safe)
  (:args (x :scs (unsigned-reg))
         (y :scs (unsigned-reg)))
  (:arg-types unsigned-num unsigned-num)
  (:results (hi :scs (unsigned-reg))
            (lo :scs (unsigned-reg)))
  (:result-types unsigned-num unsigned-num)
  (:generator 3
    (inst mulq x y lo)
    (inst srl lo 32 hi)
    (inst mskll lo 4 lo)))

(define-vop (bignum-lognot)
  (:translate sb!bignum:%lognot)
  (:policy :fast-safe)
  (:args (x :scs (unsigned-reg)))
  (:arg-types unsigned-num)
  (:results (r :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:generator 1
    (inst not x r)
    (inst mskll r 4 r)))

(define-vop (fixnum-to-digit)
  (:translate sb!bignum:%fixnum-to-digit)
  (:policy :fast-safe)
  (:args (fixnum :scs (any-reg)))
  (:arg-types tagged-num)
  (:results (digit :scs (unsigned-reg)))
  (:result-types unsigned-num)
  (:generator 1
    (inst sra fixnum n-fixnum-tag-bits digit)))

(define-vop (bignum-floor)
  (:translate sb!bignum:%bigfloor)
  (:policy :fast-safe)
  (:args (num-high :scs (unsigned-reg))
         (num-low :scs (unsigned-reg))
         (denom-arg :scs (unsigned-reg) :target denom))
  (:arg-types unsigned-num unsigned-num unsigned-num)
  (:temporary (:scs (unsigned-reg) :from (:argument 2)) denom)
  (:temporary (:scs (unsigned-reg) :from (:eval 0)) temp)
  (:results (quo :scs (unsigned-reg) :from (:eval 0))
            (rem :scs (unsigned-reg) :from (:argument 0)))
  (:result-types unsigned-num unsigned-num)
  (:generator 325 ; number of inst assuming targeting works.
    (inst sll num-high 32 rem)
    (inst bis rem num-low rem)
    (inst sll denom-arg 32 denom)
    (inst cmpule denom rem quo)
    (inst beq quo shift1)
    (inst subq rem denom rem)
    SHIFT1
    (dotimes (i 32)
      (let ((shift2 (gen-label)))
        (inst srl denom 1 denom)
        (inst cmpule denom rem temp)
        (inst sll quo 1 quo)
        (inst beq temp shift2)
        (inst subq rem denom rem)
        (inst bis quo 1 quo)
        (emit-label shift2)))))

(define-vop (signify-digit)
  (:translate sb!bignum:%fixnum-digit-with-correct-sign)
  (:policy :fast-safe)
  (:args (digit :scs (unsigned-reg) :target res))
  (:arg-types unsigned-num)
  (:results (res :scs (any-reg signed-reg)))
  (:result-types signed-num)
  (:generator 2
    (sc-case res
      (any-reg
       (inst sll digit 34 res)
       (inst sra res 32 res))
      (signed-reg
       (inst sll digit 32 res)
       (inst sra res 32 res)))))


(define-vop (digit-ashr)
  (:translate sb!bignum:%ashr)
  (:policy :fast-safe)
  (:args (digit :scs (unsigned-reg))
         (count :scs (unsigned-reg)))
  (:arg-types unsigned-num positive-fixnum)
  (:results (result :scs (unsigned-reg) :from (:argument 0)))
  (:result-types unsigned-num)
  (:generator 1
    (inst sll digit 32 result)
    (inst sra result count result)
    (inst srl result 32 result)))

(define-vop (digit-lshr digit-ashr)
  (:translate sb!bignum:%digit-logical-shift-right)
  (:generator 1
    (inst srl digit count result)))

(define-vop (digit-ashl digit-ashr)
  (:translate sb!bignum:%ashl)
  (:generator 1
    (inst sll digit count result)))

;;;; static functions

(define-static-fun two-arg-gcd (x y) :translate gcd)
(define-static-fun two-arg-lcm (x y) :translate lcm)

(define-static-fun two-arg-+ (x y) :translate +)
(define-static-fun two-arg-- (x y) :translate -)
(define-static-fun two-arg-* (x y) :translate *)
(define-static-fun two-arg-/ (x y) :translate /)

(define-static-fun two-arg-< (x y) :translate <)
(define-static-fun two-arg-<= (x y) :translate <=)
(define-static-fun two-arg-> (x y) :translate >)
(define-static-fun two-arg->= (x y) :translate >=)
(define-static-fun two-arg-= (x y) :translate =)
(define-static-fun two-arg-/= (x y) :translate /=)

(define-static-fun %negate (x) :translate %negate)

(define-static-fun two-arg-and (x y) :translate logand)
(define-static-fun two-arg-ior (x y) :translate logior)
(define-static-fun two-arg-xor (x y) :translate logxor)
(define-static-fun two-arg-eqv (x y) :translate logeqv)
