0.9.1.51:
[sbcl.git] / src / compiler / srctran.lisp
index 6b554db..b4214fc 100644 (file)
 (defun source-transform-cxr (form)
   (if (/= (length form) 2)
       (values nil t)
-      (let ((name (symbol-name (car form))))
-       (do ((i (- (length name) 2) (1- i))
+      (let* ((name (car form))
+            (string (symbol-name
+                     (etypecase name
+                       (symbol name)
+                       (leaf (leaf-source-name name))))))
+       (do ((i (- (length string) 2) (1- i))
             (res (cadr form)
-                 `(,(ecase (char name i)
+                 `(,(ecase (char string i)
                       (#\A 'car)
                       (#\D 'cdr))
                    ,res)))
                       #'%unary-truncate-derive-type-aux
                       #'%unary-truncate))
 
+(defoptimizer (%unary-ftruncate derive-type) ((number))
+  (let ((divisor (specifier-type '(integer 1 1))))
+    (one-arg-derive-type number
+                         #'(lambda (n)
+                             (ftruncate-derive-type-quot-aux n divisor nil))
+                         #'%unary-ftruncate)))
+
 ;;; Define optimizers for FLOOR and CEILING.
 (macrolet
     ((def (name q-name r-name)
                (or (null min) (minusp min))))
       (values nil t t)))
 
+;;; See _Hacker's Delight_, Henry S. Warren, Jr. pp 58-63 for an
+;;; explanation of LOG{AND,IOR,XOR}-DERIVE-UNSIGNED-{LOW,HIGH}-BOUND.
+;;; Credit also goes to Raymond Toy for writing (and debugging!) similar
+;;; versions in CMUCL, from which these functions copy liberally.
+
+(defun logand-derive-unsigned-low-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (lognor a c))) then (ash m -1)
+          until (zerop m) do
+          (unless (zerop (logand m (lognot a) (lognot c)))
+            (let ((temp (logandc2 (logior a m) (1- m))))
+              (when (<= temp b)
+                (setf a temp)
+                (loop-finish))
+              (setf temp (logandc2 (logior c m) (1- m)))
+              (when (<= temp d)
+                (setf c temp)
+                (loop-finish))))
+          finally (return (logand a c)))))
+
+(defun logand-derive-unsigned-high-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (logxor b d))) then (ash m -1)
+          until (zerop m) do
+          (cond
+            ((not (zerop (logand b (lognot d) m)))
+             (let ((temp (logior (logandc2 b m) (1- m))))
+               (when (>= temp a)
+                 (setf b temp)
+                 (loop-finish))))
+            ((not (zerop (logand (lognot b) d m)))
+             (let ((temp (logior (logandc2 d m) (1- m))))
+               (when (>= temp c)
+                 (setf d temp)
+                 (loop-finish)))))
+          finally (return (logand b d)))))
+
 (defun logand-derive-type-aux (x y &optional same-leaf)
-  (declare (ignore same-leaf))
+  (when same-leaf
+    (return-from logand-derive-type-aux x))
   (multiple-value-bind (x-len x-pos x-neg) (integer-type-length x)
     (declare (ignore x-pos))
-    (multiple-value-bind (y-len y-pos y-neg) (integer-type-length  y)
+    (multiple-value-bind (y-len y-pos y-neg) (integer-type-length y)
       (declare (ignore y-pos))
       (if (not x-neg)
          ;; X must be positive.
          (if (not y-neg)
              ;; They must both be positive.
-             (cond ((or (null x-len) (null y-len))
+             (cond ((and (null x-len) (null y-len))
                     (specifier-type 'unsigned-byte))
+                   ((null x-len)
+                    (specifier-type `(unsigned-byte* ,y-len)))
+                   ((null y-len)
+                    (specifier-type `(unsigned-byte* ,x-len)))
                    (t
-                    (specifier-type `(unsigned-byte* ,(min x-len y-len)))))
+                     (let ((low (logand-derive-unsigned-low-bound x y))
+                           (high (logand-derive-unsigned-high-bound x y)))
+                       (specifier-type `(integer ,low ,high)))))
              ;; X is positive, but Y might be negative.
              (cond ((null x-len)
                     (specifier-type 'unsigned-byte))
                  ;; We can't tell squat about the result.
                  (specifier-type 'integer)))))))
 
+(defun logior-derive-unsigned-low-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (logxor a c))) then (ash m -1)
+          until (zerop m) do
+          (cond
+            ((not (zerop (logandc2 (logand c m) a)))
+             (let ((temp (logand (logior a m) (1+ (lognot m)))))
+               (when (<= temp b)
+                 (setf a temp)
+                 (loop-finish))))
+            ((not (zerop (logandc2 (logand a m) c)))
+             (let ((temp (logand (logior c m) (1+ (lognot m)))))
+               (when (<= temp d)
+                 (setf c temp)
+                 (loop-finish)))))
+          finally (return (logior a c)))))
+
+(defun logior-derive-unsigned-high-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (logand b d))) then (ash m -1)
+          until (zerop m) do
+          (unless (zerop (logand b d m))
+            (let ((temp (logior (- b m) (1- m))))
+              (when (>= temp a)
+                (setf b temp)
+                (loop-finish))
+              (setf temp (logior (- d m) (1- m)))
+              (when (>= temp c)
+                (setf d temp)
+                (loop-finish))))
+          finally (return (logior b d)))))
+
 (defun logior-derive-type-aux (x y &optional same-leaf)
-  (declare (ignore same-leaf))
+  (when same-leaf
+    (return-from logior-derive-type-aux x))
   (multiple-value-bind (x-len x-pos x-neg) (integer-type-length x)
     (multiple-value-bind (y-len y-pos y-neg) (integer-type-length 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)
-                                             '*))))
+        (if (and x-len y-len)
+            (let ((low (logior-derive-unsigned-low-bound x y))
+                  (high (logior-derive-unsigned-high-bound x y)))
+              (specifier-type `(integer ,low ,high)))
+            (specifier-type `(unsigned-byte* *))))
        ((not x-pos)
        ;; X must be negative.
        (if (not y-pos)
                ;; Unbounded.
                (specifier-type 'integer))))))))
 
+(defun logxor-derive-unsigned-low-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (logxor a c))) then (ash m -1)
+          until (zerop m) do
+          (cond
+            ((not (zerop (logandc2 (logand c m) a)))
+             (let ((temp (logand (logior a m)
+                                 (1+ (lognot m)))))
+               (when (<= temp b)
+                 (setf a temp))))
+            ((not (zerop (logandc2 (logand a m) c)))
+             (let ((temp (logand (logior c m)
+                                 (1+ (lognot m)))))
+               (when (<= temp d)
+                 (setf c temp)))))
+          finally (return (logxor a c)))))
+
+(defun logxor-derive-unsigned-high-bound (x y)
+  (let ((a (numeric-type-low x))
+        (b (numeric-type-high x))
+        (c (numeric-type-low y))
+        (d (numeric-type-high y)))
+    (loop for m = (ash 1 (integer-length (logand b d))) then (ash m -1)
+          until (zerop m) do
+          (unless (zerop (logand b d m))
+            (let ((temp (logior (- b m) (1- m))))
+              (cond
+                ((>= temp a) (setf b temp))
+                (t (let ((temp (logior (- d m) (1- m))))
+                     (when (>= temp c)
+                       (setf d temp)))))))
+          finally (return (logxor b d)))))
+
 (defun logxor-derive-type-aux (x y &optional same-leaf)
-  (declare (ignore same-leaf))
+  (when same-leaf
+    (return-from logxor-derive-type-aux (specifier-type '(eql 0))))
   (multiple-value-bind (x-len x-pos x-neg) (integer-type-length x)
     (multiple-value-bind (y-len y-pos y-neg) (integer-type-length 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 or 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))))))
+        ((and (not x-neg) (not y-neg))
+         ;; Both are positive
+         (if (and x-len y-len)
+             (let ((low (logxor-derive-unsigned-low-bound x y))
+                   (high (logxor-derive-unsigned-high-bound x y)))
+               (specifier-type `(integer ,low ,high)))
+             (specifier-type '(unsigned-byte* *))))
+        ((and (not x-pos) (not y-pos))
+         ;; Both are negative.  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-pos) (not x-neg)))
+         ;; Either X is negative and Y is positive or 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))))))
 
 (macrolet ((deffrob (logfun)
             (let ((fun-aux (symbolicate logfun "-DERIVE-TYPE-AUX")))
   (deffrob logior)
   (deffrob logxor))
 
-;;; FIXME: could actually do stuff with SAME-LEAF
 (defoptimizer (logeqv derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
                             (lognot-derive-type-aux 
-                             (logxor-derive-type-aux x y same-leaf))) 
+                             (logxor-derive-type-aux x y same-leaf)))
                       #'logeqv))
 (defoptimizer (lognand derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
                       #'lognor))
 (defoptimizer (logandc1 derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
-                            (logand-derive-type-aux
-                             (lognot-derive-type-aux x) y nil))
+                            (if same-leaf
+                                (specifier-type '(eql 0))
+                                (logand-derive-type-aux
+                                 (lognot-derive-type-aux x) y nil)))
                       #'logandc1))
 (defoptimizer (logandc2 derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
-                            (logand-derive-type-aux
-                             x (lognot-derive-type-aux y) nil))
+                            (if same-leaf
+                                (specifier-type '(eql 0))
+                                (logand-derive-type-aux
+                                 x (lognot-derive-type-aux y) nil)))
                       #'logandc2))
 (defoptimizer (logorc1 derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
-                            (logior-derive-type-aux
-                             (lognot-derive-type-aux x) y nil))
+                            (if same-leaf
+                                (specifier-type '(eql -1))
+                                (logior-derive-type-aux
+                                 (lognot-derive-type-aux x) y nil)))
                       #'logorc1))
 (defoptimizer (logorc2 derive-type) ((x y))
   (two-arg-derive-type x y (lambda (x y same-leaf)
-                            (logior-derive-type-aux
-                             x (lognot-derive-type-aux y) nil))
+                            (if same-leaf
+                                (specifier-type '(eql -1))
+                                (logior-derive-type-aux
+                                 x (lognot-derive-type-aux y) nil)))
                       #'logorc2))
 \f
 ;;;; miscellaneous derive-type methods
         (specifier-type `(integer ,lo-res ,hi-res))))))
 
 (defoptimizer (code-char derive-type) ((code))
-  (specifier-type 'base-char))
+  (let ((type (lvar-type code)))
+    ;; FIXME: unions of integral ranges?  It ought to be easier to do
+    ;; this, given that CHARACTER-SET is basically an integral range
+    ;; type.  -- CSR, 2004-10-04
+    (when (numeric-type-p type)
+      (let* ((lo (numeric-type-low type))
+             (hi (numeric-type-high type))
+             (type (specifier-type `(character-set ((,lo . ,hi))))))
+        (cond
+          ;; KLUDGE: when running on the host, we lose a slight amount
+          ;; of precision so that we don't have to "unparse" types
+          ;; that formally we can't, such as (CHARACTER-SET ((0
+          ;; . 0))).  -- CSR, 2004-10-06
+          #+sb-xc-host
+          ((csubtypep type (specifier-type 'standard-char)) type)
+          #+sb-xc-host
+          ((csubtypep type (specifier-type 'base-char))
+           (specifier-type 'base-char))
+          #+sb-xc-host
+          ((csubtypep type (specifier-type 'extended-char))
+           (specifier-type 'extended-char))
+          (t #+sb-xc-host (specifier-type 'character)
+             #-sb-xc-host type))))))
 
 (defoptimizer (values derive-type) ((&rest values))
   (make-values-type :required (mapcar #'lvar-type values)))
   `(let ((mask (ash (ldb (byte size 0) -1) posn)))
      (logior (logand new mask)
             (logand int (lognot mask)))))
+
+(defoptimizer (mask-signed-field derive-type) ((size x))
+  (let ((size (lvar-type size)))
+    (if (numeric-type-p size)
+       (let ((size-high (numeric-type-high size)))
+         (if (and size-high (<= 1 size-high sb!vm:n-word-bits))
+             (specifier-type `(signed-byte ,size-high))
+             *universal-type*))
+       *universal-type*)))
+
 \f
 ;;; Modular functions
 
 ;;;
 ;;; and similar for other arguments.
 
+(defun make-modular-fun-type-deriver (prototype class width)
+  #!-sb-fluid
+  (binding* ((info (info :function :info prototype) :exit-if-null)
+             (fun (fun-info-derive-type info) :exit-if-null)
+             (mask-type (specifier-type
+                         (ecase class
+                             (:unsigned (let ((mask (1- (ash 1 width))))
+                                          `(integer ,mask ,mask)))
+                             (:signed `(signed-byte ,width))))))
+    (lambda (call)
+      (let ((res (funcall fun call)))
+        (when res
+          (if (eq class :unsigned)
+              (logand-derive-type-aux res mask-type))))))
+  #!+sb-fluid
+  (lambda (call)
+    (binding* ((info (info :function :info prototype) :exit-if-null)
+               (fun (fun-info-derive-type info) :exit-if-null)
+               (res (funcall fun call) :exit-if-null)
+               (mask-type (specifier-type
+                           (ecase class
+                             (:unsigned (let ((mask (1- (ash 1 width))))
+                                          `(integer ,mask ,mask)))
+                             (:signed `(signed-byte ,width))))))
+      (if (eq class :unsigned)
+          (logand-derive-type-aux res mask-type)))))
+
 ;;; Try to recursively cut all uses of LVAR to WIDTH bits.
 ;;;
 ;;; For good functions, we just recursively cut arguments; their
 ;;; modular version, if it exists, or NIL. 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 (lvar width)
+(defun cut-to-width (lvar class width)
   (declare (type lvar lvar) (type (integer 0) width))
-  (labels ((reoptimize-node (node name)
-             (setf (node-derived-type node)
-                   (fun-type-returns
-                    (info :function :type name)))
-             (setf (lvar-%derived-type (node-lvar 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 (not (block-delete-p (node-block node)))
-                        (combination-p node)
-                        (fun-info-p (basic-combination-kind node)))
-               (let* ((fun-ref (lvar-use (combination-fun node)))
-                      (fun-name (leaf-source-name (ref-leaf fun-ref)))
-                      (modular-fun (find-modular-version fun-name width)))
-                 (when (and modular-fun
-                            (not (and (eq fun-name 'logand)
-                                      (csubtypep
-                                       (single-value-type (node-derived-type node))
-                                       (specifier-type `(unsigned-byte* ,width))))))
-                   (binding* ((name (etypecase modular-fun
-                                      ((eql :good) fun-name)
-                                      (modular-fun-info
-                                       (modular-fun-info-name modular-fun))
-                                      (function
-                                       (funcall modular-fun node width)))
-                                :exit-if-null))
-                     (unless (eql 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))
-                     (unless (functionp modular-fun)
-                       (dolist (arg (basic-combination-args node))
-                         (when (cut-lvar arg)
-                           (setq did-something t))))
-                     (when did-something
-                       (reoptimize-node node name))
-                     did-something)))))
-           (cut-lvar (lvar &aux did-something)
-             (do-uses (node lvar)
-               (when (cut-node node)
-                 (setq did-something t)))
-             did-something))
-    (cut-lvar lvar)))
+  (let ((type (specifier-type (if (zerop width)
+                                  '(eql 0)
+                                  `(,(ecase class (:unsigned 'unsigned-byte)
+                                            (:signed 'signed-byte))
+                                     ,width)))))
+    (labels ((reoptimize-node (node name)
+               (setf (node-derived-type node)
+                     (fun-type-returns
+                      (info :function :type name)))
+               (setf (lvar-%derived-type (node-lvar node)) nil)
+               (setf (node-reoptimize node) t)
+               (setf (block-reoptimize (node-block node)) t)
+               (reoptimize-component (node-component node) :maybe))
+             (cut-node (node &aux did-something)
+               (when (and (not (block-delete-p (node-block node)))
+                          (combination-p node)
+                          (eq (basic-combination-kind node) :known))
+                 (let* ((fun-ref (lvar-use (combination-fun node)))
+                        (fun-name (leaf-source-name (ref-leaf fun-ref)))
+                        (modular-fun (find-modular-version fun-name class width)))
+                   (when (and modular-fun
+                              (not (and (eq fun-name 'logand)
+                                        (csubtypep
+                                         (single-value-type (node-derived-type node))
+                                         type))))
+                     (binding* ((name (etypecase modular-fun
+                                        ((eql :good) fun-name)
+                                        (modular-fun-info
+                                         (modular-fun-info-name modular-fun))
+                                        (function
+                                         (funcall modular-fun node width)))
+                                      :exit-if-null))
+                               (unless (eql 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))
+                               (unless (functionp modular-fun)
+                                 (dolist (arg (basic-combination-args node))
+                                   (when (cut-lvar arg)
+                                     (setq did-something t))))
+                               (when did-something
+                                 (reoptimize-node node name))
+                               did-something)))))
+             (cut-lvar (lvar &aux did-something)
+               (do-uses (node lvar)
+                 (when (cut-node node)
+                   (setq did-something t)))
+               did-something))
+      (cut-lvar lvar))))
 
 (defoptimizer (logand optimizer) ((x y) node)
   (let ((result-type (single-value-type (node-derived-type node))))
                    (>= low 0))
           (let ((width (integer-length high)))
             (when (some (lambda (x) (<= width x))
-                        *modular-funs-widths*)
+                        (modular-class-widths *unsigned-modular-class*))
+              ;; FIXME: This should be (CUT-TO-WIDTH NODE WIDTH).
+              (cut-to-width x :unsigned width)
+              (cut-to-width y :unsigned width)
+              nil ; After fixing above, replace with T.
+              )))))))
+
+(defoptimizer (mask-signed-field optimizer) ((width x) 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))
+          (let ((width (max (integer-length high) (integer-length low))))
+            (when (some (lambda (x) (<= width x))
+                        (modular-class-widths *signed-modular-class*))
               ;; FIXME: This should be (CUT-TO-WIDTH NODE WIDTH).
-              (cut-to-width x width)
-              (cut-to-width y width)
+              (cut-to-width x :signed width)
               nil ; After fixing above, replace with T.
               )))))))
 \f
   (let* ((y (lvar-value y))
         (y-abs (abs y))
         (len (1- (integer-length y-abs))))
-    (unless (= y-abs (ash 1 len))
+    (unless (and (> y-abs 0) (= y-abs (ash 1 len)))
       (give-up-ir1-transform))
     (if (minusp y)
        `(- (ash x ,len))
         (let* ((y (lvar-value y))
                (y-abs (abs y))
                (len (1- (integer-length y-abs))))
-          (unless (= y-abs (ash 1 len))
+          (unless (and (> y-abs 0) (= y-abs (ash 1 len)))
             (give-up-ir1-transform))
           (let ((shift (- len))
                 (mask (1- y-abs))
   (let* ((y (lvar-value y))
         (y-abs (abs y))
         (len (1- (integer-length y-abs))))
-    (unless (= y-abs (ash 1 len))
+    (unless (and (> y-abs 0) (= y-abs (ash 1 len)))
       (give-up-ir1-transform))
     (let ((mask (1- y-abs)))
       (if (minusp y)
   (let* ((y (lvar-value y))
         (y-abs (abs y))
         (len (1- (integer-length y-abs))))
-    (unless (= y-abs (ash 1 len))
+    (unless (and (> y-abs 0) (= y-abs (ash 1 len)))
       (give-up-ir1-transform))
     (let* ((shift (- len))
           (mask (1- y-abs)))
   (let* ((y (lvar-value y))
         (y-abs (abs y))
         (len (1- (integer-length y-abs))))
-    (unless (= y-abs (ash 1 len))
+    (unless (and (> y-abs 0) (= y-abs (ash 1 len)))
       (give-up-ir1-transform))
     (let ((mask (1- y-abs)))
       `(if (minusp x)
       (give-up-ir1-transform))
     'x))
 
+(deftransform mask-signed-field ((size x) ((constant-arg t) *) *)
+  "fold identity operation"
+  (let ((size (lvar-value size)))
+    (unless (csubtypep (lvar-type x) (specifier-type `(signed-byte ,size)))
+      (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) *)
      (or (zerop sum)
         (when (eql sum #x20)
           (let ((sum (+ ac bc)))
-            (and (> sum 161) (< sum 213)))))))
+             (or (and (> sum 161) (< sum 213))
+                 (and (> sum 415) (< sum 461))
+                 (and (> sum 463) (< sum 477))))))))
 
 (deftransform char-upcase ((x) (base-char))
   "open code"
   '(let ((n-code (char-code x)))
-     (if (and (> n-code #o140) ; Octal 141 is #\a.
-             (< n-code #o173)) ; Octal 172 is #\z.
+     (if (or (and (> n-code #o140)     ; Octal 141 is #\a.
+                  (< n-code #o173))    ; Octal 172 is #\z.
+             (and (> n-code #o337)
+                  (< n-code #o367))
+             (and (> n-code #o367)
+                  (< n-code #o377)))
         (code-char (logxor #x20 n-code))
         x)))
 
 (deftransform char-downcase ((x) (base-char))
   "open code"
   '(let ((n-code (char-code x)))
-     (if (and (> n-code 64)    ; 65 is #\A.
-             (< n-code 91))    ; 90 is #\Z.
+     (if (or (and (> n-code 64)                ; 65 is #\A.
+                  (< n-code 91))        ; 90 is #\Z.
+             (and (> n-code 191)
+                  (< n-code 215))
+             (and (> n-code 215)
+                  (< n-code 223)))
         (code-char (logxor #x20 n-code))
         x)))
 \f
 ;;; then the result is definitely false.
 (deftransform simple-equality-transform ((x y) * *
                                         :defun-only t)
-  (cond ((same-leaf-ref-p x y)
-        t)
-       ((not (types-equal-or-intersect (lvar-type x)
-                                       (lvar-type y)))
+  (cond
+    ((same-leaf-ref-p x y) t)
+    ((not (types-equal-or-intersect (lvar-type x) (lvar-type y)))
         nil)
-       (t
-        (give-up-ir1-transform))))
+    (t (give-up-ir1-transform))))
 
 (macrolet ((def (x)
              `(%deftransform ',x '(function * *) #'simple-equality-transform)))
   (def eq)
-  (def char=)
-  (def equal))
+  (def char=))
 
-;;; This is similar to SIMPLE-EQUALITY-PREDICATE, except that we also
+;;; This is similar to SIMPLE-EQUALITY-TRANSFORM, 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.
-;;; -- If either arg is definitely not a number, then we can compare
-;;;    with EQ.
+;;; -- If either arg is definitely a fixnum we punt and let the backend
+;;;    deal with it.
+;;; -- If either arg is definitely not a number or a fixnum, then we
+;;;    can compare with EQ.
 ;;; -- Otherwise, we try to put the arg we know more about second. If X
 ;;;    is constant then we put it second. If X is a subtype of Y, we put
 ;;;    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 efficiency note.
 (deftransform eql ((x y) * *)
   "convert to simpler equality predicate"
   (let ((x-type (lvar-type x))
        (y-type (lvar-type y))
-       (char-type (specifier-type 'character))
-       (number-type (specifier-type 'number)))
-    (cond ((same-leaf-ref-p x y)
-          t)
-         ((not (types-equal-or-intersect x-type y-type))
-          nil)
-         ((and (csubtypep x-type char-type)
-               (csubtypep y-type char-type))
-          '(char= x y))
-         ((or (not (types-equal-or-intersect x-type number-type))
-              (not (types-equal-or-intersect y-type number-type)))
-          '(eq x y))
-         ((and (not (constant-lvar-p y))
-               (or (constant-lvar-p x)
-                   (and (csubtypep x-type y-type)
-                        (not (csubtypep y-type x-type)))))
-          '(eql y x))
-         (t
-          (give-up-ir1-transform)))))
+       (char-type (specifier-type 'character)))
+    (flet ((simple-type-p (type)
+             (csubtypep type (specifier-type '(or fixnum (not number)))))
+           (fixnum-type-p (type)
+             (csubtypep type (specifier-type 'fixnum))))
+      (cond
+        ((same-leaf-ref-p x y) t)
+        ((not (types-equal-or-intersect x-type y-type))
+         nil)
+        ((and (csubtypep x-type char-type)
+              (csubtypep y-type char-type))
+        '(char= x y))
+        ((or (fixnum-type-p x-type) (fixnum-type-p y-type))
+         (give-up-ir1-transform))
+        ((or (simple-type-p x-type) (simple-type-p y-type))
+         '(eq x y))
+       ((and (not (constant-lvar-p y))
+             (or (constant-lvar-p x)
+                 (and (csubtypep x-type y-type)
+                      (not (csubtypep y-type x-type)))))
+        '(eql y x))
+       (t
+        (give-up-ir1-transform))))))
+
+;;; similarly to the EQL transform above, we attempt to constant-fold
+;;; or convert to a simpler predicate: mostly we have to be careful
+;;; with strings and bit-vectors.
+(deftransform equal ((x y) * *)
+  "convert to simpler equality predicate"
+  (let ((x-type (lvar-type x))
+       (y-type (lvar-type y))
+       (string-type (specifier-type 'string))
+       (bit-vector-type (specifier-type 'bit-vector)))
+    (cond
+      ((same-leaf-ref-p x y) t)
+      ((and (csubtypep x-type string-type)
+           (csubtypep y-type string-type))
+       '(string= x y))
+      ((and (csubtypep x-type bit-vector-type)
+           (csubtypep y-type bit-vector-type))
+       '(bit-vector-= x y))
+      ;; if at least one is not a string, and at least one is not a
+      ;; bit-vector, then we can reason from types.
+      ((and (not (and (types-equal-or-intersect x-type string-type)
+                     (types-equal-or-intersect y-type string-type)))
+           (not (and (types-equal-or-intersect x-type bit-vector-type)
+                     (types-equal-or-intersect y-type bit-vector-type)))
+           (not (types-equal-or-intersect x-type y-type)))
+       nil)
+      (t (give-up-ir1-transform)))))
 
 ;;; Convert to EQL if both args are rational and complexp is specified
 ;;; and the same for both.
 
 ;;; 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 lvar-type
 ;;; of a corresponding argument is known and does not intersect the
       (let ((nargs (length args)))
        (cond
          ((< nargs min)
-          (compiler-warn "Too few arguments (~D) to ~S ~S: ~
-                           requires at least ~D."
-                         nargs fun string min))
+          (warn 'format-too-few-args-warning
+                :format-control
+                "Too few arguments (~D) to ~S ~S: requires at least ~D."
+                :format-arguments (list 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)))))))
+          (warn 'format-too-many-args-warning
+                :format-control
+                "Too many arguments (~D) to ~S ~S: uses at most ~D."
+                :format-arguments (list nargs fun string max))))))))
 
 (defoptimizer (format optimizer) ((dest control &rest args))
   (when (constant-lvar-p control)
                (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)))
+                    (warn 'format-too-few-args-warning
+                          :format-control
+                          "Too few arguments (~D) to ~S ~S ~S: ~
+                            requires at least ~D."
+                          :format-arguments
+                          (list 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)))))))))))))
+                    (warn 'format-too-many-args-warning
+                          :format-control
+                          "Too many arguments (~D) to ~S ~S ~S: ~
+                            uses at most ~D."
+                          :format-arguments
+                          (list nargs 'cerror y x (max max1 max2))))))))))))))
 
 (defoptimizer (coerce derive-type) ((value type))
   (cond