X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Ffloat-tran.lisp;h=68c1ccd745356a71de1dfec45cec7a47c9953798;hb=d7e55b414d180341d79e0eddc957e1aa52551c38;hp=c44d14945e9c2a2506ee7e75a74b0cd58b227418;hpb=a157ed0be79751f85b8243c06102eea95af06aa3;p=sbcl.git diff --git a/src/compiler/float-tran.lisp b/src/compiler/float-tran.lisp index c44d149..68c1ccd 100644 --- a/src/compiler/float-tran.lisp +++ b/src/compiler/float-tran.lisp @@ -15,8 +15,10 @@ ;;;; coercions -(defknown %single-float (real) single-float (movable foldable)) -(defknown %double-float (real) double-float (movable foldable)) +(defknown %single-float (real) single-float + (movable foldable)) +(defknown %double-float (real) double-float + (movable foldable)) (deftransform float ((n f) (* single-float) *) '(%single-float n)) @@ -100,10 +102,36 @@ ;;;; float accessors (defknown make-single-float ((signed-byte 32)) single-float - (movable foldable flushable)) + (movable flushable)) (defknown make-double-float ((signed-byte 32) (unsigned-byte 32)) double-float - (movable foldable flushable)) + (movable flushable)) + +#-sb-xc-host +(deftransform make-single-float ((bits) + ((signed-byte 32))) + "Conditional constant folding" + (unless (constant-lvar-p bits) + (give-up-ir1-transform)) + (let* ((bits (lvar-value bits)) + (float (make-single-float bits))) + (when (float-nan-p float) + (give-up-ir1-transform)) + float)) + +#-sb-xc-host +(deftransform make-double-float ((hi lo) + ((signed-byte 32) (unsigned-byte 32))) + "Conditional constant folding" + (unless (and (constant-lvar-p hi) + (constant-lvar-p lo)) + (give-up-ir1-transform)) + (let* ((hi (lvar-value hi)) + (lo (lvar-value lo)) + (float (make-double-float hi lo))) + (when (float-nan-p float) + (give-up-ir1-transform)) + float)) (defknown single-float-bits (single-float) (signed-byte 32) (movable foldable flushable)) @@ -303,8 +331,11 @@ ;; problem, but in the context of evaluated and compiled (+ ) ;; giving different result if we fail to check for this. (or (not (csubtypep x (specifier-type 'integer))) + #!+x86 (csubtypep x (specifier-type `(integer ,most-negative-exactly-single-float-fixnum - ,most-positive-exactly-single-float-fixnum))))) + ,most-positive-exactly-single-float-fixnum))) + #!-x86 + (csubtypep x (specifier-type 'fixnum)))) ;;; Do some stuff to recognize when the loser is doing mixed float and ;;; rational arithmetic, or different float types, and fix it up. If @@ -332,6 +363,73 @@ (%deftransform x '(function (double-float single-float) *) #'float-contagion-arg2)) +(macrolet ((def (type &rest args) + `(deftransform * ((x y) (,type (constant-arg (member ,@args))) * + ;; Beware the SNaN! + :policy (zerop float-accuracy)) + "optimize multiplication by one" + (let ((y (lvar-value y))) + (if (minusp y) + '(%negate x) + 'x))))) + (def single-float 1.0 -1.0) + (def double-float 1.0d0 -1.0d0)) + +;;; Return the reciprocal of X if it can be represented exactly, NIL otherwise. +(defun maybe-exact-reciprocal (x) + (unless (zerop x) + (handler-case + (multiple-value-bind (significand exponent sign) + (integer-decode-float x) + ;; only powers of 2 can be inverted exactly + (unless (zerop (logand significand (1- significand))) + (return-from maybe-exact-reciprocal nil)) + (let ((expected (/ sign significand (expt 2 exponent))) + (reciprocal (/ x))) + (multiple-value-bind (significand exponent sign) + (integer-decode-float reciprocal) + ;; Denorms can't be inverted safely. + (and (eql expected (* sign significand (expt 2 exponent))) + reciprocal)))) + (error () (return-from maybe-exact-reciprocal nil))))) + +;;; Replace constant division by multiplication with exact reciprocal, +;;; if one exists. +(macrolet ((def (type) + `(deftransform / ((x y) (,type (constant-arg ,type)) * + :node node) + "convert to multiplication by reciprocal" + (let ((n (lvar-value y))) + (if (policy node (zerop float-accuracy)) + `(* x ,(/ n)) + (let ((r (maybe-exact-reciprocal n))) + (if r + `(* x ,r) + (give-up-ir1-transform + "~S does not have an exact reciprocal" + n)))))))) + (def single-float) + (def double-float)) + +;;; Optimize addition and subtraction of zero +(macrolet ((def (op type &rest args) + `(deftransform ,op ((x y) (,type (constant-arg (member ,@args))) * + ;; Beware the SNaN! + :policy (zerop float-accuracy)) + 'x))) + ;; No signed zeros, thanks. + (def + single-float 0 0.0) + (def - single-float 0 0.0) + (def + double-float 0 0.0 0.0d0) + (def - double-float 0 0.0 0.0d0)) + +;;; On most platforms (+ x x) is faster than (* x 2) +(macrolet ((def (type &rest args) + `(deftransform * ((x y) (,type (constant-arg (member ,@args)))) + '(+ x x)))) + (def single-float 2 2.0) + (def double-float 2 2.0 2.0d0)) + ;;; Prevent ZEROP, PLUSP, and MINUSP from losing horribly. We can't in ;;; general float rational args to comparison, since Common Lisp ;;; semantics says we are supposed to compare as rationals, but we can @@ -468,27 +566,27 @@ (deftransform ,name ((x) (single-float) *) #!+x86 (cond ((csubtypep (lvar-type x) (specifier-type '(single-float - (#.(- (expt 2f0 64))) - (#.(expt 2f0 64))))) + (#.(- (expt 2f0 63))) + (#.(expt 2f0 63))))) `(coerce (,',prim-quick (coerce x 'double-float)) 'single-float)) (t (compiler-notify "unable to avoid inline argument range check~@ - because the argument range (~S) was not within 2^64" + because the argument range (~S) was not within 2^63" (type-specifier (lvar-type x))) `(coerce (,',prim (coerce x 'double-float)) 'single-float))) #!-x86 `(coerce (,',prim (coerce x 'double-float)) 'single-float)) (deftransform ,name ((x) (double-float) *) #!+x86 (cond ((csubtypep (lvar-type x) (specifier-type '(double-float - (#.(- (expt 2d0 64))) - (#.(expt 2d0 64))))) + (#.(- (expt 2d0 63))) + (#.(expt 2d0 63))))) `(,',prim-quick x)) (t (compiler-notify "unable to avoid inline argument range check~@ - because the argument range (~S) was not within 2^64" + because the argument range (~S) was not within 2^63" (type-specifier (lvar-type x))) `(,',prim x))) #!-x86 `(,',prim x))))) @@ -853,11 +951,9 @@ (int-hi (if hi (ceiling (type-bound-number hi)) '*)) - (f-lo (if lo - (bound-func #'float lo) + (f-lo (or (bound-func #'float lo) '*)) - (f-hi (if hi - (bound-func #'float hi) + (f-hi (or (bound-func #'float hi) '*))) (specifier-type `(or (rational ,int-lo ,int-hi) (single-float ,f-lo, f-hi))))) @@ -887,11 +983,9 @@ (int-hi (if hi (ceiling (type-bound-number hi)) '*)) - (f-lo (if lo - (bound-func #'float lo) + (f-lo (or (bound-func #'float lo) '*)) - (f-hi (if hi - (bound-func #'float hi) + (f-hi (or (bound-func #'float hi) '*))) (specifier-type `(or (rational ,int-lo ,int-hi) (single-float ,f-lo, f-hi))))) @@ -953,7 +1047,7 @@ ;; But a positive real to any power is well-defined. (merged-interval-expt x y)) ((and (csubtypep x (specifier-type 'rational)) - (csubtypep x (specifier-type 'rational))) + (csubtypep y (specifier-type 'rational))) ;; A rational to the power of a rational could be a rational ;; or a possibly-complex single float (specifier-type '(or rational single-float (complex single-float)))) @@ -1430,15 +1524,51 @@ (define-frobs truncate %unary-truncate) (define-frobs round %unary-round)) -;;; Convert (TRUNCATE x y) to the obvious implementation. We only want -;;; this when under certain conditions and let the generic TRUNCATE -;;; handle the rest. (Note: if Y = 1, the divide and multiply by Y -;;; should be removed by other DEFTRANSFORMs.) -(deftransform truncate ((x &optional y) - (float &optional (or float integer))) - (let ((defaulted-y (if y 'y 1))) - `(let ((res (%unary-truncate (/ x ,defaulted-y)))) - (values res (- x (* ,defaulted-y res)))))) +(deftransform %unary-truncate ((x) (single-float)) + `(%unary-truncate/single-float x)) +(deftransform %unary-truncate ((x) (double-float)) + `(%unary-truncate/double-float x)) + +;;; Convert (TRUNCATE x y) to the obvious implementation. +;;; +;;; ...plus hair: Insert explicit coercions to appropriate float types: Python +;;; is reluctant it generate explicit integer->float coercions due to +;;; precision issues (see SAFE-SINGLE-COERCION-P &co), but this is not an +;;; issue here as there is no DERIVE-TYPE optimizer on specialized versions of +;;; %UNARY-TRUNCATE, so the derived type of TRUNCATE remains the best we can +;;; do here -- which is fine. Also take care not to add unnecassary division +;;; or multiplication by 1, since we are not able to always eliminate them, +;;; depending on FLOAT-ACCURACY. Finally, leave out the secondary value when +;;; we know it is unused: COERCE is not flushable. +(macrolet ((def (type other-float-arg-types) + (let ((unary (symbolicate "%UNARY-TRUNCATE/" type)) + (coerce (symbolicate "%" type))) + `(deftransform truncate ((x &optional y) + (,type + &optional (or ,type ,@other-float-arg-types integer)) + * :result result) + (let* ((result-type (and result + (lvar-derived-type result))) + (compute-all (and (values-type-p result-type) + (not (type-single-value-p result-type))))) + (if (or (not y) + (and (constant-lvar-p y) (= 1 (lvar-value y)))) + (if compute-all + `(let ((res (,',unary x))) + (values res (- x (,',coerce res)))) + `(let ((res (,',unary x))) + ;; Dummy secondary value! + (values res x))) + (if compute-all + `(let* ((f (,',coerce y)) + (res (,',unary (/ x f)))) + (values res (- x (* f (,',coerce res))))) + `(let* ((f (,',coerce y)) + (res (,',unary (/ x f)))) + ;; Dummy secondary value! + (values res x))))))))) + (def single-float ()) + (def double-float (single-float))) (deftransform floor ((number &optional divisor) (float &optional (or integer float)))