X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcompiler%2Fsrctran.lisp;h=f6caaaed4f3eae393faae6f01d8eb86947318e1e;hb=49c69bcd41790587cbcb0411c5c3497ee84f4343;hp=c1a53a9bd8d8b9e98035b260705a17cf710bbd0a;hpb=05525d3a5906d7a89fcb689c26177732493c40ce;p=sbcl.git diff --git a/src/compiler/srctran.lisp b/src/compiler/srctran.lisp index c1a53a9..f6caaae 100644 --- a/src/compiler/srctran.lisp +++ b/src/compiler/srctran.lisp @@ -179,8 +179,14 @@ (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)))) + +(deftransform logbitp + ((index integer) (unsigned-byte (or (signed-byte #.sb!vm:n-word-bits) + (unsigned-byte #.sb!vm:n-word-bits)))) + `(if (>= index #.sb!vm:n-word-bits) + (minusp integer) + (not (zerop (logand integer (ash 1 index)))))) + (define-source-transform byte (size position) `(cons ,size ,position)) (define-source-transform byte-size (spec) `(car ,spec)) @@ -992,50 +998,53 @@ (member (first members)) (member-type (type-of member))) (aver (not (rest members))) - (specifier-type `(,(if (subtypep member-type 'integer) - 'integer - member-type) - ,member ,member)))) + (specifier-type (cond ((typep member 'integer) + `(integer ,member ,member)) + ((memq member-type '(short-float single-float + double-float long-float)) + `(,member-type ,member ,member)) + (t + member-type))))) ;;; This is used in defoptimizers for computing the resulting type of ;;; a function. ;;; ;;; Given the continuation ARG, derive the resulting type using the -;;; DERIVE-FCN. DERIVE-FCN takes exactly one argument which is some +;;; DERIVE-FUN. DERIVE-FUN takes exactly one argument which is some ;;; "atomic" continuation type like numeric-type or member-type ;;; (containing just one element). It should return the resulting ;;; type, which can be a list of types. ;;; -;;; For the case of member types, if a member-fcn is given it is +;;; For the case of member types, if a MEMBER-FUN is given it is ;;; called to compute the result otherwise the member type is first -;;; converted to a numeric type and the derive-fcn is call. -(defun one-arg-derive-type (arg derive-fcn member-fcn +;;; converted to a numeric type and the DERIVE-FUN is called. +(defun one-arg-derive-type (arg derive-fun member-fun &optional (convert-type t)) - (declare (type function derive-fcn) - (type (or null function) member-fcn)) + (declare (type function derive-fun) + (type (or null function) member-fun)) (let ((arg-list (prepare-arg-for-derive-type (continuation-type arg)))) (when arg-list (flet ((deriver (x) (typecase x (member-type - (if member-fcn + (if member-fun (with-float-traps-masked (:underflow :overflow :divide-by-zero) (make-member-type :members (list - (funcall member-fcn + (funcall member-fun (first (member-type-members x)))))) ;; Otherwise convert to a numeric type. (let ((result-type-list - (funcall derive-fcn (convert-member-type x)))) + (funcall derive-fun (convert-member-type x)))) (if convert-type (convert-back-numeric-type-list result-type-list) result-type-list)))) (numeric-type (if convert-type (convert-back-numeric-type-list - (funcall derive-fcn (convert-numeric-type x))) - (funcall derive-fcn x))) + (funcall derive-fun (convert-numeric-type x))) + (funcall derive-fun x))) (t *universal-type*)))) ;; Run down the list of args and derive the type of each one, @@ -1051,14 +1060,14 @@ (first results))))))) ;;; Same as ONE-ARG-DERIVE-TYPE, except we assume the function takes -;;; two arguments. DERIVE-FCN takes 3 args in this case: the two +;;; two arguments. DERIVE-FUN takes 3 args in this case: the two ;;; original args and a third which is T to indicate if the two args ;;; really represent the same continuation. This is useful for ;;; deriving the type of things like (* x x), which should always be ;;; positive. If we didn't do this, we wouldn't be able to tell. -(defun two-arg-derive-type (arg1 arg2 derive-fcn fcn +(defun two-arg-derive-type (arg1 arg2 derive-fun fun &optional (convert-type t)) - (declare (type function derive-fcn fcn)) + (declare (type function derive-fun fun)) (flet ((deriver (x y same-arg) (cond ((and (member-type-p x) (member-type-p y)) (let* ((x (first (member-type-members x))) @@ -1066,7 +1075,7 @@ (result (with-float-traps-masked (:underflow :overflow :divide-by-zero :invalid) - (funcall fcn x y)))) + (funcall fun x y)))) (cond ((null result)) ((and (floatp result) (float-nan-p result)) (make-numeric-type :class 'float @@ -1077,21 +1086,21 @@ ((and (member-type-p x) (numeric-type-p y)) (let* ((x (convert-member-type x)) (y (if convert-type (convert-numeric-type y) y)) - (result (funcall derive-fcn x y same-arg))) + (result (funcall derive-fun x y same-arg))) (if convert-type (convert-back-numeric-type-list result) result))) ((and (numeric-type-p x) (member-type-p y)) (let* ((x (if convert-type (convert-numeric-type x) x)) (y (convert-member-type y)) - (result (funcall derive-fcn x y same-arg))) + (result (funcall derive-fun x y same-arg))) (if convert-type (convert-back-numeric-type-list result) result))) ((and (numeric-type-p x) (numeric-type-p y)) (let* ((x (if convert-type (convert-numeric-type x) x)) (y (if convert-type (convert-numeric-type y) y)) - (result (funcall derive-fcn x y same-arg))) + (result (funcall derive-fun x y same-arg))) (if convert-type (convert-back-numeric-type-list result) result))) @@ -2128,27 +2137,19 @@ ;; They 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))))) + (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))))) + (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)))) + (t (specifier-type `(unsigned-byte* ,y-len)))) ;; Either might be negative. (if (and x-len y-len) ;; The result is bounded. @@ -2163,11 +2164,9 @@ (cond ((and (not x-neg) (not y-neg)) ;; Both are positive. - (if (and x-len y-len (zerop x-len) (zerop y-len)) - (specifier-type '(integer 0 0)) - (specifier-type `(unsigned-byte ,(if (and x-len y-len) - (max x-len y-len) - '*))))) + (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) @@ -2206,11 +2205,9 @@ (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. - (if (and x-len y-len (zerop x-len) (zerop y-len)) - (specifier-type '(integer 0 0)) - (specifier-type `(unsigned-byte ,(if (and x-len y-len) - (max x-len y-len) - '*))))) + (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 @@ -2226,10 +2223,10 @@ (t (specifier-type 'integer)))))) -(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))))) +(macrolet ((deffrob (logfun) + (let ((fun-aux (symbolicate logfun "-DERIVE-TYPE-AUX"))) + `(defoptimizer (,logfun derive-type) ((x y)) + (two-arg-derive-type x y #',fun-aux #',logfun))))) (deffrob logand) (deffrob logior) (deffrob logxor)) @@ -2262,10 +2259,7 @@ (specifier-type 'base-char)) (defoptimizer (values derive-type) ((&rest values)) - (values-specifier-type - `(values ,@(mapcar (lambda (x) - (type-specifier (continuation-type x))) - values)))) + (make-values-type :required (mapcar #'continuation-type values))) ;;;; byte operations ;;;; @@ -2321,7 +2315,7 @@ (csubtypep size (specifier-type 'integer))) (let ((size-high (numeric-type-high size))) (if (and size-high (<= size-high sb!vm:n-word-bits)) - (specifier-type `(unsigned-byte ,size-high)) + (specifier-type `(unsigned-byte* ,size-high)) (specifier-type 'unsigned-byte))) *universal-type*))) @@ -2336,57 +2330,46 @@ (posn-high (numeric-type-high posn))) (if (and size-high posn-high (<= (+ size-high posn-high) sb!vm:n-word-bits)) - (specifier-type `(unsigned-byte ,(+ size-high posn-high))) + (specifier-type `(unsigned-byte* ,(+ size-high posn-high))) (specifier-type 'unsigned-byte))) *universal-type*))) -(defoptimizer (%dpb derive-type) ((newbyte size posn int)) +(defun %deposit-field-derive-type-aux (size posn int) (let ((size (continuation-type size)) (posn (continuation-type posn)) (int (continuation-type int))) - (if (and (numeric-type-p size) - (csubtypep size (specifier-type 'integer)) - (numeric-type-p posn) - (csubtypep posn (specifier-type 'integer)) - (numeric-type-p int) - (csubtypep int (specifier-type 'integer))) - (let ((size-high (numeric-type-high size)) - (posn-high (numeric-type-high posn)) - (high (numeric-type-high int)) - (low (numeric-type-low int))) - (if (and size-high posn-high high low - (<= (+ size-high posn-high) sb!vm:n-word-bits)) - (specifier-type - (list (if (minusp low) 'signed-byte 'unsigned-byte) - (max (integer-length high) - (integer-length low) - (+ size-high posn-high)))) - *universal-type*)) - *universal-type*))) + (when (and (numeric-type-p size) + (numeric-type-p posn) + (numeric-type-p int)) + (let ((size-high (numeric-type-high size)) + (posn-high (numeric-type-high posn)) + (high (numeric-type-high int)) + (low (numeric-type-low int))) + (when (and size-high posn-high high low + ;; KLUDGE: we need this cutoff here, otherwise we + ;; will merrily derive the type of %DPB as + ;; (UNSIGNED-BYTE 1073741822), and then attempt to + ;; canonicalize this type to (INTEGER 0 (1- (ASH 1 + ;; 1073741822))), with hilarious consequences. We + ;; cutoff at 4*SB!VM:N-WORD-BITS to allow inference + ;; over a reasonable amount of shifting, even on + ;; the alpha/32 port, where N-WORD-BITS is 32 but + ;; machine integers are 64-bits. -- CSR, + ;; 2003-09-12 + (<= (+ size-high posn-high) (* 4 sb!vm:n-word-bits))) + (let ((raw-bit-count (max (integer-length high) + (integer-length low) + (+ size-high posn-high)))) + (specifier-type + (if (minusp low) + `(signed-byte ,(1+ raw-bit-count)) + `(unsigned-byte* ,raw-bit-count))))))))) + +(defoptimizer (%dpb derive-type) ((newbyte size posn int)) + (%deposit-field-derive-type-aux size posn int)) (defoptimizer (%deposit-field derive-type) ((newbyte size posn int)) - (let ((size (continuation-type size)) - (posn (continuation-type posn)) - (int (continuation-type int))) - (if (and (numeric-type-p size) - (csubtypep size (specifier-type 'integer)) - (numeric-type-p posn) - (csubtypep posn (specifier-type 'integer)) - (numeric-type-p int) - (csubtypep int (specifier-type 'integer))) - (let ((size-high (numeric-type-high size)) - (posn-high (numeric-type-high posn)) - (high (numeric-type-high int)) - (low (numeric-type-low int))) - (if (and size-high posn-high high low - (<= (+ size-high posn-high) sb!vm:n-word-bits)) - (specifier-type - (list (if (minusp low) 'signed-byte 'unsigned-byte) - (max (integer-length high) - (integer-length low) - (+ size-high posn-high)))) - *universal-type*)) - *universal-type*))) + (%deposit-field-derive-type-aux size posn int)) (deftransform %ldb ((size posn int) (fixnum fixnum integer) @@ -2443,6 +2426,81 @@ (logior (logand new mask) (logand int (lognot mask))))) +;;; Modular functions + +;;; (ldb (byte s 0) (foo x y ...)) = +;;; (ldb (byte s 0) (foo (ldb (byte s 0) x) y ...)) +;;; +;;; and similar for other arguments. + +;;; Try to recursively cut all uses of the continuation CONT to WIDTH +;;; bits. +;;; +;;; For good functions, we just recursively cut arguments; their +;;; "goodness" means that the result will not increase (in the +;;; (unsigned-byte +infinity) sense). An ordinary modular function is +;;; replaced with the version, cutting its result to WIDTH or more +;;; bits. If we have changed anything, we need to flush old derived +;;; types, because they have nothing in common with the new code. +(defun cut-to-width (cont width) + (declare (type continuation cont) (type (integer 0) width)) + (labels ((reoptimize-node (node name) + (setf (node-derived-type node) + (fun-type-returns + (info :function :type name))) + (setf (continuation-%derived-type (node-cont node)) nil) + (setf (node-reoptimize node) t) + (setf (block-reoptimize (node-block node)) t) + (setf (component-reoptimize (node-component node)) t)) + (cut-node (node &aux did-something) + (when (and (combination-p node) + (fun-info-p (basic-combination-kind node))) + (let* ((fun-ref (continuation-use (combination-fun node))) + (fun-name (leaf-source-name (ref-leaf fun-ref))) + (modular-fun (find-modular-version fun-name width)) + (name (and (modular-fun-info-p modular-fun) + (modular-fun-info-name modular-fun)))) + (when (and modular-fun + (not (and (eq name 'logand) + (csubtypep + (single-value-type (node-derived-type node)) + (specifier-type `(unsigned-byte ,width)))))) + (unless (eq modular-fun :good) + (setq did-something t) + (change-ref-leaf + fun-ref + (find-free-fun name "in a strange place")) + (setf (combination-kind node) :full)) + (dolist (arg (basic-combination-args node)) + (when (cut-continuation arg) + (setq did-something t))) + (when did-something + (reoptimize-node node fun-name)) + did-something)))) + (cut-continuation (cont &aux did-something) + (do-uses (node cont) + (when (cut-node node) + (setq did-something t))) + did-something)) + (cut-continuation cont))) + +(defoptimizer (logand optimizer) ((x y) node) + (let ((result-type (single-value-type (node-derived-type node)))) + (when (numeric-type-p result-type) + (let ((low (numeric-type-low result-type)) + (high (numeric-type-high result-type))) + (when (and (numberp low) + (numberp high) + (>= low 0)) + (let ((width (integer-length high))) + (when (some (lambda (x) (<= width x)) + *modular-funs-widths*) + ;; FIXME: This should be (CUT-TO-WIDTH NODE WIDTH). + (cut-to-width x width) + (cut-to-width y width) + nil ; After fixing above, replace with T. + ))))))) + ;;; miscellanous numeric transforms ;;; If a constant appears as the first arg, swap the args. @@ -2501,54 +2559,6 @@ `(- (ash x ,len)) `(ash x ,len)))) -;;; If both arguments and the result are (UNSIGNED-BYTE 32), try to -;;; come up with a ``better'' multiplication using multiplier -;;; recoding. There are two different ways the multiplier can be -;;; recoded. The more obvious is to shift X by the correct amount for -;;; each bit set in Y and to sum the results. But if there is a string -;;; of bits that are all set, you can add X shifted by one more then -;;; the bit position of the first set bit and subtract X shifted by -;;; the bit position of the last set bit. We can't use this second -;;; method when the high order bit is bit 31 because shifting by 32 -;;; doesn't work too well. -(deftransform * ((x y) - ((unsigned-byte 32) (unsigned-byte 32)) - (unsigned-byte 32)) - "recode as shift and add" - (unless (constant-continuation-p y) - (give-up-ir1-transform)) - (let ((y (continuation-value y)) - (result nil) - (first-one nil)) - (labels ((tub32 (x) `(truly-the (unsigned-byte 32) ,x)) - (add (next-factor) - (setf result - (tub32 - (if result - `(+ ,result ,(tub32 next-factor)) - next-factor))))) - (declare (inline add)) - (dotimes (bitpos 32) - (if first-one - (when (not (logbitp bitpos y)) - (add (if (= (1+ first-one) bitpos) - ;; There is only a single bit in the string. - `(ash x ,first-one) - ;; There are at least two. - `(- ,(tub32 `(ash x ,bitpos)) - ,(tub32 `(ash x ,first-one))))) - (setf first-one nil)) - (when (logbitp bitpos y) - (setf first-one bitpos)))) - (when first-one - (cond ((= first-one 31)) - ((= first-one 30) - (add '(ash x 30))) - (t - (add `(- ,(tub32 '(ash x 31)) ,(tub32 `(ash x ,first-one)))))) - (add '(ash x 31)))) - (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)), do FLOOR and correct a ;;; remainder. @@ -2644,6 +2654,17 @@ (def logxor -1 (lognot x)) (def logxor 0 x)) +(deftransform logand ((x y) (* (constant-arg t)) *) + "fold identity operation" + (let ((y (continuation-value y))) + (unless (and (plusp y) + (= y (1- (ash 1 (integer-length y))))) + (give-up-ir1-transform)) + (unless (csubtypep (continuation-type x) + (specifier-type `(integer 0 ,y))) + (give-up-ir1-transform)) + '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-arg (member 0)) rational) *) @@ -2737,7 +2758,19 @@ ;; multiplication and division for small integral powers. (unless (not-more-contagious y x) (give-up-ir1-transform)) - (cond ((zerop val) '(float 1 x)) + (cond ((zerop val) + (let ((x-type (continuation-type x))) + (cond ((csubtypep x-type (specifier-type '(or rational + (complex rational)))) + '1) + ((csubtypep x-type (specifier-type 'real)) + `(if (rationalp x) + 1 + (float 1 x))) + ((csubtypep x-type (specifier-type 'complex)) + ;; both parts are float + `(1+ (* x ,val))) + (t (give-up-ir1-transform))))) ((= val 2) '(* x x)) ((= val -2) '(/ (* x x))) ((= val 3) '(* x x x)) @@ -2803,8 +2836,8 @@ ;;; change. (defun same-leaf-ref-p (x y) (declare (type continuation x y)) - (let ((x-use (continuation-use x)) - (y-use (continuation-use y))) + (let ((x-use (principal-continuation-use x)) + (y-use (principal-continuation-use y))) (and (ref-p x-use) (ref-p y-use) (eq (ref-leaf x-use) (ref-leaf y-use)) @@ -2959,6 +2992,24 @@ #-sb-xc-host ; (See CROSS-FLOAT-INFINITY-KLUDGE.) (deftransform > ((x y) (float float) *) (ir1-transform-< y x x y '<)) + +(defun ir1-transform-char< (x y first second inverse) + (cond + ((same-leaf-ref-p x y) nil) + ;; If we had interval representation of character types, as we + ;; might eventually have to to support 2^21 characters, then here + ;; we could do some compile-time computation as in IR1-TRANSFORM-< + ;; above. -- CSR, 2003-07-01 + ((and (constant-continuation-p first) + (not (constant-continuation-p second))) + `(,inverse y x)) + (t (give-up-ir1-transform)))) + +(deftransform char< ((x y) (character character) *) + (ir1-transform-char< x y x y 'char>)) + +(deftransform char> ((x y) (character character) *) + (ir1-transform-char< y x x y 'char<)) ;;;; converting N-arg comparisons ;;;; @@ -2975,11 +3026,11 @@ ;;; negated test as appropriate. If it is a degenerate one-arg call, ;;; then we transform to code that returns true. Otherwise, we bind ;;; all the arguments and expand into a bunch of IFs. -(declaim (ftype (function (symbol list boolean) *) multi-compare)) -(defun multi-compare (predicate args not-p) +(declaim (ftype (function (symbol list boolean t) *) multi-compare)) +(defun multi-compare (predicate args not-p type) (let ((nargs (length args))) (cond ((< nargs 1) (values nil t)) - ((= nargs 1) `(progn ,@args t)) + ((= nargs 1) `(progn (the ,type ,@args) t)) ((= nargs 2) (if not-p `(if (,predicate ,(first args) ,(second args)) nil t) @@ -2995,40 +3046,46 @@ `(if (,predicate ,current ,last) ,result nil)))) ((zerop i) - `((lambda ,vars ,result) . ,args))))))) - -(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)) - -(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)) + `((lambda ,vars (declare (type ,type ,@vars)) ,result) + ,@args))))))) + +(define-source-transform = (&rest args) (multi-compare '= args nil 'number)) +(define-source-transform < (&rest args) (multi-compare '< args nil 'real)) +(define-source-transform > (&rest args) (multi-compare '> args nil 'real)) +(define-source-transform <= (&rest args) (multi-compare '> args t 'real)) +(define-source-transform >= (&rest args) (multi-compare '< args t 'real)) + +(define-source-transform char= (&rest args) (multi-compare 'char= args nil + 'character)) +(define-source-transform char< (&rest args) (multi-compare 'char< args nil + 'character)) +(define-source-transform char> (&rest args) (multi-compare 'char> args nil + 'character)) +(define-source-transform char<= (&rest args) (multi-compare 'char> args t + 'character)) +(define-source-transform char>= (&rest args) (multi-compare 'char< args t + 'character)) (define-source-transform char-equal (&rest args) - (multi-compare 'char-equal args nil)) + (multi-compare 'char-equal args nil 'character)) (define-source-transform char-lessp (&rest args) - (multi-compare 'char-lessp args nil)) + (multi-compare 'char-lessp args nil 'character)) (define-source-transform char-greaterp (&rest args) - (multi-compare 'char-greaterp args nil)) + (multi-compare 'char-greaterp args nil 'character)) (define-source-transform char-not-greaterp (&rest args) - (multi-compare 'char-greaterp args t)) + (multi-compare 'char-greaterp args t 'character)) (define-source-transform char-not-lessp (&rest args) - (multi-compare 'char-lessp args t)) + (multi-compare 'char-lessp args t 'character)) ;;; This function does source transformation of N-arg inequality ;;; 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)) -(defun multi-not-equal (predicate args) +(declaim (ftype (function (symbol list t) *) multi-not-equal)) +(defun multi-not-equal (predicate args type) (let ((nargs (length args))) (cond ((< nargs 1) (values nil t)) - ((= nargs 1) `(progn ,@args t)) + ((= nargs 1) `(progn (the ,type ,@args) t)) ((= nargs 2) `(if (,predicate ,(first args) ,(second args)) nil t)) ((not (policy *lexenv* @@ -3041,24 +3098,18 @@ (next (cdr vars) (cdr next)) (result t)) ((null next) - `((lambda ,vars ,result) . ,args)) + `((lambda ,vars (declare (type ,type ,@vars)) ,result) + ,@args)) (let ((v1 (first var))) (dolist (v2 next) (setq result `(if (,predicate ,v1 ,v2) nil ,result)))))))))) -(define-source-transform /= (&rest args) (multi-not-equal '= args)) -(define-source-transform char/= (&rest args) (multi-not-equal 'char= args)) +(define-source-transform /= (&rest args) + (multi-not-equal '= args 'number)) +(define-source-transform char/= (&rest args) + (multi-not-equal 'char= args 'character)) (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))) + (multi-not-equal 'char-equal args 'character)) ;;; Expand MAX and MIN into the obvious comparisons. (define-source-transform max (arg0 &rest rest) @@ -3176,6 +3227,49 @@ ;;;; or T and the control string is a function (i.e. FORMATTER), then ;;;; convert the call to FORMAT to just a FUNCALL of that function. +;;; for compile-time argument count checking. +;;; +;;; FIXME I: this is currently called from DEFTRANSFORMs, the vast +;;; majority of which are not going to transform the code, but instead +;;; are going to GIVE-UP-IR1-TRANSFORM unconditionally. It would be +;;; nice to make this explicit, maybe by implementing a new +;;; "optimizer" (say, DEFOPTIMIZER CONSISTENCY-CHECK). +;;; +;;; FIXME II: In some cases, type information could be correlated; for +;;; instance, ~{ ... ~} requires a list argument, so if the +;;; continuation-type of a corresponding argument is known and does +;;; not intersect the list type, a warning could be signalled. +(defun check-format-args (string args fun) + (declare (type string string)) + (unless (typep string 'simple-string) + (setq string (coerce string 'simple-string))) + (multiple-value-bind (min max) + (handler-case (sb!format:%compiler-walk-format-string string args) + (sb!format:format-error (c) + (compiler-warn "~A" c))) + (when min + (let ((nargs (length args))) + (cond + ((< nargs min) + (compiler-warn "Too few arguments (~D) to ~S ~S: ~ + requires at least ~D." + nargs fun string min)) + ((> nargs max) + (;; to get warned about probably bogus code at + ;; cross-compile time. + #+sb-xc-host compiler-warn + ;; ANSI saith that too many arguments doesn't cause a + ;; run-time error. + #-sb-xc-host compiler-style-warn + "Too many arguments (~D) to ~S ~S: uses at most ~D." + nargs fun string max))))))) + +(defoptimizer (format optimizer) ((dest control &rest args)) + (when (constant-continuation-p control) + (let ((x (continuation-value control))) + (when (stringp x) + (check-format-args x args 'format))))) + (deftransform format ((dest control &rest args) (t simple-string &rest t) * :policy (> speed space)) (unless (constant-continuation-p control) @@ -3200,6 +3294,60 @@ (funcall control *standard-output* ,@arg-names) nil))) +(macrolet + ((def (name) + `(defoptimizer (,name optimizer) ((control &rest args)) + (when (constant-continuation-p control) + (let ((x (continuation-value control))) + (when (stringp x) + (check-format-args x args ',name))))))) + (def error) + (def warn) + #+sb-xc-host ; Only we should be using these + (progn + (def style-warn) + (def compiler-abort) + (def compiler-error) + (def compiler-warn) + (def compiler-style-warn) + (def compiler-notify) + (def maybe-compiler-notify) + (def bug))) + +(defoptimizer (cerror optimizer) ((report control &rest args)) + (when (and (constant-continuation-p control) + (constant-continuation-p report)) + (let ((x (continuation-value control)) + (y (continuation-value report))) + (when (and (stringp x) (stringp y)) + (multiple-value-bind (min1 max1) + (handler-case + (sb!format:%compiler-walk-format-string x args) + (sb!format:format-error (c) + (compiler-warn "~A" c))) + (when min1 + (multiple-value-bind (min2 max2) + (handler-case + (sb!format:%compiler-walk-format-string y args) + (sb!format:format-error (c) + (compiler-warn "~A" c))) + (when min2 + (let ((nargs (length args))) + (cond + ((< nargs (min min1 min2)) + (compiler-warn "Too few arguments (~D) to ~S ~S ~S: ~ + requires at least ~D." + nargs 'cerror y x (min min1 min2))) + ((> nargs (max max1 max2)) + (;; to get warned about probably bogus code at + ;; cross-compile time. + #+sb-xc-host compiler-warn + ;; ANSI saith that too many arguments doesn't cause a + ;; run-time error. + #-sb-xc-host compiler-style-warn + "Too many arguments (~D) to ~S ~S ~S: uses at most ~D." + nargs 'cerror y x (max max1 max2))))))))))))) + (defoptimizer (coerce derive-type) ((value type)) (cond ((constant-continuation-p type) @@ -3431,7 +3579,7 @@ (loop for i of-type index from (ash current-heap-size -1) downto 1 do (%heapify i)) - (loop + (loop (when (< current-heap-size 2) (return)) (rotatef (%elt 1) (%elt current-heap-size)) @@ -3479,5 +3627,5 @@ (format t "/(CONTINUATION-VALUE X)=~S~%" (continuation-value x))) (format t "/MESSAGE=~S~%" (continuation-value message)) (give-up-ir1-transform "not a real transform")) - (defun /report-continuation (&rest rest) - (declare (ignore rest)))) + (defun /report-continuation (x message) + (declare (ignore x message))))