Improve floatingpoint type derivation in some cases
Affects  Status  Importance  Assigned to  Milestone  

 SBCL 
Undecided

Unassigned 
Bug Description
Hi,
in the following function
(defun f (x)
(declare (type (singlefloat 0.0) x))
(when (> x 0)
(log x)))
the compiler generates a full call to LOG as it can't derive
that LOG's argument can't be zero. I needed to change that to
(defun g (x)
(declare (type (singlefloat 0.0) x))
(when (> x 0)
(locally (declare (type (singlefloat (0.0)) x))
(log x))))
so that the type is honored and a call to the C library "log"
is generated. This seems silly. Moreover, if less is known
about X, the derivation works better:
(defun h (x)
(declare (type (singlefloat 1.0) x))
(when (> x 0)
(log x)))
This generates a call to the C library "log" as desired.
I have tracked this down to CONSTRAIN
tracing it during compilation of the functions shows
(for f):
0: (SBC::
#<
#<
T NIL)
0: SBC::CONSTRAIN
#<
(for h):
0: (SBC::
#<
#<
T NIL)
0: SBC::CONSTRAIN
#<
So, the patch attached below changes CONSTRAIN
to return the openly bounded type,
#<SB
in the first case above, too.
I have compared the old and the new version of this function
with all combinations of NIL, 0.0, (0.0), 1.0 and (1.0) as
the lower and upper bounds of the first two arguments and with
all combinations of T and NIL for the remaining two, and have
convinced myself that firstly, in the cases where the output
differs, the new version is better, and secondly, that the
function now returns the tightest possible type in all cases.
With the argument list of CONSTRAIN
(X Y GREATER OREQUAL),
without loss of generality taking GREATER = T, the only changed
cases are with OREQUAL = NIL and TYPEBOUNDNUMBER of the lower
bounds of X and Y being equal numbers (using 0.0 below as an
example only), the upper bounds being mostly don't care (using
NIL below), the following two variations:
X = (SINGLEFLOAT 0.0)
Y = (SINGLEFLOAT 0.0)
and
X = (SINGLEFLOAT 0.0)
Y = (SINGLEFLOAT (0.0))
In both cases the old result is
(SINGLEFLOAT 0.0)
and the new one
(SINGLEFLOAT (0.0))
(An upper bound of X that is too low can change the new result
to the empty type where the old result was something like
(SINGLEFLOAT 0.0 0.0)).
The patched version builds and passes the test suite under
Linux, x8664.
Greetings,
Lutz
Changed in sbcl:  
assignee:  nobody → Nikodemus Siivola (nikodemus) 
status:  New → In Progress 
Nikodemus Siivola (nikodemus) wrote :  #2 
Changed in sbcl:  
assignee:  Nikodemus Siivola (nikodemus) → nobody 
status:  In Progress → Fix Committed 
Changed in sbcl:  
status:  Fix Committed → Fix Released 
commit 0dda5090b6c16a641000b4eb2dcd479f39b784ca
Author: Lutz Euler <email address hidden>
Date: Wed Nov 23 20:31:09 2011 +0100
Tighter floatingpoint type constraints in some cases
CONSTRAINFLOATTYPE used to return a closed bound in some cases where
the corresponding (tighter) open bound would have been derivable,
leading to missed optimisation opportunities. For example the compiler
did not derive that x is not zero in the following call to LOG:
(defun foo (x)
(declare (type (singlefloat 0.0) x))
(when (> x 0.0)
(log x)))
Fix CONSTRAINFLOATTYPE so that it returns the tightest possible result
in all cases.
See lp#894498 for details.