X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fsrctran.lisp;h=462e4494f7ca2ca55fd99668bdcb67c7fca3c22f;hb=22b819c0cd0ca0ea5be52ba280b9e9e0b8e86210;hp=adc61328330788114dd1f97db279f1d510e9d74e;hpb=4a0ab5193096ca70dbbf43bb21418544f6d018b7;p=sbcl.git diff --git a/src/compiler/srctran.lisp b/src/compiler/srctran.lisp index adc6132..462e449 100644 --- a/src/compiler/srctran.lisp +++ b/src/compiler/srctran.lisp @@ -1,6 +1,6 @@ ;;;; 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. +;;;; within the compiler. FIXME: and other IR1 transforms and stuff. ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. @@ -15,35 +15,36 @@ ;;; 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)) +(define-source-transform not (x) `(if ,x nil t)) +(define-source-transform null (x) `(if ,x nil t)) ;;; ENDP is just NULL with a LIST assertion. The assertion will be ;;; optimized away when SAFETY optimization is low; hopefully that ;;; is consistent with ANSI's "should return an error". -(def-source-transform endp (x) `(null (the list ,x))) +(define-source-transform endp (x) `(null (the list ,x))) ;;; 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)) +(define-source-transform identity (x) `(prog1 ,x)) +(define-source-transform values (x) `(prog1 ,x)) -;;; Bind the values and make a closure that returns them. -(def-source-transform constantly (value) - (let ((rest (gensym "CONSTANTLY-REST-"))) - `(lambda (&rest ,rest) - (declare (ignore ,rest)) - ,value))) +;;; Bind the value and make a closure that returns it. +(define-source-transform constantly (value) + (with-unique-names (rest n-value) + `(let ((,n-value ,value)) + (lambda (&rest ,rest) + (declare (ignore ,rest)) + ,n-value)))) ;;; 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) +(deftransform complement ((fun) * * :node node) "open code" (multiple-value-bind (min max) - (function-type-nargs (continuation-type fun)) + (fun-type-nargs (continuation-type fun)) (cond ((and min (eql min max)) (let ((dums (make-gensym-list min))) @@ -62,7 +63,7 @@ ;;; Translate CxR into CAR/CDR combos. (defun source-transform-cxr (form) - (if (or (byte-compiling) (/= (length form) 2)) + (if (/= (length form) 2) (values nil t) (let ((name (symbol-name (car form)))) (do ((i (- (length name) 2) (1- i)) @@ -97,31 +98,31 @@ ;;; 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)) +(define-source-transform first (x) `(car ,x)) +(define-source-transform rest (x) `(cdr ,x)) +(define-source-transform second (x) `(cadr ,x)) +(define-source-transform third (x) `(caddr ,x)) +(define-source-transform fourth (x) `(cadddr ,x)) +(define-source-transform fifth (x) `(nth 4 ,x)) +(define-source-transform sixth (x) `(nth 5 ,x)) +(define-source-transform seventh (x) `(nth 6 ,x)) +(define-source-transform eighth (x) `(nth 7 ,x)) +(define-source-transform ninth (x) `(nth 8 ,x)) +(define-source-transform tenth (x) `(nth 9 ,x)) ;;; Translate RPLACx to LET and SETF. -(def-source-transform rplaca (x y) +(define-source-transform rplaca (x y) (once-only ((n-x x)) `(progn (setf (car ,n-x) ,y) ,n-x))) -(def-source-transform rplacd (x y) +(define-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))) +(define-source-transform nth (n l) `(car (nthcdr ,n ,l))) (defvar *default-nthcdr-open-code-limit* 6) (defvar *extreme-nthcdr-open-code-limit* 20) @@ -145,56 +146,56 @@ ;;;; arithmetic and numerology -(def-source-transform plusp (x) `(> ,x 0)) -(def-source-transform minusp (x) `(< ,x 0)) -(def-source-transform zerop (x) `(= ,x 0)) +(define-source-transform plusp (x) `(> ,x 0)) +(define-source-transform minusp (x) `(< ,x 0)) +(define-source-transform zerop (x) `(= ,x 0)) -(def-source-transform 1+ (x) `(+ ,x 1)) -(def-source-transform 1- (x) `(- ,x 1)) +(define-source-transform 1+ (x) `(+ ,x 1)) +(define-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))) +(define-source-transform oddp (x) `(not (zerop (logand ,x 1)))) +(define-source-transform evenp (x) `(zerop (logand ,x 1))) ;;; Note that all the integer division functions are available for ;;; inline expansion. -;;; FIXME: DEF-FROB instead of FROB -(macrolet ((frob (fun) - `(def-source-transform ,fun (x &optional (y nil y-p)) +(macrolet ((deffrob (fun) + `(define-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) + (deffrob truncate) + (deffrob round) #-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) - (frob floor) + (deffrob floor) #-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) - (frob ceiling)) - -(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))) -(def-source-transform logtest (x y) `(not (zerop (logand ,x ,y)))) -(def-source-transform logbitp (index integer) + (deffrob ceiling)) + +(define-source-transform lognand (x y) `(lognot (logand ,x ,y))) +(define-source-transform lognor (x y) `(lognot (logior ,x ,y))) +(define-source-transform logandc1 (x y) `(logand (lognot ,x) ,y)) +(define-source-transform logandc2 (x y) `(logand ,x (lognot ,y))) +(define-source-transform logorc1 (x y) `(logior (lognot ,x) ,y)) +(define-source-transform logorc2 (x y) `(logior ,x (lognot ,y))) +(define-source-transform logtest (x y) `(not (zerop (logand ,x ,y)))) +(define-source-transform logbitp (index integer) `(not (zerop (logand (ash 1 ,index) ,integer)))) -(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) +(define-source-transform byte (size position) + `(cons ,size ,position)) +(define-source-transform byte-size (spec) `(car ,spec)) +(define-source-transform byte-position (spec) `(cdr ,spec)) +(define-source-transform ldb-test (bytespec integer) `(not (zerop (mask-field ,bytespec ,integer)))) ;;; 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) +(define-source-transform numerator (num) (once-only ((n-num `(the rational ,num))) `(if (ratiop ,n-num) (%numerator ,n-num) ,n-num))) -(def-source-transform denominator (num) +(define-source-transform denominator (num) (once-only ((n-num `(the rational ,num))) `(if (ratiop ,n-num) (%denominator ,n-num) @@ -218,9 +219,6 @@ ;;;; numeric-type has everything we want to know. Reason 2 wins for ;;;; now. -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(progn - ;;; 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. @@ -248,7 +246,7 @@ ;; The bound exists, so keep it open still. (list new-val)))) (t - (error "Unknown bound type in make-interval!"))))) + (error "unknown bound type in MAKE-INTERVAL"))))) (%make-interval :low (normalize-bound low) :high (normalize-bound high)))) @@ -261,6 +259,7 @@ ;;; 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) + (declare (type function f)) (and x (with-float-traps-masked (:underflow :overflow :inexact :divide-by-zero) ;; With these traps masked, we might get things like infinity @@ -359,11 +358,11 @@ (defun interval-bounded-p (x how) (declare (type interval x)) (ecase how - ('above + (above (interval-high x)) - ('below + (below (interval-low x)) - ('both + (both (and (interval-low x) (interval-high x))))) ;;; signed zero comparison functions. Use these functions if we need @@ -629,11 +628,12 @@ ((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 - (make-interval :low (bound-mul (interval-low x) (interval-low y)) - :high (bound-mul (interval-high x) (interval-high y)))) + ;; If we are here, X and Y are both positive. + (make-interval + :low (bound-mul (interval-low x) (interval-low y)) + :high (bound-mul (interval-high x) (interval-high y)))) (t - (error "This shouldn't happen!")))))) + (bug "excluded case in INTERVAL-MUL")))))) ;;; Divide two intervals. (defun interval-div (top bot) @@ -678,18 +678,20 @@ ;; sign of the result. (interval-neg (interval-div (interval-neg top) bot))) ((and (eq top-range '+) (eq bot-range '+)) - ;; The easy case - (make-interval :low (bound-div (interval-low top) (interval-high bot) t) - :high (bound-div (interval-high top) (interval-low bot) nil))) + ;; the easy case + (make-interval + :low (bound-div (interval-low top) (interval-high bot) t) + :high (bound-div (interval-high top) (interval-low bot) nil))) (t - (error "This shouldn't happen!")))))) + (bug "excluded case in INTERVAL-DIV")))))) ;;; 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). (defun interval-func (f x) - (declare (type interval x)) + (declare (type function f) + (type interval x)) (let ((lo (bound-func f (interval-low x))) (hi (bound-func f (interval-high x)))) (make-interval :low lo :high hi))) @@ -734,9 +736,9 @@ (defun interval-abs (x) (declare (type interval x)) (case (interval-range-info x) - ('+ + (+ (copy-interval x)) - ('- + (- (interval-neg x)) (t (destructuring-bind (x- x+) (interval-split 0 x t t) @@ -745,9 +747,8 @@ ;;; Compute the square of an interval. (defun interval-sqr (x) (declare (type interval x)) - (interval-func #'(lambda (x) (* x x)) + (interval-func (lambda (x) (* x x)) (interval-abs x))) -) ; PROGN ;;;; numeric DERIVE-TYPE methods @@ -771,9 +772,6 @@ :high high)) (numeric-contagion x y)))) -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(progn - ;;; simple utility to flatten a list (defun flatten-list (x) (labels ((flatten-helper (x r);; 'r' is the stuff to the 'right'. @@ -818,7 +816,6 @@ ;;; are equal to an intermediate convention for which they are ;;; considered different which is more natural for some of the ;;; optimisers. -#!-negative-zero-is-not-zero (defun convert-numeric-type (type) (declare (type numeric-type type)) ;;; Only convert real float interval delimiters types. @@ -837,11 +834,11 @@ :low (if lo-float-zero-p (if (consp lo) (list (float 0.0 lo-val)) - (float -0.0 lo-val)) + (float (load-time-value (make-unportable-float :single-float-negative-zero)) lo-val)) lo) :high (if hi-float-zero-p (if (consp hi) - (list (float -0.0 hi-val)) + (list (float (load-time-value (make-unportable-float :single-float-negative-zero)) hi-val)) (float 0.0 hi-val)) hi)) type)) @@ -851,7 +848,6 @@ ;;; Convert back from the intermediate convention for which -0.0 and ;;; 0.0 are considered different to the standard type convention for ;;; which and equal. -#!-negative-zero-is-not-zero (defun convert-back-numeric-type (type) (declare (type numeric-type type)) ;;; Only convert real float interval delimiters types. @@ -939,7 +935,6 @@ type)) ;;; Convert back a possible list of numeric types. -#!-negative-zero-is-not-zero (defun convert-back-numeric-type-list (type-list) (typecase type-list (list @@ -961,7 +956,9 @@ ;;; FIXME: MAKE-CANONICAL-UNION-TYPE and CONVERT-MEMBER-TYPE probably ;;; belong in the kernel's type logic, invoked always, instead of in -;;; the compiler, invoked only during some type optimizations. +;;; the compiler, invoked only during some type optimizations. (In +;;; fact, as of 0.pre8.100 or so they probably are, under +;;; MAKE-MEMBER-TYPE, so probably this code can be deleted) ;;; Take a list of types and return a canonical type specifier, ;;; combining any MEMBER types together. If both positive and negative @@ -976,24 +973,15 @@ (setf members (union members (member-type-members type))) (push type misc-types))) #!+long-float - (when (null (set-difference '(-0l0 0l0) members)) - #!-negative-zero-is-not-zero - (push (specifier-type '(long-float 0l0 0l0)) misc-types) - #!+negative-zero-is-not-zero - (push (specifier-type '(long-float -0l0 0l0)) misc-types) - (setf members (set-difference members '(-0l0 0l0)))) - (when (null (set-difference '(-0d0 0d0) members)) - #!-negative-zero-is-not-zero - (push (specifier-type '(double-float 0d0 0d0)) misc-types) - #!+negative-zero-is-not-zero - (push (specifier-type '(double-float -0d0 0d0)) misc-types) - (setf members (set-difference members '(-0d0 0d0)))) - (when (null (set-difference '(-0f0 0f0) members)) - #!-negative-zero-is-not-zero - (push (specifier-type '(single-float 0f0 0f0)) misc-types) - #!+negative-zero-is-not-zero - (push (specifier-type '(single-float -0f0 0f0)) misc-types) - (setf members (set-difference members '(-0f0 0f0)))) + (when (null (set-difference `(,(load-time-value (make-unportable-float :long-float-negative-zero)) 0.0l0) members)) + (push (specifier-type '(long-float 0.0l0 0.0l0)) misc-types) + (setf members (set-difference members `(,(load-time-value (make-unportable-float :long-float-negative-zero)) 0.0l0)))) + (when (null (set-difference `(,(load-time-value (make-unportable-float :double-float-negative-zero)) 0.0d0) members)) + (push (specifier-type '(double-float 0.0d0 0.0d0)) misc-types) + (setf members (set-difference members `(,(load-time-value (make-unportable-float :double-float-negative-zero)) 0.0d0)))) + (when (null (set-difference `(,(load-time-value (make-unportable-float :single-float-negative-zero)) 0.0f0) members)) + (push (specifier-type '(single-float 0.0f0 0.0f0)) misc-types) + (setf members (set-difference members `(,(load-time-value (make-unportable-float :single-float-negative-zero)) 0.0f0)))) (if members (apply #'type-union (make-member-type :members members) misc-types) (apply #'type-union misc-types)))) @@ -1024,8 +1012,7 @@ (defun one-arg-derive-type (arg derive-fcn member-fcn &optional (convert-type t)) (declare (type function derive-fcn) - (type (or null function) member-fcn) - #!+negative-zero-is-not-zero (ignore convert-type)) + (type (or null function) member-fcn)) (let ((arg-list (prepare-arg-for-derive-type (continuation-type arg)))) (when arg-list (flet ((deriver (x) @@ -1041,20 +1028,14 @@ ;; Otherwise convert to a numeric type. (let ((result-type-list (funcall derive-fcn (convert-member-type x)))) - #!-negative-zero-is-not-zero (if convert-type (convert-back-numeric-type-list result-type-list) - result-type-list) - #!+negative-zero-is-not-zero - result-type-list))) + result-type-list)))) (numeric-type - #!-negative-zero-is-not-zero (if convert-type (convert-back-numeric-type-list (funcall derive-fcn (convert-numeric-type x))) - (funcall derive-fcn x)) - #!+negative-zero-is-not-zero - (funcall derive-fcn x)) + (funcall derive-fcn x))) (t *universal-type*)))) ;; Run down the list of args and derive the type of each one, @@ -1077,10 +1058,8 @@ ;;; positive. If we didn't do this, we wouldn't be able to tell. (defun two-arg-derive-type (arg1 arg2 derive-fcn fcn &optional (convert-type t)) - #!+negative-zero-is-not-zero - (declare (ignore convert-type)) - (flet (#!-negative-zero-is-not-zero - (deriver (x y same-arg) + (declare (type function derive-fcn fcn)) + (flet ((deriver (x y same-arg) (cond ((and (member-type-p x) (member-type-p y)) (let* ((x (first (member-type-members x))) (y (first (member-type-members y))) @@ -1117,26 +1096,6 @@ (convert-back-numeric-type-list result) result))) (t - *universal-type*))) - #!+negative-zero-is-not-zero - (deriver (x y same-arg) - (cond ((and (member-type-p x) (member-type-p y)) - (let* ((x (first (member-type-members x))) - (y (first (member-type-members y))) - (result (with-float-traps-masked - (:underflow :overflow :divide-by-zero) - (funcall fcn x y)))) - (if result - (make-member-type :members (list result))))) - ((and (member-type-p x) (numeric-type-p y)) - (let ((x (convert-member-type x))) - (funcall derive-fcn x y same-arg))) - ((and (numeric-type-p x) (member-type-p y)) - (let ((y (convert-member-type y))) - (funcall derive-fcn x y same-arg))) - ((and (numeric-type-p x) (numeric-type-p y)) - (funcall derive-fcn x y same-arg)) - (t *universal-type*)))) (let ((same-arg (same-leaf-ref-p arg1 arg2)) (a1 (prepare-arg-for-derive-type (continuation-type arg1))) @@ -1162,8 +1121,6 @@ (if (rest results) (make-canonical-union-type results) (first results))))))) - -) ; PROGN #+sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (progn @@ -1349,110 +1306,30 @@ ) ; PROGN - -;;; KLUDGE: All this ASH optimization is suppressed under CMU CL -;;; because as of version 2.4.6 for Debian, CMU CL blows up on (ASH -;;; 1000000000 -100000000000) (i.e. ASH of two bignums yielding zero) -;;; and it's hard to avoid that calculation in here. -#-(and cmu sb-xc-host) -(progn -#+sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(defoptimizer (ash derive-type) ((n shift)) - ;; Large resulting bounds are easy to generate but are not - ;; particularly useful, so an open outer bound is returned for a - ;; shift greater than 64 - the largest word size of any of the ports. - ;; Large negative shifts are also problematic as the ASH - ;; implementation only accepts shifts greater than - ;; MOST-NEGATIVE-FIXNUM. These issues are handled by two local - ;; functions: - ;; ASH-OUTER: Perform the shift when within an acceptable range, - ;; otherwise return an open bound. - ;; ASH-INNER: Perform the shift when within range, limited to a - ;; maximum of 64, otherwise returns the inner limit. - ;; - ;; FIXME: The magic number 64 should be given a mnemonic name as a - ;; symbolic constant -- perhaps +MAX-REGISTER-SIZE+. And perhaps is - ;; should become an architecture-specific SB!VM:+MAX-REGISTER-SIZE+ - ;; instead of trying to have a single magic number which covers - ;; all possible ports. - (flet ((ash-outer (n s) - (when (and (fixnump s) - (<= s 64) - (> s sb!vm:*target-most-negative-fixnum*)) - (ash n s))) - (ash-inner (n s) - (if (and (fixnump s) - (> s sb!vm:*target-most-negative-fixnum*)) - (ash n (min s 64)) - (if (minusp n) -1 0)))) - (or (let ((n-type (continuation-type n))) - (when (numeric-type-p n-type) - (let ((n-low (numeric-type-low n-type)) - (n-high (numeric-type-high n-type))) - (if (constant-continuation-p shift) - (let ((shift (continuation-value shift))) - (make-numeric-type :class 'integer - :complexp :real - :low (when n-low (ash n-low shift)) - :high (when n-high (ash n-high shift)))) - (let ((s-type (continuation-type shift))) - (when (numeric-type-p s-type) - (let* ((s-low (numeric-type-low s-type)) - (s-high (numeric-type-high s-type)) - (low-slot (when n-low - (if (minusp n-low) - (ash-outer n-low s-high) - (ash-inner n-low s-low)))) - (high-slot (when n-high - (if (minusp n-high) - (ash-inner n-high s-low) - (ash-outer n-high s-high))))) - (make-numeric-type :class 'integer - :complexp :real - :low low-slot - :high high-slot)))))))) - *universal-type*)) - (or (let ((n-type (continuation-type n))) - (when (numeric-type-p n-type) - (let ((n-low (numeric-type-low n-type)) - (n-high (numeric-type-high n-type))) - (if (constant-continuation-p shift) - (let ((shift (continuation-value shift))) - (make-numeric-type :class 'integer - :complexp :real - :low (when n-low (ash n-low shift)) - :high (when n-high (ash n-high shift)))) - (let ((s-type (continuation-type shift))) - (when (numeric-type-p s-type) - (let ((s-low (numeric-type-low s-type)) - (s-high (numeric-type-high s-type))) - (if (and s-low s-high (<= s-low 64) (<= s-high 64)) - (make-numeric-type :class 'integer - :complexp :real - :low (when n-low - (min (ash n-low s-high) - (ash n-low s-low))) - :high (when n-high - (max (ash n-high s-high) - (ash n-high s-low)))) - (make-numeric-type :class 'integer - :complexp :real))))))))) - *universal-type*)) - -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (defun ash-derive-type-aux (n-type shift same-arg) (declare (ignore same-arg)) + ;; KLUDGE: All this ASH optimization is suppressed under CMU CL for + ;; some bignum cases because as of version 2.4.6 for Debian and 18d, + ;; CMU CL blows up on (ASH 1000000000 -100000000000) (i.e. ASH of + ;; two bignums yielding zero) and it's hard to avoid that + ;; calculation in here. + #+(and cmu sb-xc-host) + (when (and (or (typep (numeric-type-low n-type) 'bignum) + (typep (numeric-type-high n-type) 'bignum)) + (or (typep (numeric-type-low shift) 'bignum) + (typep (numeric-type-high shift) 'bignum))) + (return-from ash-derive-type-aux *universal-type*)) (flet ((ash-outer (n s) (when (and (fixnump s) (<= s 64) - (> s sb!vm:*target-most-negative-fixnum*)) + (> s sb!xc:most-negative-fixnum)) (ash n s))) ;; KLUDGE: The bare 64's here should be related to ;; symbolic machine word size values somehow. (ash-inner (n s) (if (and (fixnump s) - (> s sb!vm:*target-most-negative-fixnum*)) + (> s sb!xc:most-negative-fixnum)) (ash n (min s 64)) (if (minusp n) -1 0)))) (or (and (csubtypep n-type (specifier-type 'integer)) @@ -1472,10 +1349,8 @@ (ash-outer n-high s-high)))))) *universal-type*))) -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (defoptimizer (ash derive-type) ((n shift)) (two-arg-derive-type n shift #'ash-derive-type-aux #'ash)) -) ; PROGN #+sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (macrolet ((frob (fun) @@ -1486,12 +1361,8 @@ (values (if hi (,fun hi) nil) (if lo (,fun lo) nil)))))) (defoptimizer (%negate derive-type) ((num)) - (derive-integer-type num num (frob -))) + (derive-integer-type num num (frob -)))) - (defoptimizer (lognot derive-type) ((int)) - (derive-integer-type int int (frob lognot)))) - -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (defoptimizer (lognot derive-type) ((int)) (derive-integer-type int int (lambda (type type2) @@ -1743,7 +1614,7 @@ ;;; Define optimizers for FLOOR and CEILING. (macrolet - ((frob-opt (name q-name r-name) + ((def (name q-name r-name) (let ((q-aux (symbolicate q-name "-AUX")) (r-aux (symbolicate r-name "-AUX"))) `(progn @@ -1807,54 +1678,52 @@ (when (and quot rem) (make-values-type :required (list quot rem)))))))))) - ;; FIXME: DEF-FROB-OPT, not just FROB-OPT - (frob-opt floor floor-quotient-bound floor-rem-bound) - (frob-opt ceiling ceiling-quotient-bound ceiling-rem-bound)) + (def floor floor-quotient-bound floor-rem-bound) + (def ceiling ceiling-quotient-bound ceiling-rem-bound)) ;;; Define optimizers for FFLOOR and FCEILING -(macrolet - ((frob-opt (name q-name r-name) - (let ((q-aux (symbolicate "F" q-name "-AUX")) - (r-aux (symbolicate r-name "-AUX"))) - `(progn - ;; Compute type of quotient (first) result. - (defun ,q-aux (number-type divisor-type) - (let* ((number-interval - (numeric-type->interval number-type)) - (divisor-interval - (numeric-type->interval divisor-type)) - (quot (,q-name (interval-div number-interval - divisor-interval))) - (res-type (numeric-contagion number-type divisor-type))) - (make-numeric-type - :class (numeric-type-class res-type) - :format (numeric-type-format res-type) - :low (interval-low quot) - :high (interval-high quot)))) - - (defoptimizer (,name derive-type) ((number divisor)) - (flet ((derive-q (n d same-arg) - (declare (ignore same-arg)) - (if (and (numeric-type-real-p n) - (numeric-type-real-p d)) - (,q-aux n d) - *empty-type*)) - (derive-r (n d same-arg) - (declare (ignore same-arg)) - (if (and (numeric-type-real-p n) - (numeric-type-real-p d)) - (,r-aux n d) - *empty-type*))) - (let ((quot (two-arg-derive-type - number divisor #'derive-q #',name)) - (rem (two-arg-derive-type - number divisor #'derive-r #'mod))) - (when (and quot rem) - (make-values-type :required (list quot rem)))))))))) - - ;; FIXME: DEF-FROB-OPT, not just FROB-OPT - (frob-opt ffloor floor-quotient-bound floor-rem-bound) - (frob-opt fceiling ceiling-quotient-bound ceiling-rem-bound)) +(macrolet ((def (name q-name r-name) + (let ((q-aux (symbolicate "F" q-name "-AUX")) + (r-aux (symbolicate r-name "-AUX"))) + `(progn + ;; Compute type of quotient (first) result. + (defun ,q-aux (number-type divisor-type) + (let* ((number-interval + (numeric-type->interval number-type)) + (divisor-interval + (numeric-type->interval divisor-type)) + (quot (,q-name (interval-div number-interval + divisor-interval))) + (res-type (numeric-contagion number-type + divisor-type))) + (make-numeric-type + :class (numeric-type-class res-type) + :format (numeric-type-format res-type) + :low (interval-low quot) + :high (interval-high quot)))) + + (defoptimizer (,name derive-type) ((number divisor)) + (flet ((derive-q (n d same-arg) + (declare (ignore same-arg)) + (if (and (numeric-type-real-p n) + (numeric-type-real-p d)) + (,q-aux n d) + *empty-type*)) + (derive-r (n d same-arg) + (declare (ignore same-arg)) + (if (and (numeric-type-real-p n) + (numeric-type-real-p d)) + (,r-aux n d) + *empty-type*))) + (let ((quot (two-arg-derive-type + number divisor #'derive-q #',name)) + (rem (two-arg-derive-type + number divisor #'derive-r #'mod))) + (when (and quot rem) + (make-values-type :required (list quot rem)))))))))) + + (def ffloor floor-quotient-bound floor-rem-bound) + (def fceiling ceiling-quotient-bound ceiling-rem-bound)) ;;; functions to compute the bounds on the quotient and remainder for ;;; the FLOOR function @@ -2232,7 +2101,7 @@ (defoptimizer (random derive-type) ((bound &optional state)) (one-arg-derive-type bound #'random-derive-type-aux nil)) -;;;; logical derive-type methods +;;;; DERIVE-TYPE methods for LOGAND, LOGIOR, and friends ;;; Return the maximum number of bits an integer of the supplied type ;;; can take up, or NIL if it is unbounded. The second (third) value @@ -2247,123 +2116,6 @@ (or (null min) (minusp min)))) (values nil t t))) -#+sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(progn - -(defoptimizer (logand derive-type) ((x y)) - (multiple-value-bind (x-len x-pos x-neg) - (integer-type-length (continuation-type x)) - (declare (ignore x-pos)) - (multiple-value-bind (y-len y-pos y-neg) - (integer-type-length (continuation-type y)) - (declare (ignore y-pos)) - (if (not x-neg) - ;; X must be positive. - (if (not y-neg) - ;; The must both be positive. - (cond ((or (null x-len) (null y-len)) - (specifier-type 'unsigned-byte)) - ((or (zerop x-len) (zerop y-len)) - (specifier-type '(integer 0 0))) - (t - (specifier-type `(unsigned-byte ,(min x-len y-len))))) - ;; X is positive, but Y might be negative. - (cond ((null x-len) - (specifier-type 'unsigned-byte)) - ((zerop x-len) - (specifier-type '(integer 0 0))) - (t - (specifier-type `(unsigned-byte ,x-len))))) - ;; X might be negative. - (if (not y-neg) - ;; Y must be positive. - (cond ((null y-len) - (specifier-type 'unsigned-byte)) - ((zerop y-len) - (specifier-type '(integer 0 0))) - (t - (specifier-type - `(unsigned-byte ,y-len)))) - ;; Either might be negative. - (if (and x-len y-len) - ;; The result is bounded. - (specifier-type `(signed-byte ,(1+ (max x-len y-len)))) - ;; We can't tell squat about the result. - (specifier-type 'integer))))))) - -(defoptimizer (logior derive-type) ((x y)) - (multiple-value-bind (x-len x-pos x-neg) - (integer-type-length (continuation-type x)) - (multiple-value-bind (y-len y-pos y-neg) - (integer-type-length (continuation-type y)) - (cond - ((and (not x-neg) (not y-neg)) - ;; Both are positive. - (specifier-type `(unsigned-byte ,(if (and x-len y-len) - (max x-len y-len) - '*)))) - ((not x-pos) - ;; X must be negative. - (if (not y-pos) - ;; Both are negative. The result is going to be negative and be - ;; the same length or shorter than the smaller. - (if (and x-len y-len) - ;; It's bounded. - (specifier-type `(integer ,(ash -1 (min x-len y-len)) -1)) - ;; It's unbounded. - (specifier-type '(integer * -1))) - ;; X is negative, but we don't know about Y. The result will be - ;; negative, but no more negative than X. - (specifier-type - `(integer ,(or (numeric-type-low (continuation-type x)) '*) - -1)))) - (t - ;; X might be either positive or negative. - (if (not y-pos) - ;; But Y is negative. The result will be negative. - (specifier-type - `(integer ,(or (numeric-type-low (continuation-type y)) '*) - -1)) - ;; We don't know squat about either. It won't get any bigger. - (if (and x-len y-len) - ;; Bounded. - (specifier-type `(signed-byte ,(1+ (max x-len y-len)))) - ;; Unbounded. - (specifier-type 'integer)))))))) - -(defoptimizer (logxor derive-type) ((x y)) - (multiple-value-bind (x-len x-pos x-neg) - (integer-type-length (continuation-type x)) - (multiple-value-bind (y-len y-pos y-neg) - (integer-type-length (continuation-type y)) - (cond - ((or (and (not x-neg) (not y-neg)) - (and (not x-pos) (not y-pos))) - ;; Either both are negative or both are positive. The result - ;; will be positive, and as long as the longer. - (specifier-type `(unsigned-byte ,(if (and x-len y-len) - (max x-len y-len) - '*)))) - ((or (and (not x-pos) (not y-neg)) - (and (not y-neg) (not y-pos))) - ;; Either X is negative and Y is positive of vice-versa. The - ;; result will be negative. - (specifier-type `(integer ,(if (and x-len y-len) - (ash -1 (max x-len y-len)) - '*) - -1))) - ;; We can't tell what the sign of the result is going to be. - ;; All we know is that we don't create new bits. - ((and x-len y-len) - (specifier-type `(signed-byte ,(1+ (max x-len y-len))))) - (t - (specifier-type 'integer)))))) - -) ; PROGN - -#-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(progn - (defun logand-derive-type-aux (x y &optional same-leaf) (declare (ignore same-leaf)) (multiple-value-bind (x-len x-pos x-neg) (integer-type-length x) @@ -2461,7 +2213,7 @@ '*))))) ((or (and (not x-pos) (not y-neg)) (and (not y-neg) (not y-pos))) - ;; Either X is negative and Y is positive of vice-verca. The + ;; Either X is negative and Y is positive of vice-versa. The ;; result will be negative. (specifier-type `(integer ,(if (and x-len y-len) (ash -1 (max x-len y-len)) @@ -2474,21 +2226,22 @@ (t (specifier-type 'integer)))))) -(macrolet ((frob (logfcn) +(macrolet ((deffrob (logfcn) (let ((fcn-aux (symbolicate logfcn "-DERIVE-TYPE-AUX"))) `(defoptimizer (,logfcn derive-type) ((x y)) (two-arg-derive-type x y #',fcn-aux #',logfcn))))) - ;; FIXME: DEF-FROB, not just FROB - (frob logand) - (frob logior) - (frob logxor)) + (deffrob logand) + (deffrob logior) + (deffrob logxor)) + +;;;; miscellaneous derive-type methods (defoptimizer (integer-length derive-type) ((x)) (let ((x-type (continuation-type x))) (when (and (numeric-type-p x-type) (csubtypep x-type (specifier-type 'integer))) - ;; If the X is of type (INTEGER LO HI), then the integer-length - ;; of X is (INTEGER (min lo hi) (max lo hi), basically. Be + ;; If the X is of type (INTEGER LO HI), then the INTEGER-LENGTH + ;; of X is (INTEGER (MIN lo hi) (MAX lo hi), basically. Be ;; careful about LO or HI being NIL, though. Also, if 0 is ;; contained in X, the lower bound is obviously 0. (flet ((null-or-min (a b) @@ -2504,17 +2257,14 @@ (when (ctypep 0 x-type) (setf min-len 0)) (specifier-type `(integer ,(or min-len '*) ,(or max-len '*)))))))) -) ; PROGN - -;;;; miscellaneous derive-type methods (defoptimizer (code-char derive-type) ((code)) (specifier-type 'base-char)) (defoptimizer (values derive-type) ((&rest values)) (values-specifier-type - `(values ,@(mapcar #'(lambda (x) - (type-specifier (continuation-type x))) + `(values ,@(mapcar (lambda (x) + (type-specifier (continuation-type x))) values)))) ;;;; byte operations @@ -2549,19 +2299,19 @@ `(let ((,,temp ,,spec)) ,,@body)))))) - (def-source-transform ldb (spec int) + (define-source-transform ldb (spec int) (with-byte-specifier (size pos spec) `(%ldb ,size ,pos ,int))) - (def-source-transform dpb (newbyte spec int) + (define-source-transform dpb (newbyte spec int) (with-byte-specifier (size pos spec) `(%dpb ,newbyte ,size ,pos ,int))) - (def-source-transform mask-field (spec int) + (define-source-transform mask-field (spec int) (with-byte-specifier (size pos spec) `(%mask-field ,size ,pos ,int))) - (def-source-transform deposit-field (newbyte spec int) + (define-source-transform deposit-field (newbyte spec int) (with-byte-specifier (size pos spec) `(%deposit-field ,newbyte ,size ,pos ,int)))) @@ -2570,7 +2320,7 @@ (if (and (numeric-type-p size) (csubtypep size (specifier-type 'integer))) (let ((size-high (numeric-type-high size))) - (if (and size-high (<= size-high sb!vm:word-bits)) + (if (and size-high (<= size-high sb!vm:n-word-bits)) (specifier-type `(unsigned-byte ,size-high)) (specifier-type 'unsigned-byte))) *universal-type*))) @@ -2585,7 +2335,7 @@ (let ((size-high (numeric-type-high size)) (posn-high (numeric-type-high posn))) (if (and size-high posn-high - (<= (+ size-high posn-high) sb!vm:word-bits)) + (<= (+ size-high posn-high) sb!vm:n-word-bits)) (specifier-type `(unsigned-byte ,(+ size-high posn-high))) (specifier-type 'unsigned-byte))) *universal-type*))) @@ -2605,7 +2355,7 @@ (high (numeric-type-high int)) (low (numeric-type-low int))) (if (and size-high posn-high high low - (<= (+ size-high posn-high) sb!vm:word-bits)) + (<= (+ size-high posn-high) sb!vm:n-word-bits)) (specifier-type (list (if (minusp low) 'signed-byte 'unsigned-byte) (max (integer-length high) @@ -2629,7 +2379,7 @@ (high (numeric-type-high int)) (low (numeric-type-low int))) (if (and size-high posn-high high low - (<= (+ size-high posn-high) sb!vm:word-bits)) + (<= (+ size-high posn-high) sb!vm:n-word-bits)) (specifier-type (list (if (minusp low) 'signed-byte 'unsigned-byte) (max (integer-length high) @@ -2640,19 +2390,19 @@ (deftransform %ldb ((size posn int) (fixnum fixnum integer) - (unsigned-byte #.sb!vm:word-bits)) + (unsigned-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(logand (ash int (- posn)) - (ash ,(1- (ash 1 sb!vm:word-bits)) - (- size ,sb!vm:word-bits)))) + (ash ,(1- (ash 1 sb!vm:n-word-bits)) + (- size ,sb!vm:n-word-bits)))) (deftransform %mask-field ((size posn int) (fixnum fixnum integer) - (unsigned-byte #.sb!vm:word-bits)) + (unsigned-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(logand int - (ash (ash ,(1- (ash 1 sb!vm:word-bits)) - (- size ,sb!vm:word-bits)) + (ash (ash ,(1- (ash 1 sb!vm:n-word-bits)) + (- size ,sb!vm:n-word-bits)) posn))) ;;; Note: for %DPB and %DEPOSIT-FIELD, we can't use @@ -2663,7 +2413,7 @@ (deftransform %dpb ((new size posn int) * - (unsigned-byte #.sb!vm:word-bits)) + (unsigned-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(let ((mask (ldb (byte size 0) -1))) (logior (ash (logand new mask) posn) @@ -2671,7 +2421,7 @@ (deftransform %dpb ((new size posn int) * - (signed-byte #.sb!vm:word-bits)) + (signed-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(let ((mask (ldb (byte size 0) -1))) (logior (ash (logand new mask) posn) @@ -2679,7 +2429,7 @@ (deftransform %deposit-field ((new size posn int) * - (unsigned-byte #.sb!vm:word-bits)) + (unsigned-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(let ((mask (ash (ldb (byte size 0) -1) posn))) (logior (logand new mask) @@ -2687,7 +2437,7 @@ (deftransform %deposit-field ((new size posn int) * - (signed-byte #.sb!vm:word-bits)) + (signed-byte #.sb!vm:n-word-bits)) "convert to inline logical operations" `(let ((mask (ash (ldb (byte size 0) -1) posn))) (logior (logand new mask) @@ -2699,7 +2449,7 @@ (deftransform commutative-arg-swap ((x y) * * :defun-only t :node node) (if (and (constant-continuation-p x) (not (constant-continuation-p y))) - `(,(continuation-function-name (basic-combination-fun node)) + `(,(continuation-fun-name (basic-combination-fun node)) y ,(continuation-value x)) (give-up-ir1-transform))) @@ -2709,7 +2459,7 @@ "place constant arg last")) ;;; Handle the case of a constant BOOLE-CODE. -(deftransform boole ((op x y) * * :when :both) +(deftransform boole ((op x y) * *) "convert to inline logical operations" (unless (constant-continuation-p op) (give-up-ir1-transform "BOOLE code is not a constant.")) @@ -2738,7 +2488,7 @@ ;;;; converting special case multiply/divide to shifts ;;; If arg is a constant power of two, turn * into a shift. -(deftransform * ((x y) (integer integer) * :when :both) +(deftransform * ((x y) (integer integer) *) "convert x*2^k to shift" (unless (constant-continuation-p y) (give-up-ir1-transform)) @@ -2800,7 +2550,8 @@ (or result 0))) ;;; If arg is a constant power of two, turn FLOOR into a shift and -;;; mask. If CEILING, add in (1- (ABS Y)) and then do FLOOR. +;;; mask. If CEILING, add in (1- (ABS Y)), do FLOOR and correct a +;;; remainder. (flet ((frob (y ceil-p) (unless (constant-continuation-p y) (give-up-ir1-transform)) @@ -2810,13 +2561,14 @@ (unless (= y-abs (ash 1 len)) (give-up-ir1-transform)) (let ((shift (- len)) - (mask (1- y-abs))) - `(let ,(when ceil-p `((x (+ x ,(1- y-abs))))) + (mask (1- y-abs)) + (delta (if ceil-p (* (signum y) (1- y-abs)) 0))) + `(let ((x (+ x ,delta))) ,(if (minusp y) `(values (ash (- x) ,shift) - (- (logand (- x) ,mask))) + (- (- (logand (- x) ,mask)) ,delta)) `(values (ash x ,shift) - (logand x ,mask)))))))) + (- (logand x ,mask) ,delta)))))))) (deftransform floor ((x y) (integer integer) *) "convert division by 2^k to shift" (frob y nil)) @@ -2825,7 +2577,7 @@ (frob y t))) ;;; Do the same for MOD. -(deftransform mod ((x y) (integer integer) * :when :both) +(deftransform mod ((x y) (integer integer) *) "convert remainder mod 2^k to LOGAND" (unless (constant-continuation-p y) (give-up-ir1-transform)) @@ -2862,7 +2614,7 @@ (logand x ,mask)))))) ;;; And the same for REM. -(deftransform rem ((x y) (integer integer) * :when :both) +(deftransform rem ((x y) (integer integer) *) "convert remainder mod 2^k to LOGAND" (unless (constant-continuation-p y) (give-up-ir1-transform)) @@ -2877,32 +2629,28 @@ (logand x ,mask))))) ;;;; arithmetic and logical identity operation elimination -;;;; -;;;; Flush calls to various arith functions that convert to the -;;;; identity function or a constant. - -(dolist (stuff '((ash 0 x) - (logand -1 x) - (logand 0 0) - (logior 0 x) - (logior -1 -1) - (logxor -1 (lognot x)) - (logxor 0 x))) - (destructuring-bind (name identity result) stuff - (deftransform name ((x y) `(* (constant-argument (member ,identity))) '* - :eval-name t :when :both) - "fold identity operations" - result))) + +;;; Flush calls to various arith functions that convert to the +;;; identity function or a constant. +(macrolet ((def (name identity result) + `(deftransform ,name ((x y) (* (constant-arg (member ,identity))) *) + "fold identity operations" + ',result))) + (def ash 0 x) + (def logand -1 x) + (def logand 0 0) + (def logior 0 x) + (def logior -1 -1) + (def logxor -1 (lognot x)) + (def logxor 0 x)) ;;; These are restricted to rationals, because (- 0 0.0) is 0.0, not -0.0, and ;;; (* 0 -4.0) is -0.0. -(deftransform - ((x y) ((constant-argument (member 0)) rational) * - :when :both) +(deftransform - ((x y) ((constant-arg (member 0)) rational) *) "convert (- 0 x) to negate" '(%negate y)) -(deftransform * ((x y) (rational (constant-argument (member 0))) * - :when :both) - "convert (* x 0) to 0." +(deftransform * ((x y) (rational (constant-arg (member 0))) *) + "convert (* x 0) to 0" 0) ;;; Return T if in an arithmetic op including continuations X and Y, @@ -2943,7 +2691,7 @@ ;;; ;;; If y is not constant, not zerop, or is contagious, or a positive ;;; float +0.0 then give up. -(deftransform + ((x y) (t (constant-argument t)) * :when :both) +(deftransform + ((x y) (t (constant-arg t)) *) "fold zero arg" (let ((val (continuation-value y))) (unless (and (zerop val) @@ -2956,7 +2704,7 @@ ;;; ;;; If y is not constant, not zerop, or is contagious, or a negative ;;; float -0.0 then give up. -(deftransform - ((x y) (t (constant-argument t)) * :when :both) +(deftransform - ((x y) (t (constant-arg t)) *) "fold zero arg" (let ((val (continuation-value y))) (unless (and (zerop val) @@ -2966,22 +2714,21 @@ 'x) ;;; Fold (OP x +/-1) -(dolist (stuff '((* x (%negate x)) - (/ x (%negate x)) - (expt x (/ 1 x)))) - (destructuring-bind (name result minus-result) stuff - (deftransform name ((x y) '(t (constant-argument real)) '* :eval-name t - :when :both) - "fold identity operations" - (let ((val (continuation-value y))) - (unless (and (= (abs val) 1) - (not-more-contagious y x)) - (give-up-ir1-transform)) - (if (minusp val) minus-result result))))) +(macrolet ((def (name result minus-result) + `(deftransform ,name ((x y) (t (constant-arg real)) *) + "fold identity operations" + (let ((val (continuation-value y))) + (unless (and (= (abs val) 1) + (not-more-contagious y x)) + (give-up-ir1-transform)) + (if (minusp val) ',minus-result ',result))))) + (def * x (%negate x)) + (def / x (%negate x)) + (def expt x (/ 1 x))) ;;; Fold (expt x n) into multiplications for small integral values of ;;; N; convert (expt x 1/2) to sqrt. -(deftransform expt ((x y) (t (constant-argument real)) *) +(deftransform expt ((x y) (t (constant-arg real)) *) "recode as multiplication or sqrt" (let ((val (continuation-value y))) ;; If Y would cause the result to be promoted to the same type as @@ -3002,21 +2749,24 @@ ;;; KLUDGE: Shouldn't (/ 0.0 0.0), etc. cause exceptions in these ;;; transformations? ;;; Perhaps we should have to prove that the denominator is nonzero before -;;; doing them? (Also the DOLIST over macro calls is weird. Perhaps -;;; just FROB?) -- WHN 19990917 -;;; -;;; FIXME: What gives with the single quotes in the argument lists -;;; for DEFTRANSFORMs here? Does that work? Is it needed? Why? -(dolist (name '(ash /)) - (deftransform name ((x y) '((constant-argument (integer 0 0)) integer) '* - :eval-name t :when :both) - "fold zero arg" - 0)) -(dolist (name '(truncate round floor ceiling)) - (deftransform name ((x y) '((constant-argument (integer 0 0)) integer) '* - :eval-name t :when :both) - "fold zero arg" - '(values 0 0))) +;;; doing them? -- WHN 19990917 +(macrolet ((def (name) + `(deftransform ,name ((x y) ((constant-arg (integer 0 0)) integer) + *) + "fold zero arg" + 0))) + (def ash) + (def /)) + +(macrolet ((def (name) + `(deftransform ,name ((x y) ((constant-arg (integer 0 0)) integer) + *) + "fold zero arg" + '(values 0 0)))) + (def truncate) + (def round) + (def floor) + (def ceiling)) ;;;; character operations @@ -3064,8 +2814,7 @@ ;;; if there is no intersection between the types of the arguments, ;;; then the result is definitely false. (deftransform simple-equality-transform ((x y) * * - :defun-only t - :when :both) + :defun-only t) (cond ((same-leaf-ref-p x y) t) ((not (types-equal-or-intersect (continuation-type x) @@ -3074,11 +2823,14 @@ (t (give-up-ir1-transform)))) -(dolist (x '(eq char= equal)) - (%deftransform x '(function * *) #'simple-equality-transform)) +(macrolet ((def (x) + `(%deftransform ',x '(function * *) #'simple-equality-transform))) + (def eq) + (def char=) + (def equal)) -;;; Similar to SIMPLE-EQUALITY-PREDICATE, except that we also try to -;;; convert to a type-specific predicate or EQ: +;;; This is similar to SIMPLE-EQUALITY-PREDICATE, except that we also +;;; try to convert to a type-specific predicate or EQ: ;;; -- If both args are characters, convert to CHAR=. This is better than ;;; just converting to EQ, since CHAR= may have special compilation ;;; strategies for non-standard representations, etc. @@ -3089,8 +2841,8 @@ ;;; it second. These rules make it easier for the back end to match ;;; these interesting cases. ;;; -- If Y is a fixnum, then we quietly pass because the back end can -;;; handle that case, otherwise give an efficency note. -(deftransform eql ((x y) * * :when :both) +;;; handle that case, otherwise give an efficiency note. +(deftransform eql ((x y) * *) "convert to simpler equality predicate" (let ((x-type (continuation-type x)) (y-type (continuation-type y)) @@ -3116,7 +2868,7 @@ ;;; Convert to EQL if both args are rational and complexp is specified ;;; and the same for both. -(deftransform = ((x y) * * :when :both) +(deftransform = ((x y) * *) "open code" (let ((x-type (continuation-type x)) (y-type (continuation-type y))) @@ -3194,18 +2946,18 @@ (t (give-up-ir1-transform)))))) -(deftransform < ((x y) (integer integer) * :when :both) +(deftransform < ((x y) (integer integer) *) (ir1-transform-< x y x y '>)) -(deftransform > ((x y) (integer integer) * :when :both) +(deftransform > ((x y) (integer integer) *) (ir1-transform-< y x x y '<)) #-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(deftransform < ((x y) (float float) * :when :both) +(deftransform < ((x y) (float float) *) (ir1-transform-< x y x y '>)) #-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) -(deftransform > ((x y) (float float) * :when :both) +(deftransform > ((x y) (float float) *) (ir1-transform-< y x x y '<)) ;;;; converting N-arg comparisons @@ -3245,31 +2997,31 @@ ((zerop i) `((lambda ,vars ,result) . ,args))))))) -(def-source-transform = (&rest args) (multi-compare '= args nil)) -(def-source-transform < (&rest args) (multi-compare '< args nil)) -(def-source-transform > (&rest args) (multi-compare '> args nil)) -(def-source-transform <= (&rest args) (multi-compare '> args t)) -(def-source-transform >= (&rest args) (multi-compare '< args t)) +(define-source-transform = (&rest args) (multi-compare '= args nil)) +(define-source-transform < (&rest args) (multi-compare '< args nil)) +(define-source-transform > (&rest args) (multi-compare '> args nil)) +(define-source-transform <= (&rest args) (multi-compare '> args t)) +(define-source-transform >= (&rest args) (multi-compare '< args t)) -(def-source-transform char= (&rest args) (multi-compare 'char= args nil)) -(def-source-transform char< (&rest args) (multi-compare 'char< args nil)) -(def-source-transform char> (&rest args) (multi-compare 'char> args nil)) -(def-source-transform char<= (&rest args) (multi-compare 'char> args t)) -(def-source-transform char>= (&rest args) (multi-compare 'char< args t)) +(define-source-transform char= (&rest args) (multi-compare 'char= args nil)) +(define-source-transform char< (&rest args) (multi-compare 'char< args nil)) +(define-source-transform char> (&rest args) (multi-compare 'char> args nil)) +(define-source-transform char<= (&rest args) (multi-compare 'char> args t)) +(define-source-transform char>= (&rest args) (multi-compare 'char< args t)) -(def-source-transform char-equal (&rest args) +(define-source-transform char-equal (&rest args) (multi-compare 'char-equal args nil)) -(def-source-transform char-lessp (&rest args) +(define-source-transform char-lessp (&rest args) (multi-compare 'char-lessp args nil)) -(def-source-transform char-greaterp (&rest args) +(define-source-transform char-greaterp (&rest args) (multi-compare 'char-greaterp args nil)) -(def-source-transform char-not-greaterp (&rest args) +(define-source-transform char-not-greaterp (&rest args) (multi-compare 'char-greaterp args t)) -(def-source-transform char-not-lessp (&rest args) +(define-source-transform char-not-lessp (&rest args) (multi-compare 'char-lessp args t)) ;;; This function does source transformation of N-arg inequality -;;; functions such as /=. This is similar to Multi-Compare in the <3 +;;; functions such as /=. This is similar to MULTI-COMPARE in the <3 ;;; arg cases. If there are more than two args, then we expand into ;;; the appropriate n^2 comparisons only when speed is important. (declaim (ftype (function (symbol list) *) multi-not-equal)) @@ -3294,26 +3046,33 @@ (dolist (v2 next) (setq result `(if (,predicate ,v1 ,v2) nil ,result)))))))))) -(def-source-transform /= (&rest args) (multi-not-equal '= args)) -(def-source-transform char/= (&rest args) (multi-not-equal 'char= args)) -(def-source-transform char-not-equal (&rest args) +(define-source-transform /= (&rest args) (multi-not-equal '= args)) +(define-source-transform char/= (&rest args) (multi-not-equal 'char= args)) +(define-source-transform char-not-equal (&rest args) (multi-not-equal 'char-equal args)) +;;; FIXME: can go away once bug 194 is fixed and we can use (THE REAL X) +;;; as God intended +(defun error-not-a-real (x) + (error 'simple-type-error + :datum x + :expected-type 'real + :format-control "not a REAL: ~S" + :format-arguments (list x))) + ;;; Expand MAX and MIN into the obvious comparisons. -(def-source-transform max (arg &rest more-args) - (if (null more-args) - `(values ,arg) - (once-only ((arg1 arg) - (arg2 `(max ,@more-args))) - `(if (> ,arg1 ,arg2) - ,arg1 ,arg2)))) -(def-source-transform min (arg &rest more-args) - (if (null more-args) - `(values ,arg) - (once-only ((arg1 arg) - (arg2 `(min ,@more-args))) - `(if (< ,arg1 ,arg2) - ,arg1 ,arg2)))) +(define-source-transform max (arg0 &rest rest) + (once-only ((arg0 arg0)) + (if (null rest) + `(values (the real ,arg0)) + `(let ((maxrest (max ,@rest))) + (if (> ,arg0 maxrest) ,arg0 maxrest))))) +(define-source-transform min (arg0 &rest rest) + (once-only ((arg0 arg0)) + (if (null rest) + `(values (the real ,arg0)) + `(let ((minrest (min ,@rest))) + (if (< ,arg0 minrest) ,arg0 minrest))))) ;;;; converting N-arg arithmetic functions ;;;; @@ -3321,39 +3080,42 @@ ;;;; versions, and degenerate cases are flushed. ;;; Left-associate FIRST-ARG and MORE-ARGS using FUNCTION. -(declaim (ftype (function (symbol t list) list) associate-arguments)) -(defun associate-arguments (function first-arg more-args) +(declaim (ftype (function (symbol t list) list) associate-args)) +(defun associate-args (function first-arg more-args) (let ((next (rest more-args)) (arg (first more-args))) (if (null next) `(,function ,first-arg ,arg) - (associate-arguments function `(,function ,first-arg ,arg) next)))) + (associate-args function `(,function ,first-arg ,arg) next)))) ;;; Do source transformations for transitive functions such as +. ;;; One-arg cases are replaced with the arg and zero arg cases with -;;; the identity. If LEAF-FUN is true, then replace two-arg calls with -;;; a call to that function. -(defun source-transform-transitive (fun args identity &optional leaf-fun) +;;; the identity. ONE-ARG-RESULT-TYPE is, if non-NIL, the type to +;;; ensure (with THE) that the argument in one-argument calls is. +(defun source-transform-transitive (fun args identity + &optional one-arg-result-type) (declare (symbol fun leaf-fun) (list args)) (case (length args) (0 identity) - (1 `(values ,(first args))) - (2 (if leaf-fun - `(,leaf-fun ,(first args) ,(second args)) - (values nil t))) + (1 (if one-arg-result-type + `(values (the ,one-arg-result-type ,(first args))) + `(values ,(first args)))) + (2 (values nil t)) (t - (associate-arguments fun (first args) (rest args))))) - -(def-source-transform + (&rest args) (source-transform-transitive '+ args 0)) -(def-source-transform * (&rest args) (source-transform-transitive '* args 1)) -(def-source-transform logior (&rest args) - (source-transform-transitive 'logior args 0)) -(def-source-transform logxor (&rest args) - (source-transform-transitive 'logxor args 0)) -(def-source-transform logand (&rest args) - (source-transform-transitive 'logand args -1)) - -(def-source-transform logeqv (&rest args) + (associate-args fun (first args) (rest args))))) + +(define-source-transform + (&rest args) + (source-transform-transitive '+ args 0 'number)) +(define-source-transform * (&rest args) + (source-transform-transitive '* args 1 'number)) +(define-source-transform logior (&rest args) + (source-transform-transitive 'logior args 0 'integer)) +(define-source-transform logxor (&rest args) + (source-transform-transitive 'logxor args 0 'integer)) +(define-source-transform logand (&rest args) + (source-transform-transitive 'logand args -1 'integer)) + +(define-source-transform logeqv (&rest args) (if (evenp (length args)) `(lognot (logxor ,@args)) `(logxor ,@args))) @@ -3362,33 +3124,35 @@ ;;; because when they are given one argument, they return its absolute ;;; value. -(def-source-transform gcd (&rest args) +(define-source-transform gcd (&rest args) (case (length args) (0 0) (1 `(abs (the integer ,(first args)))) (2 (values nil t)) - (t (associate-arguments 'gcd (first args) (rest args))))) + (t (associate-args 'gcd (first args) (rest args))))) -(def-source-transform lcm (&rest args) +(define-source-transform lcm (&rest args) (case (length args) (0 1) (1 `(abs (the integer ,(first args)))) (2 (values nil t)) - (t (associate-arguments 'lcm (first args) (rest args))))) + (t (associate-args 'lcm (first args) (rest args))))) ;;; Do source transformations for intransitive n-arg functions such as ;;; /. With one arg, we form the inverse. With two args we pass. ;;; Otherwise we associate into two-arg calls. -(declaim (ftype (function (symbol list t) list) source-transform-intransitive)) +(declaim (ftype (function (symbol list t) + (values list &optional (member nil t))) + source-transform-intransitive)) (defun source-transform-intransitive (function args inverse) (case (length args) ((0 2) (values nil t)) (1 `(,@inverse ,(first args))) - (t (associate-arguments function (first args) (rest args))))) + (t (associate-args function (first args) (rest args))))) -(def-source-transform - (&rest args) +(define-source-transform - (&rest args) (source-transform-intransitive '- args '(%negate))) -(def-source-transform / (&rest args) +(define-source-transform / (&rest args) (source-transform-intransitive '/ args '(/ 1))) ;;;; transforming APPLY @@ -3396,11 +3160,11 @@ ;;; We convert APPLY into MULTIPLE-VALUE-CALL so that the compiler ;;; only needs to understand one kind of variable-argument call. It is ;;; more efficient to convert APPLY to MV-CALL than MV-CALL to APPLY. -(def-source-transform apply (fun arg &rest more-args) +(define-source-transform apply (fun arg &rest more-args) (let ((args (cons arg more-args))) `(multiple-value-call ,fun - ,@(mapcar #'(lambda (x) - `(values ,x)) + ,@(mapcar (lambda (x) + `(values ,x)) (butlast args)) (values-list ,(car (last args)))))) @@ -3437,108 +3201,256 @@ nil))) (defoptimizer (coerce derive-type) ((value type)) - (let ((value-type (continuation-type value)) - (type-type (continuation-type type))) - (labels - ((good-cons-type-p (cons-type) - ;; Make sure the cons-type we're looking at is something - ;; we're prepared to handle which is basically something - ;; that array-element-type can return. - (or (and (member-type-p cons-type) - (null (rest (member-type-members cons-type))) - (null (first (member-type-members cons-type)))) - (let ((car-type (cons-type-car-type cons-type))) - (and (member-type-p car-type) - (null (rest (member-type-members car-type))) - (or (symbolp (first (member-type-members car-type))) - (numberp (first (member-type-members car-type))) - (and (listp (first (member-type-members car-type))) - (numberp (first (first (member-type-members - car-type)))))) - (good-cons-type-p (cons-type-cdr-type cons-type)))))) - (unconsify-type (good-cons-type) - ;; Convert the "printed" respresentation of a cons - ;; specifier into a type specifier. That is, the specifier - ;; (cons (eql signed-byte) (cons (eql 16) null)) is - ;; converted to (signed-byte 16). - (cond ((or (null good-cons-type) - (eq good-cons-type 'null)) - nil) - ((and (eq (first good-cons-type) 'cons) - (eq (first (second good-cons-type)) 'member)) - `(,(second (second good-cons-type)) - ,@(unconsify-type (caddr good-cons-type)))))) - (coerceable-p (c-type) - ;; Can the value be coerced to the given type? Coerce is - ;; complicated, so we don't handle every possible case - ;; here---just the most common and easiest cases: - ;; - ;; o Any real can be coerced to a float type. - ;; o Any number can be coerced to a complex single/double-float. - ;; o An integer can be coerced to an integer. - (let ((coerced-type c-type)) - (or (and (subtypep coerced-type 'float) - (csubtypep value-type (specifier-type 'real))) - (and (subtypep coerced-type - '(or (complex single-float) - (complex double-float))) - (csubtypep value-type (specifier-type 'number))) - (and (subtypep coerced-type 'integer) - (csubtypep value-type (specifier-type 'integer)))))) - (process-types (type) - ;; FIXME - ;; This needs some work because we should be able to derive - ;; the resulting type better than just the type arg of - ;; coerce. That is, if x is (integer 10 20), the (coerce x - ;; 'double-float) should say (double-float 10d0 20d0) - ;; instead of just double-float. - (cond ((member-type-p type) - (let ((members (member-type-members type))) - (if (every #'coerceable-p members) - (specifier-type `(or ,@members)) - *universal-type*))) - ((and (cons-type-p type) - (good-cons-type-p type)) - (let ((c-type (unconsify-type (type-specifier type)))) - (if (coerceable-p c-type) - (specifier-type c-type) - *universal-type*))) - (t - *universal-type*)))) - (cond ((union-type-p type-type) - (apply #'type-union (mapcar #'process-types - (union-type-types type-type)))) - ((or (member-type-p type-type) - (cons-type-p type-type)) - (process-types type-type)) - (t - *universal-type*))))) + (cond + ((constant-continuation-p type) + ;; This branch is essentially (RESULT-TYPE-SPECIFIER-NTH-ARG 2), + ;; but dealing with the niggle that complex canonicalization gets + ;; in the way: (COERCE 1 'COMPLEX) returns 1, which is not of + ;; type COMPLEX. + (let* ((specifier (continuation-value type)) + (result-typeoid (careful-specifier-type specifier))) + (cond + ((null result-typeoid) nil) + ((csubtypep result-typeoid (specifier-type 'number)) + ;; the difficult case: we have to cope with ANSI 12.1.5.3 + ;; Rule of Canonical Representation for Complex Rationals, + ;; which is a truly nasty delivery to field. + (cond + ((csubtypep result-typeoid (specifier-type 'real)) + ;; cleverness required here: it would be nice to deduce + ;; that something of type (INTEGER 2 3) coerced to type + ;; DOUBLE-FLOAT should return (DOUBLE-FLOAT 2.0d0 3.0d0). + ;; FLOAT gets its own clause because it's implemented as + ;; a UNION-TYPE, so we don't catch it in the NUMERIC-TYPE + ;; logic below. + result-typeoid) + ((and (numeric-type-p result-typeoid) + (eq (numeric-type-complexp result-typeoid) :real)) + ;; FIXME: is this clause (a) necessary or (b) useful? + result-typeoid) + ((or (csubtypep result-typeoid + (specifier-type '(complex single-float))) + (csubtypep result-typeoid + (specifier-type '(complex double-float))) + #!+long-float + (csubtypep result-typeoid + (specifier-type '(complex long-float)))) + ;; float complex types are never canonicalized. + result-typeoid) + (t + ;; if it's not a REAL, or a COMPLEX FLOAToid, it's + ;; probably just a COMPLEX or equivalent. So, in that + ;; case, we will return a complex or an object of the + ;; provided type if it's rational: + (type-union result-typeoid + (type-intersection (continuation-type value) + (specifier-type 'rational)))))) + (t result-typeoid)))) + (t + ;; OK, the result-type argument isn't constant. However, there + ;; are common uses where we can still do better than just + ;; *UNIVERSAL-TYPE*: e.g. (COERCE X (ARRAY-ELEMENT-TYPE Y)), + ;; where Y is of a known type. See messages on cmucl-imp + ;; 2001-02-14 and sbcl-devel 2002-12-12. We only worry here + ;; about types that can be returned by (ARRAY-ELEMENT-TYPE Y), on + ;; the basis that it's unlikely that other uses are both + ;; time-critical and get to this branch of the COND (non-constant + ;; second argument to COERCE). -- CSR, 2002-12-16 + (let ((value-type (continuation-type value)) + (type-type (continuation-type type))) + (labels + ((good-cons-type-p (cons-type) + ;; Make sure the cons-type we're looking at is something + ;; we're prepared to handle which is basically something + ;; that array-element-type can return. + (or (and (member-type-p cons-type) + (null (rest (member-type-members cons-type))) + (null (first (member-type-members cons-type)))) + (let ((car-type (cons-type-car-type cons-type))) + (and (member-type-p car-type) + (null (rest (member-type-members car-type))) + (or (symbolp (first (member-type-members car-type))) + (numberp (first (member-type-members car-type))) + (and (listp (first (member-type-members + car-type))) + (numberp (first (first (member-type-members + car-type)))))) + (good-cons-type-p (cons-type-cdr-type cons-type)))))) + (unconsify-type (good-cons-type) + ;; Convert the "printed" respresentation of a cons + ;; specifier into a type specifier. That is, the + ;; specifier (CONS (EQL SIGNED-BYTE) (CONS (EQL 16) + ;; NULL)) is converted to (SIGNED-BYTE 16). + (cond ((or (null good-cons-type) + (eq good-cons-type 'null)) + nil) + ((and (eq (first good-cons-type) 'cons) + (eq (first (second good-cons-type)) 'member)) + `(,(second (second good-cons-type)) + ,@(unconsify-type (caddr good-cons-type)))))) + (coerceable-p (c-type) + ;; Can the value be coerced to the given type? Coerce is + ;; complicated, so we don't handle every possible case + ;; here---just the most common and easiest cases: + ;; + ;; * Any REAL can be coerced to a FLOAT type. + ;; * Any NUMBER can be coerced to a (COMPLEX + ;; SINGLE/DOUBLE-FLOAT). + ;; + ;; FIXME I: we should also be able to deal with characters + ;; here. + ;; + ;; FIXME II: I'm not sure that anything is necessary + ;; here, at least while COMPLEX is not a specialized + ;; array element type in the system. Reasoning: if + ;; something cannot be coerced to the requested type, an + ;; error will be raised (and so any downstream compiled + ;; code on the assumption of the returned type is + ;; unreachable). If something can, then it will be of + ;; the requested type, because (by assumption) COMPLEX + ;; (and other difficult types like (COMPLEX INTEGER) + ;; aren't specialized types. + (let ((coerced-type c-type)) + (or (and (subtypep coerced-type 'float) + (csubtypep value-type (specifier-type 'real))) + (and (subtypep coerced-type + '(or (complex single-float) + (complex double-float))) + (csubtypep value-type (specifier-type 'number)))))) + (process-types (type) + ;; FIXME: This needs some work because we should be able + ;; to derive the resulting type better than just the + ;; type arg of coerce. That is, if X is (INTEGER 10 + ;; 20), then (COERCE X 'DOUBLE-FLOAT) should say + ;; (DOUBLE-FLOAT 10d0 20d0) instead of just + ;; double-float. + (cond ((member-type-p type) + (let ((members (member-type-members type))) + (if (every #'coerceable-p members) + (specifier-type `(or ,@members)) + *universal-type*))) + ((and (cons-type-p type) + (good-cons-type-p type)) + (let ((c-type (unconsify-type (type-specifier type)))) + (if (coerceable-p c-type) + (specifier-type c-type) + *universal-type*))) + (t + *universal-type*)))) + (cond ((union-type-p type-type) + (apply #'type-union (mapcar #'process-types + (union-type-types type-type)))) + ((or (member-type-p type-type) + (cons-type-p type-type)) + (process-types type-type)) + (t + *universal-type*))))))) +(defoptimizer (compile derive-type) ((nameoid function)) + (when (csubtypep (continuation-type nameoid) + (specifier-type 'null)) + (values-specifier-type '(values function boolean boolean)))) + +;;; FIXME: Maybe also STREAM-ELEMENT-TYPE should be given some loving +;;; treatment along these lines? (See discussion in COERCE DERIVE-TYPE +;;; optimizer, above). (defoptimizer (array-element-type derive-type) ((array)) - (let* ((array-type (continuation-type array))) - #!+sb-show - (format t "~& defoptimizer array-elt-derive-type - array-element-type ~~ -~A~%" array-type) + (let ((array-type (continuation-type array))) (labels ((consify (list) (if (endp list) '(eql nil) `(cons (eql ,(car list)) ,(consify (rest list))))) (get-element-type (a) - (let ((element-type (type-specifier - (array-type-specialized-element-type a)))) - (cond ((symbolp element-type) + (let ((element-type + (type-specifier (array-type-specialized-element-type a)))) + (cond ((eq element-type '*) + (specifier-type 'type-specifier)) + ((symbolp element-type) (make-member-type :members (list element-type))) ((consp element-type) (specifier-type (consify element-type))) (t - (error "Can't grok type ~A~%" element-type)))))) + (error "can't understand type ~S~%" element-type)))))) (cond ((array-type-p array-type) - (get-element-type array-type)) - ((union-type-p array-type) + (get-element-type array-type)) + ((union-type-p array-type) (apply #'type-union (mapcar #'get-element-type (union-type-types array-type)))) - (t - *universal-type*))))) + (t + *universal-type*))))) + +(define-source-transform sb!impl::sort-vector (vector start end predicate key) + `(macrolet ((%index (x) `(truly-the index ,x)) + (%parent (i) `(ash ,i -1)) + (%left (i) `(%index (ash ,i 1))) + (%right (i) `(%index (1+ (ash ,i 1)))) + (%heapify (i) + `(do* ((i ,i) + (left (%left i) (%left i))) + ((> left current-heap-size)) + (declare (type index i left)) + (let* ((i-elt (%elt i)) + (i-key (funcall keyfun i-elt)) + (left-elt (%elt left)) + (left-key (funcall keyfun left-elt))) + (multiple-value-bind (large large-elt large-key) + (if (funcall ,',predicate i-key left-key) + (values left left-elt left-key) + (values i i-elt i-key)) + (let ((right (%right i))) + (multiple-value-bind (largest largest-elt) + (if (> right current-heap-size) + (values large large-elt) + (let* ((right-elt (%elt right)) + (right-key (funcall keyfun right-elt))) + (if (funcall ,',predicate large-key right-key) + (values right right-elt) + (values large large-elt)))) + (cond ((= largest i) + (return)) + (t + (setf (%elt i) largest-elt + (%elt largest) i-elt + i largest))))))))) + (%sort-vector (keyfun &optional (vtype 'vector)) + `(macrolet (;; KLUDGE: In SBCL ca. 0.6.10, I had trouble getting + ;; type inference to propagate all the way + ;; through this tangled mess of + ;; inlining. The TRULY-THE here works + ;; around that. -- WHN + (%elt (i) + `(aref (truly-the ,',vtype ,',',vector) + (%index (+ (%index ,i) start-1))))) + (let ((start-1 (1- ,',start)) ; Heaps prefer 1-based addressing. + (current-heap-size (- ,',end ,',start)) + (keyfun ,keyfun)) + (declare (type (integer -1 #.(1- most-positive-fixnum)) + start-1)) + (declare (type index current-heap-size)) + (declare (type function keyfun)) + (loop for i of-type index + from (ash current-heap-size -1) downto 1 do + (%heapify i)) + (loop + (when (< current-heap-size 2) + (return)) + (rotatef (%elt 1) (%elt current-heap-size)) + (decf current-heap-size) + (%heapify 1)))))) + (if (typep ,vector 'simple-vector) + ;; (VECTOR T) is worth optimizing for, and SIMPLE-VECTOR is + ;; what we get from (VECTOR T) inside WITH-ARRAY-DATA. + (if (null ,key) + ;; Special-casing the KEY=NIL case lets us avoid some + ;; function calls. + (%sort-vector #'identity simple-vector) + (%sort-vector ,key simple-vector)) + ;; It's hard to anticipate many speed-critical applications for + ;; sorting vector types other than (VECTOR T), so we just lump + ;; them all together in one slow dynamically typed mess. + (locally + (declare (optimize (speed 2) (space 2) (inhibit-warnings 3))) + (%sort-vector (or ,key #'identity)))))) ;;;; debuggers' little helpers