X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Ffloat-tran.lisp;h=a1184e12ae59221f26aa7c22738b823fe6ca8253;hb=95591ed483dbb8c0846c129953acac1554f28809;hp=301628291f486eebc0007c19189f43d050ede3ff;hpb=808d56b363a2eefbe46ff03a5c04157c0d6e3571;p=sbcl.git diff --git a/src/compiler/float-tran.lisp b/src/compiler/float-tran.lisp index 3016282..a1184e1 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)) @@ -332,16 +334,48 @@ (%deftransform x '(function (double-float single-float) *) #'float-contagion-arg2)) -;;; Optimize division and multiplication by one and minus one. -(macrolet ((def (op type &rest args) - `(deftransform ,op ((x y) (,type (constant-arg (member ,@args)))) - (if (minusp (lvar-value y)) - '(+ (%negate x) ,(coerce 0 type)) - '(+ x ,(coerce 0 type)))))) - (def / single-float 1 1.0 -1 -1.0) - (def * single-float 1 1.0 -1 -1.0) - (def / double-float 1 1.0 1.0d0 -1 -1.0 -1.0d0) - (def * double-float 1 1.0 1.0d0 -1 -1.0 -1.0d0)) +(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) + (multiple-value-bind (significand exponent sign) + ;; Signals an error for NaNs and infinities. + (handler-case (integer-decode-float x) + (error () (return-from maybe-exact-reciprocal nil))) + (let ((expected (/ sign significand (expt 2 exponent)))) + (let ((reciprocal (/ 1 x))) + (multiple-value-bind (significand exponent sign) (integer-decode-float reciprocal) + (when (eql expected (* sign significand (expt 2 exponent))) + reciprocal))))))) + +;;; 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 subsctraction of zero (macrolet ((def (op type &rest args) @@ -1460,15 +1494,48 @@ (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 (lvar-derived-type result))) + (if (or (not y) + (and (constant-lvar-p y) (= 1 (lvar-value y)))) + (if (values-type-p result-type) + `(let ((res (,',unary x))) + (values res (- x (,',coerce res)))) + `(let ((res (,',unary x))) + ;; Dummy secondary value! + (values res x))) + (if (values-type-p result-type) + `(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)))