X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fx86-64%2Farith.lisp;h=922962632595cac1b7a0356b6575adcab7a2f7c8;hb=91a5cbf7375439309fede4776d8debc7a132dc20;hp=4878c68af138ea5ea25fab1d64862f287b972544;hpb=18775b5e3c9a75f5301e09ddef649f2f35ab9752;p=sbcl.git diff --git a/src/compiler/x86-64/arith.lisp b/src/compiler/x86-64/arith.lisp index 4878c68..9229626 100644 --- a/src/compiler/x86-64/arith.lisp +++ b/src/compiler/x86-64/arith.lisp @@ -11,6 +11,26 @@ (in-package "SB!VM") + +;; If chopping X to 32 bits and sign-extending is equal to the original X, +;; return the chopped X, which the CPU will always treat as signed. +;; Notably this allows MOST-POSITIVE-WORD to be an immediate constant. +(defun immediate32-p (x) + (typecase x + ((signed-byte 32) x) + ((unsigned-byte 64) + (let ((chopped (sb!c::mask-signed-field 32 x))) + (and (= x (ldb (byte 64 0) chopped)) + chopped))) + (t nil))) + +;; If 'immediate32-p' is true, use it; otherwise use a RIP-relative constant. +;; I couldn't think of a more accurate name for this other than maybe +;; 'signed-immediate32-or-rip-relativize' which is just too awful. +(defun constantize (x) + (or (immediate32-p x) + (register-inline-constant :qword x))) + ;;;; unary operations (define-vop (fast-safe-arith-op) @@ -109,98 +129,110 @@ (:note "inline (signed-byte 64) arithmetic")) (define-vop (fast-fixnum-binop-c fast-safe-arith-op) - (:args (x :target r :scs (any-reg) - :load-if (or (not (typep y '(signed-byte 29))) - (not (sc-is x any-reg control-stack))))) + (:args (x :target r :scs (any-reg) :load-if t)) (:info y) (:arg-types tagged-num (:constant fixnum)) - (:results (r :scs (any-reg) - :load-if (or (not (location= x r)) - (not (typep y '(signed-byte 29)))))) + (:results (r :scs (any-reg) :load-if t)) (:result-types tagged-num) (:note "inline fixnum arithmetic")) (define-vop (fast-unsigned-binop-c fast-safe-arith-op) - (:args (x :target r :scs (unsigned-reg) - :load-if (or (not (typep y '(unsigned-byte 31))) - (not (sc-is x unsigned-reg unsigned-stack))))) + (:args (x :target r :scs (unsigned-reg) :load-if t)) (:info y) (:arg-types unsigned-num (:constant (unsigned-byte 64))) - (:results (r :scs (unsigned-reg) - :load-if (or (not (location= x r)) - (not (typep y '(unsigned-byte 31)))))) + (:results (r :scs (unsigned-reg) :load-if t)) (:result-types unsigned-num) (:note "inline (unsigned-byte 64) arithmetic")) (define-vop (fast-signed-binop-c fast-safe-arith-op) - (:args (x :target r :scs (signed-reg) - :load-if (or (not (typep y '(signed-byte 32))) - (not (sc-is x signed-reg signed-stack))))) + (:args (x :target r :scs (signed-reg) :load-if t)) (:info y) (:arg-types signed-num (:constant (signed-byte 64))) - (:results (r :scs (signed-reg) - :load-if (or (not (location= x r)) - (not (typep y '(signed-byte 32)))))) + (:results (r :scs (signed-reg) :load-if t)) (:result-types signed-num) (:note "inline (signed-byte 64) arithmetic")) -(macrolet ((define-binop (translate untagged-penalty op) +(macrolet ((define-binop (translate untagged-penalty op + &key fixnum=>fixnum c/fixnum=>fixnum + signed=>signed c/signed=>signed + unsigned=>unsigned c/unsigned=>unsigned) + `(progn (define-vop (,(symbolicate "FAST-" translate "/FIXNUM=>FIXNUM") fast-fixnum-binop) (:translate ,translate) (:generator 2 - (move r x) - (inst ,op r y))) + ,@(or fixnum=>fixnum `((move r x) (inst ,op r y))))) (define-vop (,(symbolicate 'fast- translate '-c/fixnum=>fixnum) fast-fixnum-binop-c) (:translate ,translate) (:generator 1 - (move r x) - (inst ,op r (if (typep y '(signed-byte 29)) - (fixnumize y) - (register-inline-constant :qword (fixnumize y)))))) + ,@(or c/fixnum=>fixnum + `((move r x) + (inst ,op r (constantize (fixnumize y))))))) (define-vop (,(symbolicate "FAST-" translate "/SIGNED=>SIGNED") fast-signed-binop) (:translate ,translate) (:generator ,(1+ untagged-penalty) - (move r x) - (inst ,op r y))) + ,@(or signed=>signed `((move r x) (inst ,op r y))))) (define-vop (,(symbolicate 'fast- translate '-c/signed=>signed) fast-signed-binop-c) (:translate ,translate) (:generator ,untagged-penalty - (move r x) - (inst ,op r (if (typep y '(signed-byte 32)) - y - (register-inline-constant :qword y))))) + ,@(or c/signed=>signed + `((move r x) (inst ,op r (constantize y)))))) (define-vop (,(symbolicate "FAST-" translate "/UNSIGNED=>UNSIGNED") - fast-unsigned-binop) + fast-unsigned-binop) (:translate ,translate) (:generator ,(1+ untagged-penalty) - (move r x) - (inst ,op r y))) + ,@(or unsigned=>unsigned `((move r x) (inst ,op r y))))) (define-vop (,(symbolicate 'fast- translate '-c/unsigned=>unsigned) fast-unsigned-binop-c) (:translate ,translate) (:generator ,untagged-penalty - (move r x) - (inst ,op r (if (typep y '(unsigned-byte 31)) - y - (register-inline-constant :qword y)))))))) + ,@(or c/unsigned=>unsigned + `((move r x) (inst ,op r (constantize y))))))))) ;;(define-binop + 4 add) (define-binop - 4 sub) - (define-binop logand 2 and) - (define-binop logior 2 or) - (define-binop logxor 2 xor)) + + ;; The following have microoptimizations for some special cases + ;; not caught by the front end. + + (define-binop logand 2 and + :c/unsigned=>unsigned + ((move r x) + (let ((y (constantize y))) + ;; ANDing with #xFFFF_FFFF_FFFF_FFFF is a no-op, other than + ;; the eflags state which we don't care about. + (unless (eql y -1) ; do nothing if this is true + (inst and r y))))) + + (define-binop logior 2 or + :c/unsigned=>unsigned + ((let ((y (constantize y))) + (cond ((and (register-p r) (eql y -1)) ; special-case "OR reg, all-ones" + ;; I have yet to elicit this case. Can it happen? + (inst mov r -1)) + (t + (move r x) + (inst or r y)))))) + + (define-binop logxor 2 xor + :c/unsigned=>unsigned + ((move r x) + (let ((y (constantize y))) + (if (eql y -1) ; special-case "XOR reg, [all-ones]" + (inst not r) + (inst xor r y)))))) ;;; Special handling of add on the x86; can use lea to avoid a ;;; register load, otherwise it uses add. +;;; FIXME: either inherit from fast-foo-binop or explain why not. (define-vop (fast-+/fixnum=>fixnum fast-safe-arith-op) (:translate +) (:args (x :scs (any-reg) :target r @@ -227,26 +259,20 @@ (define-vop (fast-+-c/fixnum=>fixnum fast-safe-arith-op) (:translate +) - (:args (x :target r :scs (any-reg) - :load-if (or (not (typep y '(signed-byte 29))) - (not (sc-is x any-reg control-stack))))) + (:args (x :target r :scs (any-reg) :load-if t)) (:info y) (:arg-types tagged-num (:constant fixnum)) - (:results (r :scs (any-reg) - :load-if (or (not (location= x r)) - (not (typep y '(signed-byte 29)))))) + (:results (r :scs (any-reg) :load-if t)) (:result-types tagged-num) (:note "inline fixnum arithmetic") (:generator 1 - (cond ((and (sc-is x any-reg) (sc-is r any-reg) (not (location= x r)) - (typep y '(signed-byte 29))) - (inst lea r (make-ea :qword :base x :disp (fixnumize y)))) - ((typep y '(signed-byte 29)) - (move r x) - (inst add r (fixnumize y))) - (t - (move r x) - (inst add r (register-inline-constant :qword (fixnumize y))))))) + (let ((y (fixnumize y))) + (cond ((and (not (location= x r)) + (typep y '(signed-byte 32))) + (inst lea r (make-ea :qword :base x :disp y))) + (t + (move r x) + (inst add r (constantize y))))))) (define-vop (fast-+/signed=>signed fast-safe-arith-op) (:translate +) @@ -271,7 +297,6 @@ (move r x) (inst add r y))))) - ;;;; Special logand cases: (logand signed unsigned) => unsigned (define-vop (fast-logand/signed-unsigned=>unsigned @@ -284,11 +309,12 @@ (y :scs (unsigned-reg unsigned-stack))) (:arg-types signed-num unsigned-num)) +;; This special case benefits from the special case for c/unsigned=>unsigned. +;; In particular, converting a (signed-byte 64) to (unsigned-byte 64) by +;; way of (LDB (byte 64 0)) doesn't need an AND instruction. (define-vop (fast-logand-c/signed-unsigned=>unsigned fast-logand-c/unsigned=>unsigned) - (:args (x :target r :scs (signed-reg) - :load-if (or (not (typep y '(unsigned-byte 31))) - (not (sc-is r signed-reg signed-stack))))) + (:args (x :target r :scs (signed-reg))) (:arg-types signed-num (:constant (unsigned-byte 64)))) (define-vop (fast-logand/unsigned-signed=>unsigned @@ -323,10 +349,8 @@ (move r x) (cond ((= y 1) (inst inc r)) - ((typep y '(signed-byte 32)) - (inst add r y)) (t - (inst add r (register-inline-constant :qword y)))))))) + (inst add r (constantize y)))))))) (define-vop (fast-+/unsigned=>unsigned fast-safe-arith-op) (:translate +) @@ -373,10 +397,8 @@ (move r x) (cond ((= y 1) (inst inc r)) - ((typep y '(unsigned-byte 31)) - (inst add r y)) (t - (inst add r (register-inline-constant :qword y)))))))) + (inst add r (constantize y)))))))) ;;;; multiplication and division @@ -535,9 +557,7 @@ (:generator 30 (move eax x) (inst cqo) - (if (typep y '(signed-byte 29)) - (inst mov y-arg (fixnumize y)) - (setf y-arg (register-inline-constant :qword (fixnumize y)))) + (inst mov y-arg (fixnumize y)) (inst idiv eax y-arg) (if (location= quo eax) (inst shl eax n-fixnum-tag-bits) @@ -593,9 +613,7 @@ (:generator 32 (move eax x) (inst xor edx edx) - (if (typep y '(unsigned-byte 31)) - (inst mov y-arg y) - (setf y-arg (register-inline-constant :qword y))) + (inst mov y-arg y) (inst div eax y-arg) (move quo eax) (move rem edx))) @@ -646,9 +664,7 @@ (:generator 32 (move eax x) (inst cqo) - (if (typep y '(signed-byte 32)) - (inst mov y-arg y) - (setf y-arg (register-inline-constant :qword y))) + (inst mov y-arg y) (inst idiv eax y-arg) (move quo eax) (move rem edx))) @@ -840,7 +856,7 @@ constant shift greater than word length"))) (:generator 5 (move result number) (move ecx amount) - (inst or ecx ecx) + (inst test ecx ecx) (inst jmp :ns POSITIVE) (inst neg ecx) (inst cmp ecx 63) @@ -869,7 +885,7 @@ constant shift greater than word length"))) (:generator 5 (move result number) (move ecx amount) - (inst or ecx ecx) + (inst test ecx ecx) (inst jmp :ns POSITIVE) (inst neg ecx) (inst cmp ecx 63) @@ -886,6 +902,52 @@ constant shift greater than word length"))) DONE)) +#!+ash-right-vops +(define-vop (fast-%ash/right/unsigned) + (:translate %ash/right) + (:policy :fast-safe) + (:args (number :scs (unsigned-reg) :target result) + (amount :scs (unsigned-reg) :target rcx)) + (:arg-types unsigned-num unsigned-num) + (:results (result :scs (unsigned-reg) :from (:argument 0))) + (:result-types unsigned-num) + (:temporary (:sc signed-reg :offset rcx-offset :from (:argument 1)) rcx) + (:generator 4 + (move result number) + (move rcx amount) + (inst shr result :cl))) + +#!+ash-right-vops +(define-vop (fast-%ash/right/signed) + (:translate %ash/right) + (:policy :fast-safe) + (:args (number :scs (signed-reg) :target result) + (amount :scs (unsigned-reg) :target rcx)) + (:arg-types signed-num unsigned-num) + (:results (result :scs (signed-reg) :from (:argument 0))) + (:result-types signed-num) + (:temporary (:sc signed-reg :offset rcx-offset :from (:argument 1)) rcx) + (:generator 4 + (move result number) + (move rcx amount) + (inst sar result :cl))) + +#!+ash-right-vops +(define-vop (fast-%ash/right/fixnum) + (:translate %ash/right) + (:policy :fast-safe) + (:args (number :scs (any-reg) :target result) + (amount :scs (unsigned-reg) :target rcx)) + (:arg-types tagged-num unsigned-num) + (:results (result :scs (any-reg) :from (:argument 0))) + (:result-types tagged-num) + (:temporary (:sc signed-reg :offset rcx-offset :from (:argument 1)) rcx) + (:generator 3 + (move result number) + (move rcx amount) + (inst sar result :cl) + (inst and result (lognot fixnum-tag-mask)))) + (in-package "SB!C") (defknown %lea (integer integer (member 1 2 4 8 16) (signed-byte 64)) @@ -981,7 +1043,7 @@ constant shift greater than word length"))) (:generator 4 (move result number) (move ecx amount) - (inst or ecx ecx) + (inst test ecx ecx) (inst jmp :ns POSITIVE) (inst neg ecx) (zeroize zero) @@ -1006,9 +1068,7 @@ constant shift greater than word length"))) (:result-types unsigned-num) (:generator 28 (move res arg) - (if (sc-is res unsigned-reg) - (inst test res res) - (inst cmp res 0)) + (inst test res res) (inst jmp :ge POS) (inst not res) POS @@ -1037,6 +1097,52 @@ constant shift greater than word length"))) (zeroize res) DONE)) +;; INTEGER-LENGTH is implemented by using the BSR instruction, which +;; returns the position of the first 1-bit from the right. And that needs +;; to be incremented to get the width of the integer, and BSR doesn't +;; work on 0, so it needs a branch to handle 0. + +;; But fixnums are tagged by being shifted left n-fixnum-tag-bits times, +;; untagging by shifting right n-fixnum-tag-bits-1 times (and if +;; n-fixnum-tag-bits = 1, no shifting is required), will make the +;; resulting integer one bit wider, making the increment unnecessary. +;; Then, to avoid calling BSR on 0, OR the result with 1. That sets the +;; first bit to 1, and if all other bits are 0, BSR will return 0, +;; which is the correct value for INTEGER-LENGTH. +(define-vop (positive-fixnum-len) + (:translate integer-length) + (:note "inline positive fixnum integer-length") + (:policy :fast-safe) + (:args (arg :scs (any-reg))) + (:arg-types positive-fixnum) + (:results (res :scs (unsigned-reg))) + (:result-types unsigned-num) + (:generator 24 + (move res arg) + (when (> n-fixnum-tag-bits 1) + (inst shr res (1- n-fixnum-tag-bits))) + (inst or res 1) + (inst bsr res res))) + +(define-vop (fixnum-len) + (:translate integer-length) + (:note "inline fixnum integer-length") + (:policy :fast-safe) + (:args (arg :scs (any-reg) :target res)) + (:arg-types tagged-num) + (:results (res :scs (unsigned-reg))) + (:result-types unsigned-num) + (:generator 25 + (move res arg) + (when (> n-fixnum-tag-bits 1) + (inst sar res (1- n-fixnum-tag-bits))) + (inst test res res) + (inst jmp :ge POS) + (inst not res) + POS + (inst or res 1) + (inst bsr res res))) + (define-vop (unsigned-byte-64-count) (:translate logcount) (:note "inline (unsigned-byte 64) logcount") @@ -1101,9 +1207,6 @@ constant shift greater than word length"))) (:affected) (:policy :fast-safe)) -;;; constant variants are declared for 32 bits not 64 bits, because -;;; loading a 64 bit constant is silly - (define-vop (fast-conditional/fixnum fast-conditional) (:args (x :scs (any-reg) :load-if (not (and (sc-is x control-stack) @@ -1113,9 +1216,7 @@ constant shift greater than word length"))) (:note "inline fixnum comparison")) (define-vop (fast-conditional-c/fixnum fast-conditional/fixnum) - (:args (x :scs (any-reg) - :load-if (or (not (typep y '(signed-byte 29))) - (not (sc-is x any-reg control-stack))))) + (:args (x :scs (any-reg) :load-if t)) (:arg-types tagged-num (:constant fixnum)) (:info y)) @@ -1128,9 +1229,7 @@ constant shift greater than word length"))) (:note "inline (signed-byte 64) comparison")) (define-vop (fast-conditional-c/signed fast-conditional/signed) - (:args (x :scs (signed-reg) - :load-if (or (not (typep y '(signed-byte 32))) - (not (sc-is x signed-reg signed-stack))))) + (:args (x :scs (signed-reg) :load-if t)) (:arg-types signed-num (:constant (signed-byte 64))) (:info y)) @@ -1143,12 +1242,78 @@ constant shift greater than word length"))) (:note "inline (unsigned-byte 64) comparison")) (define-vop (fast-conditional-c/unsigned fast-conditional/unsigned) - (:args (x :scs (unsigned-reg) - :load-if (or (not (typep y '(unsigned-byte 31))) - (not (sc-is x unsigned-reg unsigned-stack))))) + (:args (x :scs (unsigned-reg) :load-if t)) (:arg-types unsigned-num (:constant (unsigned-byte 64))) (:info y)) +;; Stolen liberally from the x86 32-bit implementation. +(macrolet ((define-logtest-vops () + `(progn + ,@(loop for suffix in '(/fixnum -c/fixnum + /signed -c/signed + /unsigned -c/unsigned) + for cost in '(4 3 6 5 6 5) + collect + `(define-vop (,(symbolicate "FAST-LOGTEST" suffix) + ,(symbolicate "FAST-CONDITIONAL" suffix)) + (:translate logtest) + (:conditional :ne) + (:generator ,cost + (emit-optimized-test-inst x + ,(case suffix + (-c/fixnum + `(constantize (fixnumize y))) + ((-c/signed -c/unsigned) + `(constantize y)) + (t + 'y))))))))) + (define-logtest-vops)) + +(defknown %logbitp (integer unsigned-byte) boolean + (movable foldable flushable always-translatable)) + +;;; only for constant folding within the compiler +(defun %logbitp (integer index) + (logbitp index integer)) + +;;; too much work to do the non-constant case (maybe?) +(define-vop (fast-logbitp-c/fixnum fast-conditional-c/fixnum) + (:translate %logbitp) + (:conditional :c) + (:arg-types tagged-num (:constant (integer 0 #.(- 63 n-fixnum-tag-bits)))) + (:generator 4 + (inst bt x (+ y n-fixnum-tag-bits)))) + +(define-vop (fast-logbitp/signed fast-conditional/signed) + (:args (x :scs (signed-reg signed-stack)) + (y :scs (signed-reg))) + (:translate %logbitp) + (:conditional :c) + (:generator 6 + (inst bt x y))) + +(define-vop (fast-logbitp-c/signed fast-conditional-c/signed) + (:translate %logbitp) + (:conditional :c) + (:arg-types signed-num (:constant (integer 0 63))) + (:generator 5 + (inst bt x y))) + +(define-vop (fast-logbitp/unsigned fast-conditional/unsigned) + (:args (x :scs (unsigned-reg unsigned-stack)) + (y :scs (unsigned-reg))) + (:translate %logbitp) + (:conditional :c) + (:generator 6 + (inst bt x y))) + +(define-vop (fast-logbitp-c/unsigned fast-conditional-c/unsigned) + (:translate %logbitp) + (:conditional :c) + (:arg-types unsigned-num (:constant (integer 0 63))) + (:generator 5 + (inst bt x y))) + (macrolet ((define-conditional-vop (tran cond unsigned not-cond not-unsigned) `(progn ,@(mapcar @@ -1166,20 +1331,9 @@ constant shift greater than word length"))) (inst cmp x ,(case suffix (-c/fixnum - `(if (typep y '(signed-byte 29)) - (fixnumize y) - (register-inline-constant - :qword (fixnumize y)))) - (-c/signed - `(if (typep y '(signed-byte 32)) - y - (register-inline-constant - :qword y))) - (-c/unsigned - `(if (typep y '(unsigned-byte 31)) - y - (register-inline-constant - :qword y))) + `(constantize (fixnumize y))) + ((-c/signed -c/unsigned) + `(constantize y)) (t 'y)))))) '(/fixnum -c/fixnum /signed -c/signed /unsigned -c/unsigned) ; '(/fixnum /signed /unsigned) @@ -1199,10 +1353,8 @@ constant shift greater than word length"))) (:generator 5 (cond ((and (sc-is x signed-reg) (zerop y)) (inst test x x)) ; smaller instruction - ((typep y '(signed-byte 32)) - (inst cmp x y)) (t - (inst cmp x (register-inline-constant :qword y)))))) + (inst cmp x (constantize y)))))) (define-vop (fast-if-eql/unsigned fast-conditional/unsigned) (:translate eql) @@ -1214,10 +1366,8 @@ constant shift greater than word length"))) (:generator 5 (cond ((and (sc-is x unsigned-reg) (zerop y)) (inst test x x)) ; smaller instruction - ((typep y '(unsigned-byte 31)) - (inst cmp x y)) (t - (inst cmp x (register-inline-constant :qword y)))))) + (inst cmp x (constantize y)))))) ;;; EQL/FIXNUM is funny because the first arg can be of any type, not just a ;;; known fixnum. @@ -1247,23 +1397,21 @@ constant shift greater than word length"))) (:arg-types * tagged-num) (:variant-cost 7)) -(define-vop (fast-eql-c/fixnum fast-conditional/fixnum) - (:args (x :scs (any-reg) - :load-if (or (not (typep y '(signed-byte 29))) - (not (sc-is x any-reg descriptor-reg control-stack))))) +(define-vop (fast-eql-c/fixnum fast-conditional-c/fixnum) + (:args (x :scs (any-reg) :load-if t)) (:arg-types tagged-num (:constant fixnum)) (:info y) + (:conditional :e) + (:policy :fast-safe) (:translate eql) (:generator 2 (cond ((and (sc-is x any-reg descriptor-reg) (zerop y)) (inst test x x)) ; smaller instruction - ((typep y '(signed-byte 29)) - (inst cmp x (fixnumize y))) (t - (inst cmp x (register-inline-constant :qword (fixnumize y))))))) + (inst cmp x (constantize (fixnumize y))))))) (define-vop (generic-eql-c/fixnum fast-eql-c/fixnum) - (:args (x :scs (any-reg descriptor-reg))) + (:args (x :scs (any-reg descriptor-reg) :load-if t)) (:arg-types * (:constant fixnum)) (:variant-cost 6)) @@ -1322,20 +1470,11 @@ constant shift greater than word length"))) (defmacro define-mod-binop-c ((name prototype) function) `(define-vop (,name ,prototype) (:args (x :target r :scs (unsigned-reg signed-reg) - :load-if (not (and (or (sc-is x unsigned-stack) - (sc-is x signed-stack)) - (or (sc-is r unsigned-stack) - (sc-is r signed-stack)) - (location= x r) - (typep y '(signed-byte 32)))))) + :load-if t)) (:info y) (:arg-types untagged-num (:constant (or (unsigned-byte 64) (signed-byte 64)))) (:results (r :scs (unsigned-reg signed-reg) :from (:argument 0) - :load-if (not (and (or (sc-is x unsigned-stack) - (sc-is x signed-stack)) - (or (sc-is r unsigned-stack) - (sc-is r unsigned-stack)) - (location= x r))))) + :load-if t)) (:result-types unsigned-num) (:translate ,function))) @@ -1506,7 +1645,7 @@ constant shift greater than word length"))) (:arg-types unsigned-num) (:conditional :ns) (:generator 3 - (inst or digit digit))) + (inst test digit digit))) ;;; For add and sub with carry the sc of carry argument is any-reg so @@ -1751,6 +1890,77 @@ constant shift greater than word length"))) (move result digit) (move ecx count) (inst shl result :cl))) + +(define-vop (logand-bignum/c) + (:translate logand) + (:policy :fast-safe) + (:args (x :scs (descriptor-reg))) + (:arg-types bignum (:constant word)) + (:results (r :scs (unsigned-reg))) + (:info mask) + (:result-types unsigned-num) + (:generator 4 + (let ((mask (constantize mask))) + (cond ((or (integerp mask) + (location= x r)) + (loadw r x bignum-digits-offset other-pointer-lowtag) + (unless (eql mask -1) + (inst and r mask))) + (t + (inst mov r mask) + (inst and r (make-ea-for-object-slot x + bignum-digits-offset + other-pointer-lowtag))))))) + +;; Specialised mask-signed-field VOPs. +(define-vop (mask-signed-field-word/c) + (:translate sb!c::mask-signed-field) + (:policy :fast-safe) + (:args (x :scs (signed-reg unsigned-reg) :target r)) + (:arg-types (:constant (integer 0 64)) untagged-num) + (:results (r :scs (signed-reg))) + (:result-types signed-num) + (:info width) + (:generator 3 + (cond ((zerop width) + (zeroize r)) + ((= width 64) + (move r x)) + ((member width '(32 16 8)) + (inst movsx r (reg-in-size x (ecase width + (32 :dword) + (16 :word) + (8 :byte))))) + (t + (move r x) + (let ((delta (- n-word-bits width))) + (inst shl r delta) + (inst sar r delta)))))) + +(define-vop (mask-signed-field-bignum/c) + (:translate sb!c::mask-signed-field) + (:policy :fast-safe) + (:args (x :scs (descriptor-reg) :target r)) + (:arg-types (:constant (integer 0 64)) bignum) + (:results (r :scs (signed-reg))) + (:result-types signed-num) + (:info width) + (:generator 4 + (cond ((zerop width) + (zeroize r)) + ((member width '(8 16 32 64)) + (ecase width + (64 (loadw r x bignum-digits-offset other-pointer-lowtag)) + ((32 16 8) + (inst movsx r (make-ea (ecase width (32 :dword) (16 :word) (8 :byte)) + :base x + :disp (- (* bignum-digits-offset n-word-bytes) + other-pointer-lowtag)))))) + (t + (loadw r x bignum-digits-offset other-pointer-lowtag) + (let ((delta (- n-word-bits width))) + (inst shl r delta) + (inst sar r delta)))))) ;;;; static functions