Skip to content
srctran.lisp 137 KiB
Newer Older
wlott's avatar
wlott committed
;;; -*- Package: C; Log: C.Log -*-
;;;
;;; **********************************************************************
ram's avatar
ram committed
;;; This code was written as part of the CMU Common Lisp project at
;;; Carnegie Mellon University, and has been placed in the public domain.
;;;
(ext:file-comment
ram's avatar
ram committed
;;;
wlott's avatar
wlott committed
;;; **********************************************************************
;;;
;;;    This file contains macro-like source transformations which convert
;;; uses of certain functions into the canonical form desired within the
;;; compiler.  ### and other IR1 transforms and stuff.  Some code adapted from
;;; CLC, written by Wholey and Fahlman.
;;;
;;; Written by Rob MacLachlan
;;;
;;; Propagate-float-type extension by Raymond Toy.
;;;
(in-package "C")
(intl:textdomain "cmucl")
wlott's avatar
wlott committed

;;; Source transform for Not, Null  --  Internal
;;;
;;;    Convert into an IF so that IF optimizations will eliminate redundant
;;; negations.
;;;
(def-source-transform not (x) `(if ,x nil t))
(def-source-transform null (x) `(if ,x nil t))

;;; Source transform for Endp  --  Internal
;;;
;;;    Endp is just NULL with a List assertion.
;;;
(def-source-transform endp (x)
  `(null (the (values &optional list &rest t) ,x)))
wlott's avatar
wlott committed

;;; We turn Identity into Prog1 so that it is obvious that it just returns the
;;; first value of its argument.  Ditto for Values with one arg.
(def-source-transform identity (x) `(prog1 ,x))
(def-source-transform values (x) `(prog1 ,x))

;;; CONSTANTLY source transform  --  Internal
;;;
;;;    Bind the values and make a closure that returns them.
;;;
(def-source-transform constantly (value &rest values)
  (let ((temps (loop repeat (1+ (length values))
		     collect (gensym)))
	(dum (gensym)))
    `(let ,(loop for temp in temps and
	         value in (list* value values)
	         collect `(,temp ,value))
       #'(lambda (&rest ,dum)
	   (declare (ignore ,dum))
	   (values ,@temps)))))


;;; COMPLEMENT IR1 transform  --  Internal
;;;
;;;    If the function has a known number of arguments, then return a lambda
;;; with the appropriate fixed number of args.  If the destination is a
;;; FUNCALL, then do the &REST APPLY thing, and let MV optimization figure
;;; things out.
;;;
(deftransform complement ((fun) * * :node node :when :both)
  (multiple-value-bind (min max)
		       (function-type-nargs (continuation-type fun))
    (cond
     ((and min (eql min max))
      (let ((dums (loop repeat min collect (gensym))))
	`#'(lambda ,dums (not (funcall fun ,@dums)))))
     ((let* ((cont (node-cont node))
	     (dest (continuation-dest cont)))
	(and (combination-p dest)
	     (eq (combination-fun dest) cont)))
      '#'(lambda (&rest args)
	   (not (apply fun args))))
     (t
      (give-up (intl:gettext "Function doesn't have fixed argument count."))))))
wlott's avatar
wlott committed

;;;; List hackery:

;;;
;;; Translate CxxR into car/cdr combos.

(defun source-transform-cxr (form)
  (if (or (byte-compiling) (/= (length form) 2))
      (values nil t)
      (let ((name (symbol-name (car form))))
	(do ((i (- (length name) 2) (1- i))
	     (res (cadr form)
		  `(,(ecase (char name i)
		       (#\A 'car)
		       (#\D 'cdr))
		    ,res)))
	    ((zerop i) res)))))

(do ((i 2 (1+ i))
     (b '(1 0) (cons i b)))
    ((= i 5))
  (dotimes (j (ash 1 i))
    (setf (info function source-transform
		(intern (format nil "C~{~:[A~;D~]~}R"
				(mapcar #'(lambda (x) (logbitp x j)) b))))
	  #'source-transform-cxr)))
wlott's avatar
wlott committed

;;;
;;; Turn First..Fourth and Rest into the obvious synonym, assuming whatever is
;;; right for them is right for us.  Fifth..Tenth turn into Nth, which can be
;;; expanded into a car/cdr later on if policy favors it.
(def-source-transform first (x) `(car ,x))
(def-source-transform rest (x) `(cdr ,x))
(def-source-transform second (x) `(cadr ,x))
(def-source-transform third (x) `(caddr ,x))
(def-source-transform fourth (x) `(cadddr ,x))
(def-source-transform fifth (x) `(nth 4 ,x))
(def-source-transform sixth (x) `(nth 5 ,x))
(def-source-transform seventh (x) `(nth 6 ,x))
(def-source-transform eighth (x) `(nth 7 ,x))
(def-source-transform ninth (x) `(nth 8 ,x))
(def-source-transform tenth (x) `(nth 9 ,x))


;;;
;;; Translate RPLACx to LET and SETF.
(def-source-transform rplaca (x y)
  (once-only ((n-x x))
    `(progn
       (setf (car ,n-x) ,y)
       ,n-x)))
;;;
(def-source-transform rplacd (x y)
  (once-only ((n-x x))
    `(progn
       (setf (cdr ,n-x) ,y)
       ,n-x)))


(def-source-transform nth (n l) `(car (nthcdr ,n ,l)))
  
(defvar *default-nthcdr-open-code-limit* 6)
(defvar *extreme-nthcdr-open-code-limit* 20)

(deftransform nthcdr ((n l) (unsigned-byte t) * :node node)
  "convert NTHCDR to CAxxR"
wlott's avatar
wlott committed
  (unless (constant-continuation-p n) (give-up))
  (let ((n (continuation-value n)))
    (when (> n
	     (if (policy node (= speed 3) (= space 0))
		 *extreme-nthcdr-open-code-limit*
		 *default-nthcdr-open-code-limit*))
      (give-up))

    (labels ((frob (n)
	       (if (zerop n)
		   'l
		   `(cdr ,(frob (1- n))))))
      (frob n))))


;;;; ARITHMETIC and NUMEROLOGY.

(def-source-transform plusp (x) `(> ,x 0))
(def-source-transform minusp (x) `(< ,x 0))
(def-source-transform zerop (x) `(= ,x 0))

(def-source-transform 1+ (x) `(+ ,x 1))
(def-source-transform 1- (x) `(- ,x 1))

(def-source-transform oddp (x) `(not (zerop (logand ,x 1))))
(def-source-transform evenp (x) `(zerop (logand ,x 1)))

;;; Note that all the integer division functions are available for inline
;;; expansion.

(macrolet ((frob (fun)
	     `(def-source-transform ,fun (x &optional (y nil y-p))
		(declare (ignore y))
		(if y-p
		    (values nil t)
		    `(,',fun ,x 1)))))
  (frob truncate)
  (frob round)
  (frob floor)
  (frob ceiling))
wlott's avatar
wlott committed

;; Some of these source transforms are not needed when modular
;; arithmetic is available.  When modular arithmetic is available, the
;; various backends need to define them there.
#-modular-arith
(progn
wlott's avatar
wlott committed
(def-source-transform lognand (x y) `(lognot (logand ,x ,y)))
(def-source-transform lognor (x y) `(lognot (logior ,x ,y)))
(def-source-transform logandc1 (x y) `(logand (lognot ,x) ,y))
(def-source-transform logandc2 (x y) `(logand ,x (lognot ,y)))
(def-source-transform logorc1 (x y) `(logior (lognot ,x) ,y))
(def-source-transform logorc2 (x y) `(logior ,x (lognot ,y)))
wlott's avatar
wlott committed
(def-source-transform logtest (x y) `(not (zerop (logand ,x ,y))))
(def-source-transform byte (size position) `(cons ,size ,position))
(def-source-transform byte-size (spec) `(car ,spec))
(def-source-transform byte-position (spec) `(cdr ,spec))
(def-source-transform ldb-test (bytespec integer)
  `(not (zerop (mask-field ,bytespec ,integer))))
wlott's avatar
wlott committed


;;; With the ratio and complex accessors, we pick off the "identity" case, and
;;; use a primitive to handle the cell access case.
;;;
(def-source-transform numerator (num)
  (once-only ((n-num `(the (values rational &rest t) ,num)))
	 ,n-num)))
;;;
(def-source-transform denominator (num)
  (once-only ((n-num `(the (values rational &rest t) ,num)))
(deftransform logbitp ((index integer)
		       (integer (or (signed-byte #.vm:word-bits)
				    (unsigned-byte #.vm:word-bits)))
		       (member nil t))
  `(if (>= index #.vm:word-bits)
       (minusp integer)
       (not (zerop (logand integer (ash 1 index))))))

;;;; Interval arithmetic for computing bounds
;;;; (toy@rtp.ericsson.se)
;;;;
;;;; This is a set of routines for operating on intervals.  It implements a
;;;; simple interval arithmetic package.  Although CMUCL has an interval type
;;;; in numeric-type, we choose to use our own for two reasons:
;;;;
;;;;   1.  This package is simpler than numeric-type
;;;;
;;;;   2.  It makes debugging much easier because you can just strip out these
;;;;   routines and test them independently of CMUCL.  (A big win!)
;;;; One disadvantage is a probable increase in consing because we have to
;;;; create these new interval structures even though numeric-type has
;;;; everything we want to know.  Reason 2 wins for now.
;;; The basic interval type.  It can handle open and closed intervals.  A
;;; bound is open if it is a list containing a number, just like Lisp says.
;;; NIL means unbounded.
;;;
(defstruct (interval
	     (:constructor %make-interval))
(defun make-interval (&key low high)
  (labels ((normalize-bound (val)
	     (cond ((and (floatp val)
			 (float-infinity-p val))
		    ;; Handle infinities
		    nil)
		   ((or (numberp val)
			(eq val nil))
		    ;; Handle any closed bounds
		    val)
		   ((listp val)
		    ;; We have an open bound.  Normalize the numeric bound.
		    ;; If the normalized bound is still a number (not nil),
		    ;; keep the bound open.  Otherwise, the bound is really
		    ;; unbounded, so drop the openness.
		    (let ((new-val (normalize-bound (first val))))
		      (when new-val
			;; Bound exists, so keep it open still
			(list new-val))))
		   (t
		    (error (intl:gettext "Unknown bound type in make-interval!"))))))
    (%make-interval :low (normalize-bound low)
		    :high (normalize-bound high))))

pw's avatar
pw committed
(declaim (inline bound-value set-bound))

;;; Extract the numeric value of a bound.  Return NIL, if X is NIL.
(defun bound-value (x)
  (if (consp x) (car x) x))

;;; Given a number X, create a form suitable as a bound for an interval.
;;; Make the bound open if OPEN-P is T.  NIL remains NIL.
;;;
(defun set-bound (x open-p)
  (if (and x open-p) (list x) x))

;;; Apply the function F to a bound X.  If X is an open bound, then the result
;;; will be open.  IF X is NIL, the result is NIL.
;;;
(defun bound-func (f x)
  (and x
       (with-float-traps-masked (:underflow :overflow :inexact :divide-by-zero)
	 ;; With these traps masked, we might get things like infinity or
	 ;; negative infinity returned.  Check for this and return NIL to
	 ;; indicate unbounded.
	 ;;
	 ;; We also ignore any errors that funcall might cause and
	 ;; return NIL instead to indicate infinity.
	 (let ((y (ignore-errors (funcall f (bound-value x)))))
	   (if (and (floatp y)
		    (float-infinity-p y))
	       nil
	       (set-bound y (consp x)))))))
;;; Apply a binary operator OP to two bounds X and Y.  The result is NIL if
;;; either is NIL.  Otherwise bound is computed and the result is open if
;;; either X or Y is open.
;;;
(defmacro bound-binop (op x y)
  `(and ,x ,y
       (with-float-traps-masked (:underflow :overflow :inexact :divide-by-zero)
	 (set-bound (,op (bound-value ,x)
			 (bound-value ,y))
	            (or (consp ,x) (consp ,y))))))

;;; NUMERIC-TYPE->INTERVAL
;;;
;;; Convert a numeric-type object to an interval object.
dtc's avatar
dtc committed
;;;
(defun numeric-type->interval (x)
  (declare (type numeric-type x))
  (make-interval :low (numeric-type-low x)
		 :high (numeric-type-high x)))

(defun copy-interval-limit (limit)
  (if (numberp limit)
      limit
      (copy-list limit)))

(defun copy-interval (x)
  (declare (type interval x))
  (make-interval :low (copy-interval-limit (interval-low x))
		 :high (copy-interval-limit (interval-high x))))

;;; INTERVAL-SPLIT
;;;
;;; Given a point P contained in the interval X, split X into two interval at
;;; the point P.  If CLOSE-LOWER is T, then the left interval contains P.  If
;;; CLOSE-UPPER is T, the right interval contains P. You can specify both to
;;; be T or NIL.
dtc's avatar
dtc committed
;;;
(defun interval-split (p x &optional close-lower close-upper)
  (declare (type number p)
	   (type interval x))
  ;; Need to be careful if the lower limit is -0.0 and the split point is 0.
  (let ((low (interval-low x)))
    (cond ((and (zerop p)
		(floatp (bound-value low))
		(member (bound-value low) '(-0f0 -0d0)))
	   (list (make-interval :low (copy-interval-limit low)
				:high (float -0d0 (bound-value low)))
		 (make-interval :low (if close-upper (list p) p)
				:high (copy-interval-limit (interval-high x)))))
	  (t
	   (list (make-interval :low (copy-interval-limit (interval-low x))
				:high (if close-lower p (list p)))
		 (make-interval :low (if close-upper (list p) p)
				:high (copy-interval-limit (interval-high x))))))))
;;; INTERVAL-CLOSURE
;;;
;;; Return the closure of the interval.  That is, convert open bounds to
;;; closed bounds.
dtc's avatar
dtc committed
;;;
(defun interval-closure (x)
  (declare (type interval x))
  (make-interval :low (bound-value (interval-low x))
		 :high (bound-value (interval-high x))))

;;; INTERVAL-RANGE-INFO
;;;
dtc's avatar
dtc committed
;;; For an interval X, if X >= POINT, return '+.  If X <= POINT, return
;;; '-. Otherwise return NIL.
dtc's avatar
dtc committed
;;;
(defun interval-range-info (x &optional (point 0))
  (declare (type interval x))
  (labels ((signed->= (x y)
	     ;; If one of the args is a float, we need to do a float
	     ;; comparison to get the correct value when testing for a
	     ;; signed-zero.  That is, we want (>= -0.0 0) to be false.
	     (if (and (zerop x) (zerop y)
		      (or (floatp x) (floatp y)))
		 (>= (float-sign (float x)) (float-sign (float y)))
		 (>= x y))))
    (let ((lo (interval-low x))
	  (hi (interval-high x)))
      ;; FIXME!  We get confused if X is the interval -0d0 to 0d0.
      ;; Special case that.  What else could we be missing?
      (cond ((and (zerop point)
		  (numberp (bound-value lo))
		  (numberp (bound-value hi))
		  (floatp (bound-value lo))
		  (zerop (bound-value lo))
		  (= (bound-value lo) (bound-value hi)))
	     ;; At this point lo = hi = +/- 0.0.
	     (cond ((or (eql (bound-value lo) (bound-value hi))
			(integerp (bound-value hi)))
		    ;; Both bounds are the same kind of signed 0.  Or
		    ;; the high bound is an exact 0.  The sign of the
		    ;; zero tells us the sign of the interval.
		    (if (= (float-sign (bound-value lo)) -1)
			'-
			'+))
		   (t
		    ;; They have different signs
		    nil)))
	    ((and lo (signed->= (bound-value lo) point))
	     '+)
	    ((and hi (signed->= point (bound-value hi)))
	     '-)
	    (t
	     nil)))))

;;; INTERVAL-BOUNDED-P
;;;
;;; Test to see if the interval X is bounded.  HOW determines the test, and
;;; should be either ABOVE, BELOW, or BOTH.
dtc's avatar
dtc committed
;;;
(defun interval-bounded-p (x how)
  (declare (type interval x))
  (ecase how
    ('above
     (interval-high x))
    ('below
     (interval-low x))
    ('both
     (and (interval-low x) (interval-high x)))))

;;; Return the sign of the number, taking into account the sign of
;;; signed-zeros.  An integer 0 has a positive sign here.
(declaim (inline number-sign))
(defun number-sign (x)
  (declare (real x))
  (if (floatp x)
      (float-sign x)
      (if (minusp x) -1.0 1.0)))
;;; Signed zero comparison functions.  Use these functions if we need
;;; to distinguish between signed zeroes.  Thus -0.0 < 0.0, which not
;;; true with normal Lisp comparison functions.

(defun signed-zero-= (x y)
  (declare (real x y))
  (and (= x y)
       (= (number-sign x)
	  (number-sign y))))


(macrolet ((frob (name op1 op2)
	     `(defun ,name (x y)
		(declare (real x y))
		;; Comparison (op1) is true, or the numbers are EQUAL
		;; so we need to compare the signs of the numbers
		;; appropriately.
		(or (,op1 x y)
		    (and (= x y)
			 ;; Convert the numbers to long-floats so we
			 ;; don't get problems converting to
			 ;; shorter floats from longer. 
			 (,op2 (number-sign x)
			       (number-sign y)))))))
  (frob signed-zero-< < <)
  (frob signed-zero-> > >)
  (frob signed-zero-<= < <=)
  (frob signed-zero->= > >=))
;;; INTERVAL-CONTAINS-P
;;;
;;; See if the interval X contains the number P, taking into account that the
;;; interval might not be closed.
dtc's avatar
dtc committed
;;;
(defun interval-contains-p (p x)
  (declare (type number p)
	   (type interval x))
  ;; Does the interval X contain the number P?  This would be a lot easier if
  ;; all intervals were closed!
  (let ((lo (interval-low x))
	(hi (interval-high x)))
    (cond ((and lo hi)
	   ;; The interval is bounded
	   (if (and (signed-zero-<= (bound-value lo) p)
		    (signed-zero-<= p (bound-value hi)))
	       ;; P is definitely in the closure of the interval.
	       ;; We just need to check the end points now.
	       (cond ((signed-zero-= p (bound-value lo))
		      (numberp lo))
		     ((signed-zero-= p (bound-value hi))
		      (numberp hi))
		     (t t))
	       nil))
	  (hi
	   ;; Interval with upper bound
	   (if (signed-zero-< p (bound-value hi))
	       (and (numberp hi) (signed-zero-= p hi))))
	  (lo
	   ;; Interval with lower bound
	   (if (signed-zero-> p (bound-value lo))
	       (and (numberp lo) (signed-zero-= p lo))))
	  (t
	   ;; Interval with no bounds
	   t))))

;;; INTERVAL-INTERSECT-P
;;;
;;; Determine if two intervals X and Y intersect.  Return T if so.  If
;;; CLOSED-INTERVALS-P is T, the treat the intervals as if they were closed.
;;; Otherwise the intervals are treated as they are.
;;; Thus if X = [0, 1) and Y = (1, 2), then they do not intersect because no
;;; element in X is in Y.  However, if CLOSED-INTERVALS-P is T, then they do
;;; intersect because we use the closure of X = [0, 1] and Y = [1, 2] to
;;; determine intersection.
dtc's avatar
dtc committed
;;;
(defun interval-intersect-p (x y &optional closed-intervals-p)
  (declare (type interval x y))
dtc's avatar
dtc committed
  (multiple-value-bind (intersect diff)
      (interval-intersection/difference (if closed-intervals-p
					    (interval-closure x)
					    x)
					(if closed-intervals-p
					    (interval-closure y)
					    y))
    (declare (ignore diff))
    intersect))
;;; Are the two intervals adjacent?  That is, is there a number between the
;;; two intervals that is not an element of either interval?  If so, they are
;;; not adjacent.  For example [0, 1) and [1, 2] are adjacent but [0, 1) and
;;; (1, 2] are not because 1 lies between both intervals.
;;;
(defun interval-adjacent-p (x y)
  (declare (type interval x y))
  (flet ((adjacent (lo hi)
	   ;; Check to see if lo and hi are adjacent.  If either is
	   ;; nil, they can't be adjacent.
	   (when (and lo hi (= (bound-value lo) (bound-value hi)))
	     ;; The bounds are equal.  They are adjacent if one of them is
	     ;; closed (a number).  If both are open (consp), then there is a
	     ;; number that lies between them.
	     (or (numberp lo) (numberp hi)))))
    (or (adjacent (interval-low y) (interval-high x))
	(adjacent (interval-low x) (interval-high y)))))

;;; INTERVAL-INTERSECTION/DIFFERENCE
;;;
;;; Compute the intersection and difference between two intervals.
;;; Two values are returned: the intersection and the difference.
;;;
;;; Let the two intervals be X and Y, and let I and D be the two values
;;; returned by this function.  Then I = X intersect Y.  If I is NIL (the
;;; empty set), then D is X union Y, represented as the list of X and Y.  If I
;;; is not the empty set, then D is (X union Y) - I, which is a list of two
;;; intervals.
;;; For example, let X = [1,5] and Y = [-1,3).  Then I = [1,3) and D = [-1,1)
;;; union [3,5], which is returned as a list of two intervals.
;;;
(defun interval-intersection/difference (x y)
  (declare (type interval x y))
dtc's avatar
dtc committed
  (let ((x-lo (interval-low x))
	(x-hi (interval-high x))
	(y-lo (interval-low y))
	(y-hi (interval-high y)))
    (labels
	((test-lower-bound (p int)
	   ;; Test if the low bound P is in the interval INT.
	       (if (interval-contains-p (bound-value p)
					(interval-closure int))
		   (let ((lo (interval-low int))
			 (hi (interval-high int)))
		     ;; Check for endpoints
		     (cond ((and lo (= (bound-value p) (bound-value lo)))
			    (not (and (numberp p) (consp lo))))
			   ((and hi (= (bound-value p) (bound-value hi)))
			    (and (numberp p) (numberp hi)))
			   (t t))))
	       (not (interval-bounded-p int 'below))))
	 (test-upper-bound (p int)
	   ;; Test if the upper bound P is in the interval INT.
	       (if (interval-contains-p (bound-value p)
					(interval-closure int))
		   (let ((lo (interval-low int))
			 (hi (interval-high int)))
		     ;; Check for endpoints
		     (cond ((and lo (= (bound-value p) (bound-value lo)))
			    (and (numberp p) (numberp lo)))
			   ((and hi (= (bound-value p) (bound-value hi)))
			    (not (and (numberp p) (consp hi))))
			   (t t))))
	       (not (interval-bounded-p int 'above))))
	 (opposite-bound (p)
	   ;; If P is an open bound, make it closed.  If P is a closed bound,
	   ;; make it open.
	   (if (listp p)
	       (first p)
	       (list p))))
      (let ((x-lo-in-y (test-lower-bound x-lo y))
	    (x-hi-in-y (test-upper-bound x-hi y))
	    (y-lo-in-x (test-lower-bound y-lo x))
	    (y-hi-in-x (test-upper-bound y-hi x)))
	(cond ((or x-lo-in-y x-hi-in-y y-lo-in-x y-hi-in-x)
	       ;; Intervals intersect.  Let's compute the intersection and the
	       ;; difference.
	       (multiple-value-bind (lo left-lo left-hi)
		   (cond (x-lo-in-y
			  (values x-lo y-lo (opposite-bound x-lo)))
			 (y-lo-in-x
			  (values y-lo x-lo (opposite-bound y-lo))))
		 (multiple-value-bind (hi right-lo right-hi)
		     (cond (x-hi-in-y
			    (values x-hi (opposite-bound x-hi) y-hi))
			   (y-hi-in-x
			    (values y-hi (opposite-bound y-hi) x-hi)))
		   (values (make-interval :low lo :high hi)
			   (list (make-interval :low left-lo :high left-hi)
				 (make-interval :low right-lo :high right-hi))))))
	      (t
	       (values nil (list x y))))))))

;;; INTERVAL-MERGE-PAIR
;;;
;;; If intervals X and Y intersect, return a new interval that is the union of
;;; the two.  If they do not intersect, return NIL.
dtc's avatar
dtc committed
;;;
(defun interval-merge-pair (x y)
  (declare (type interval x y))
  ;; If x and y intersect or are adjacent, create the union.
  ;; Otherwise return nil
  (when (or (interval-intersect-p x y)
	    (interval-adjacent-p x y))
    (flet ((select-bound (x1 x2 min-op max-op)
	     (let ((x1-val (bound-value x1))
		   (x2-val (bound-value x2)))
	       (cond ((and x1 x2)
		      ;; Both bounds are finite.  Select the right one.
		      (cond ((funcall min-op x1-val x2-val)
			     ;; x1 definitely better
			     x1)
			    ((funcall max-op x1-val x2-val)
			     ;; x2 definitely better
			     x2)
			    (t
			     ;; Bounds are equal.  Select either value and
			     ;; make it open only if both were open.
			     (set-bound x1-val (and (consp x1) (consp x2))))))
		     (t
		      ;; At least one bound is not finite.  The non-finite
		      ;; bound always wins.
      (let* ((x-lo (copy-interval-limit (interval-low x)))
	     (x-hi (copy-interval-limit (interval-high x)))
	     (y-lo (copy-interval-limit (interval-low y)))
	     (y-hi (copy-interval-limit (interval-high y))))
	(make-interval :low (select-bound x-lo y-lo
					  #'signed-zero-< #'signed-zero->)
		       :high (select-bound x-hi y-hi
					   #'signed-zero-> #'signed-zero-<))))))
;; Wrap a handler-case around BODY so that any errors in the body
;; will return a doubly-infinite interval.
;;
;; This is intended to catch things like (* 0f0 n) where n is an
;; integer that won't fit in a single-float.
;;
;; This is a bit heavy-handed since ANY error gets converted to an
;; unbounded interval.  Perhaps some more fine-grained control would
;; be appropriate?

(defmacro with-unbounded-interval-on-error (() &body body)
  `(handler-case
       (progn ,@body)
     (error ()
       (make-interval :low nil :high nil))))
     
;;; Basic arithmetic operations on intervals.  We probably should do true
;;; interval arithmetic here, but it's complicated because we have float and
;;; integer types and bounds can be open or closed.

;;; INTERVAL-NEG
;;;
;;; The negative of an interval
dtc's avatar
dtc committed
;;;
(defun interval-neg (x)
  (declare (type interval x))
  (make-interval :low (bound-func #'- (interval-high x))
		   :high (bound-func #'- (interval-low x))))
		       
;;; INTERVAL-ADD
;;;
;;; Add two intervals
dtc's avatar
dtc committed
;;;
(defun interval-add (x y)
  (declare (type interval x y))
  (with-unbounded-interval-on-error ()
    (make-interval :low (bound-binop + (interval-low x) (interval-low y))
		   :high (bound-binop + (interval-high x) (interval-high y)))))

;;; INTERVAL-SUB
;;;
;;; Subtract two intervals
dtc's avatar
dtc committed
;;;
(defun interval-sub (x y)
  (declare (type interval x y))
  (with-unbounded-interval-on-error ()
    (make-interval :low (bound-binop - (interval-low x) (interval-high y))
		   :high (bound-binop - (interval-high x) (interval-low y)))))

;;; INTERVAL-MUL
;;;
;;; Multiply two intervals
dtc's avatar
dtc committed
;;;
(defun interval-mul (x y)
  (declare (type interval x y))
  (flet ((bound-mul (x y)
	   (cond ((or (null x) (null y))
		  ;; Multiply by infinity is infinity
		  nil)
		 ((or (and (numberp x) (zerop x))
		      (and (numberp y) (zerop y)))
		  ;; Multiply by closed zero is special.  The result is always
		  ;; a closed bound.  But don't replace this with zero; we
		  ;; want the multiplication to produce the correct signed
		  ;; zero, if needed.
		  (* (bound-value x) (bound-value y)))
		 ((or (and (floatp x) (float-infinity-p x))
		      (and (floatp y) (float-infinity-p y)))
		  ;; Infinity times anything is infinity
		  nil)
		 (t
		  ;; General multiply.  The result is open if either is open.
		  (bound-binop * x y)))))
    (let ((x-range (interval-range-info x))
	  (y-range (interval-range-info y)))
      (cond ((null x-range)
	     ;; Split x into two and multiply each separately
	     (destructuring-bind (x- x+)
		 (interval-split 0 x t t)
	       (interval-merge-pair (interval-mul x- y)
				    (interval-mul x+ y))))
	    ((null y-range)
	     ;; Split y into two and multiply each separately
	     (destructuring-bind (y- y+)
		 (interval-split 0 y t t)
	       (interval-merge-pair (interval-mul x y-)
				    (interval-mul x y+))))
	    ((eq x-range '-)
	     (interval-neg (interval-mul (interval-neg x) y)))
	    ((eq y-range '-)
	     (interval-neg (interval-mul x (interval-neg y))))
	    ((and (eq x-range '+) (eq y-range '+))
	     ;; If we are here, X and Y are both positive
	     (with-unbounded-interval-on-error ()
	       (make-interval :low (bound-mul (interval-low x) (interval-low y))
			      :high (bound-mul (interval-high x)
					       (interval-high y)))))
	     (error (intl:gettext "This shouldn't happen!")))))))

;;; INTERVAL-DIV
;;;
;;; Divide two intervals.
dtc's avatar
dtc committed
;;;
(defun interval-div (top bot)
  (declare (type interval top bot))
  (flet ((bound-div (x y y-low-p)
	   ;; Compute x/y
	   (cond ((null y)
		  ;; Divide by infinity means result is 0.  However, we need
		  ;; to watch out for the sign of the result, to correctly
		  ;; handle signed zeros.  We also need to watch out for
		  ;; positive or negative infinity.
		  (if (floatp (bound-value x))
		      (if y-low-p
			  (- (float-sign (bound-value x) 0.0))
			  (float-sign (bound-value x) 0.0))
		      0))
		 ((zerop (bound-value y))
		  ;; Divide by zero means result is infinity
		  nil)
		 ((and (numberp x) (zerop x))
		  ;; Zero divided by anything is zero.
		  x)
		 (t
		  (bound-binop / x y)))))
    (let ((top-range (interval-range-info top))
	  (bot-range (interval-range-info bot)))
      (cond ((null bot-range)
	     ;; The denominator contains zero, so anything goes!
	     (make-interval :low nil :high nil))
	    ((eq bot-range '-)
	     ;; Denominator is negative so flip the sign, compute the result,
	     ;; and flip it back.
	     (interval-neg (interval-div top (interval-neg bot))))
	    ((null top-range)
	     ;; Split top into two positive and negative parts, and divide
	     ;; each separately
	     (destructuring-bind (top- top+)
		 (interval-split 0 top t t)
	       (interval-merge-pair (interval-div top- bot)
				    (interval-div top+ bot))))
	    ((eq top-range '-)
	     ;; Top is negative so flip the sign, divide, and flip the sign of
	     ;; the result.
	     (interval-neg (interval-div (interval-neg top) bot)))
	    ((and (eq top-range '+) (eq bot-range '+))
	     ;; The easy case, sort of.  Both are positive, so we know that
	     ;; the lower bound must be >= +0.  If bound-div returns NIL, we
	     ;; were dividing by zero, so replace that result with 0 or '(0),
	     ;; depending on whether the numerator contains 0.  This isn't
	     ;; quite right, but until we make the interval and numeric-type
	     ;; routines understand the concept of infinity better, this will
	     ;; have to do for now. (RLT)
	     (with-unbounded-interval-on-error ()
	       (make-interval :low (or (bound-div (interval-low top)
						  (interval-high bot) nil)
				       (if (interval-contains-p 0 top)
					   0
					   '(0)))
			      :high (bound-div (interval-high top)
					       (interval-low bot) t))))
	     (error (intl:gettext "This shouldn't happen!")))))))
;;; Apply the function F to the interval X.  If X = [a, b], then the result is
;;; [f(a), f(b)].  It is up to the user to make sure the result makes sense.
;;; It will if F is monotonic increasing (or non-decreasing).
dtc's avatar
dtc committed
;;;
(defun interval-func (f x)
  (declare (type interval x))
  (let ((lo (bound-func f (interval-low x)))
	(hi (bound-func f (interval-high x))))
    (make-interval :low lo :high hi)))

;;; INTERVAL-<
;;;
;;; Return T if X < Y.  That is every number in the interval X is always less
;;; than any number in the interval Y.
dtc's avatar
dtc committed
;;;
(defun interval-< (x y)
  (declare (type interval x y))
  ;; X < Y only if X is bounded above, Y is bounded below, and they don't
  ;; overlap.
  (when (and (interval-bounded-p x 'above)
	     (interval-bounded-p y 'below))
    ;; Intervals are bounded in the appropriate way.  Make sure they don't
    ;; overlap.
    (let ((left (interval-high x))
	  (right (interval-low y))) 
      (cond ((> (bound-value left)
		(bound-value right))
	     ;; Definitely overlap so result is NIL
	     nil)
	    ((< (bound-value left)
		(bound-value right))
	     ;; Definitely don't touch, so result is T
	     t)
	    (t
	     ;; Limits are equal.  Check for open or closed bounds.
	     ;; Don't overlap if one or the other are open.
	     (or (consp left) (consp right)))))))

;;; Return T if X >= Y.  That is, every number in the interval X is always
;;; greater than any number in the interval Y.
;;;
(defun interval->= (x y)
  (declare (type interval x y))
  ;; X >= Y if lower bound of X >= upper bound of Y
  (when (and (interval-bounded-p x 'below)
	     (interval-bounded-p y 'above))
    (>= (bound-value (interval-low x)) (bound-value (interval-high y)))))

;;; INTERVAL-ABS
;;;
;;; Return an interval that is the absolute value of X.  Thus, if X = [-1 10],
;;; the result is [0, 10].
dtc's avatar
dtc committed
;;;
(defun interval-abs (x)
  (declare (type interval x))
  (case (interval-range-info x)
    ('+
    ('-
     (interval-neg x))
    (t
     (destructuring-bind (x- x+)
	 (interval-split 0 x t t)
       (interval-merge-pair (interval-neg x-) x+)))))

;;; INTERVAL-SQR
;;;
;;; Compute the square of an interval.
dtc's avatar
dtc committed
;;;
(defun interval-sqr (x)
  (declare (type interval x))
  (interval-func #'(lambda (x) (* x x))
		 (interval-abs x)))
pw's avatar
pw committed

wlott's avatar
wlott committed

;;;; Numeric Derive-Type methods:

;;; Derive-Integer-Type  --  Internal
;;;
;;;    Utility for defining derive-type methods of integer operations.  If the
;;; types of both X and Y are integer types, then we compute a new integer type
;;; with bounds determined Fun when applied to X and Y.  Otherwise, we use
;;; Numeric-Contagion.
;;;
(defun derive-integer-type (x y fun)
  (declare (type continuation x y) (type function fun))
  (let ((x (continuation-type x))
	(y (continuation-type y)))
    (if (and (numeric-type-p x) (numeric-type-p y)
	     (eq (numeric-type-class x) 'integer)
	     (eq (numeric-type-class y) 'integer)
	     (eq (numeric-type-complexp x) :real)
	     (eq (numeric-type-complexp y) :real))
	(multiple-value-bind (low high)
			     (funcall fun x y)
	  (make-numeric-type :class 'integer  :complexp :real
			     :low low  :high high))
	(numeric-contagion x y))))

;; Simple utility to flatten a list
(defun flatten-list (x)
  (labels ((flatten-helper (x r);; 'r' is the stuff to the 'right'.
	     (cond ((null x) r)
		   ((atom x)
		    (cons x r))
		   (t (flatten-helper (car x)
				      (flatten-helper (cdr x) r))))))
    (flatten-helper x nil)))

;;; Take some type of continuation and massage it so that we get a list of the
;;; constituent types.  If ARG is *EMPTY-TYPE*, return NIL to indicate
;;; failure.
(defun prepare-arg-for-derive-type (arg)
  (flet ((listify (arg)
	   (typecase arg
	     (numeric-type
	      (list arg))
	     (union-type
	      (union-type-types arg))
	     (t
	      (list arg)))))
    (unless (eq arg *empty-type*)
      ;; Make sure all args are some type of numeric-type.  For member types,
      ;; convert the list of members into a union of equivalent single-element
      ;; member-type's.
      (let ((new-args nil))
	(dolist (arg (listify arg))
	  (if (member-type-p arg)
	      ;; Run down the list of members and convert to a list of
	      ;; member types.
	      (dolist (member (member-type-members arg))
		(push (if (numberp member)
			  (specifier-type `(eql ,member))
			  *empty-type*)
		      new-args))
	      (push arg new-args)))
	(unless (member *empty-type* new-args)
	  new-args)))))
;;; Convert from the standard type convention for which -0.0 and 0.0 and equal
;;; to an intermediate convention for which they are considered different
;;; which is more natural for some of the optimisers.
;;;
(defun convert-numeric-type (type)
  (declare (type numeric-type type))
dtc's avatar
dtc committed
  ;; Only convert real float interval delimiters types.
  (if (eq (numeric-type-complexp type) :real)
      (let* ((lo (numeric-type-low type))
	     (lo-val (bound-value lo))
	     (lo-float-zero-p (and lo (floatp lo-val) (= lo-val 0.0)))
	     (hi (numeric-type-high type))
	     (hi-val (bound-value hi))
	     (hi-float-zero-p (and hi (floatp hi-val) (= hi-val 0.0))))
	(if (or lo-float-zero-p hi-float-zero-p)
	    (make-numeric-type