Optimize testing of sealed structures.
[sbcl.git] / src / compiler / typetran.lisp
index 1b48fea..b6c1a8f 100644 (file)
@@ -24,8 +24,8 @@
 ;;;; predicates so complex that the only reasonable implentation is
 ;;;; via function call.
 ;;;;
-;;;; Some standard types (such as SEQUENCE) are best tested by letting
-;;;; the TYPEP source transform do its thing with the expansion. These
+;;;; Some standard types (such as ATOM) are best tested by letting the
+;;;; TYPEP source transform do its thing with the expansion. These
 ;;;; types (and corresponding predicates) are not maintained in this
 ;;;; association. In this case, there need not be any predicate
 ;;;; function unless it is required by the Common Lisp specification.
 ;;;; part of the backend; different backends can support different
 ;;;; sets of predicates.
 
+;;; Establish an association between the type predicate NAME and the
+;;; corresponding TYPE. This causes the type predicate to be
+;;; recognized for purposes of optimization.
 (defmacro define-type-predicate (name type)
-  #!+sb-doc
-  "Define-Type-Predicate Name Type
-  Establish an association between the type predicate Name and the
-  corresponding Type. This causes the type predicate to be recognized for
-  purposes of optimization."
   `(%define-type-predicate ',name ',type))
 (defun %define-type-predicate (name specifier)
   (let ((type (specifier-type specifier)))
     (setf (gethash name *backend-predicate-types*) type)
     (setf *backend-type-predicates*
-         (cons (cons type name)
-               (remove name *backend-type-predicates*
-                       :key #'cdr)))
+          (cons (cons type name)
+                (remove name *backend-type-predicates*
+                        :key #'cdr)))
     (%deftransform name '(function (t) *) #'fold-type-predicate)
     name))
 \f
 ;;; constant. At worst, it will convert to %TYPEP, which will prevent
 ;;; spurious attempts at transformation (and possible repeated
 ;;; warnings.)
-(deftransform typep ((object type))
-  (unless (constant-continuation-p type)
+(deftransform typep ((object type &optional env) * * :node node)
+  (unless (constant-lvar-p type)
     (give-up-ir1-transform "can't open-code test of non-constant type"))
-  `(typep object ',(continuation-value type)))
+  (unless (and (constant-lvar-p env) (null (lvar-value env)))
+    (give-up-ir1-transform "environment argument present and not null"))
+  (multiple-value-bind (expansion fail-p)
+      (source-transform-typep 'object (lvar-value type))
+    (if fail-p
+        (abort-ir1-transform)
+        expansion)))
 
-;;; If the continuation OBJECT definitely is or isn't of the specified
+;;; If the lvar OBJECT definitely is or isn't of the specified
 ;;; type, then return T or NIL as appropriate. Otherwise quietly
 ;;; GIVE-UP-IR1-TRANSFORM.
-(defun ir1-transform-type-predicate (object type)
-  (declare (type continuation object) (type ctype type))
-  (let ((otype (continuation-type object)))
-    (cond ((not (types-intersect otype type))
-          'nil)
-         ((csubtypep otype type)
-          't)
-         (t
-          (give-up-ir1-transform)))))
+(defun ir1-transform-type-predicate (object type node)
+  (declare (type lvar object) (type ctype type))
+  (let ((otype (lvar-type object)))
+    (flet ((tricky ()
+             (cond ((typep type 'alien-type-type)
+                    ;; We don't transform alien type tests until here, because
+                    ;; once we do that the rest of the type system can no longer
+                    ;; reason about them properly -- so we'd miss out on type
+                    ;; derivation, etc.
+                    (delay-ir1-transform node :optimize)
+                    (let ((alien-type (alien-type-type-alien-type type)))
+                      ;; If it's a lisp-rep-type, the CTYPE should be one already.
+                      (aver (not (compute-lisp-rep-type alien-type)))
+                      `(sb!alien::alien-value-typep object ',alien-type)))
+                   (t
+                    (give-up-ir1-transform)))))
+      (cond ((not (types-equal-or-intersect otype type))
+            nil)
+           ((csubtypep otype type)
+            t)
+           ((eq type *empty-type*)
+            nil)
+           (t
+            (let ((intersect (type-intersection2 type otype)))
+              (unless intersect
+                (tricky))
+              (multiple-value-bind (constantp value)
+                  (type-singleton-p intersect)
+                (if constantp
+                    `(eql object ',value)
+                    (tricky)))))))))
 
 ;;; Flush %TYPEP tests whose result is known at compile time.
-(deftransform %typep ((object type))
-  (unless (constant-continuation-p type) (give-up-ir1-transform))
+(deftransform %typep ((object type) * * :node node)
+  (unless (constant-lvar-p type)
+    (give-up-ir1-transform))
   (ir1-transform-type-predicate
    object
-   (specifier-type (continuation-value type))))
+   (ir1-transform-specifier-type (lvar-value type))
+   node))
 
 ;;; This is the IR1 transform for simple type predicates. It checks
 ;;; whether the single argument is known to (not) be of the
 ;;; appropriate type, expanding to T or NIL as appropriate.
 (deftransform fold-type-predicate ((object) * * :node node :defun-only t)
-  (let ((ctype (gethash (leaf-name
-                        (ref-leaf
-                         (continuation-use
-                          (basic-combination-fun node))))
-                       *backend-predicate-types*)))
-    (assert ctype)
-    (ir1-transform-type-predicate object ctype)))
-
-;;; If FIND-CLASS is called on a constant class, locate the CLASS-CELL
-;;; at load time.
-(deftransform find-class ((name) ((constant-argument symbol)) *
-                         :when :both)
-  (let* ((name (continuation-value name))
-        (cell (find-class-cell name)))
-    `(or (class-cell-class ',cell)
-        (error "class not yet defined: ~S" name))))
+  (let ((ctype (gethash (leaf-source-name
+                         (ref-leaf
+                          (lvar-uses
+                           (basic-combination-fun node))))
+                        *backend-predicate-types*)))
+    (aver ctype)
+    (ir1-transform-type-predicate object ctype node)))
+
+;;; If FIND-CLASSOID is called on a constant class, locate the
+;;; CLASSOID-CELL at load time.
+(deftransform find-classoid ((name) ((constant-arg symbol)) *)
+  (let* ((name (lvar-value name))
+         (cell (find-classoid-cell name :create t)))
+    `(or (classoid-cell-classoid ',cell)
+         (error "class not yet defined: ~S" name))))
+\f
+(defoptimizer (%typep-wrapper constraint-propagate-if)
+    ((test-value variable type) node gen)
+  (aver (constant-lvar-p type))
+  (let ((type (lvar-value type)))
+    (values variable (if (ctype-p type)
+                         type
+                         (handler-case (careful-specifier-type type)
+                           (t () nil))))))
+
+(deftransform %typep-wrapper ((test-value variable type) * * :node node)
+  (aver (constant-lvar-p type))
+  (if (constant-lvar-p test-value)
+      `',(lvar-value test-value)
+      (let* ((type (lvar-value type))
+             (type (if (ctype-p type)
+                       type
+                       (handler-case (careful-specifier-type type)
+                         (t () nil))))
+             (value-type (lvar-type variable)))
+        (cond ((not type)
+               'test-value)
+              ((csubtypep value-type type)
+               t)
+              ((not (types-equal-or-intersect value-type type))
+               nil)
+              (t
+               (delay-ir1-transform node :constraint)
+               'test-value)))))
 \f
-;;;; standard type predicates
+;;;; standard type predicates, i.e. those defined in package COMMON-LISP,
+;;;; plus at least one oddball (%INSTANCEP)
+;;;;
+;;;; Various other type predicates (e.g. low-level representation
+;;;; stuff like SIMPLE-ARRAY-SINGLE-FLOAT-P) are defined elsewhere.
 
-;;; FIXME: needed only at cold load time, can be uninterned afterwards;
-;;; or perhaps could just be done at toplevel
-(defun define-standard-type-predicates ()
+;;; FIXME: This function is only called once, at top level. Why not
+;;; just expand all its operations into toplevel code?
+(defun !define-standard-type-predicates ()
   (define-type-predicate arrayp array)
   ; (The ATOM predicate is handled separately as (NOT CONS).)
   (define-type-predicate bit-vector-p bit-vector)
   (define-type-predicate numberp number)
   (define-type-predicate rationalp rational)
   (define-type-predicate realp real)
+  (define-type-predicate sequencep sequence)
+  (define-type-predicate extended-sequence-p extended-sequence)
   (define-type-predicate simple-bit-vector-p simple-bit-vector)
   (define-type-predicate simple-string-p simple-string)
   (define-type-predicate simple-vector-p simple-vector)
   (define-type-predicate funcallable-instance-p funcallable-instance)
   (define-type-predicate symbolp symbol)
   (define-type-predicate vectorp vector))
-
-(define-standard-type-predicates)
+(!define-standard-type-predicates)
 \f
 ;;;; transforms for type predicates not implemented primitively
 ;;;;
 ;;;; See also VM dependent transforms.
 
-(def-source-transform atom (x)
+(define-source-transform atom (x)
   `(not (consp ,x)))
+#!+sb-unicode
+(define-source-transform base-char-p (x)
+  `(typep ,x 'base-char))
 \f
 ;;;; TYPEP source transform
 
-;;; Return a form that tests the variable N-Object for being in the binds
-;;; specified by Type. Base is the name of the base type, for declaration. We
-;;; make safety locally 0 to inhibit any checking of this assertion.
-#!-negative-zero-is-not-zero
-(defun transform-numeric-bound-test (n-object type base)
-  (declare (type numeric-type type))
-  (let ((low (numeric-type-low type))
-       (high (numeric-type-high type)))
-    `(locally
-       (declare (optimize (safety 0)))
-       (and ,@(when low
-               (if (consp low)
-                   `((> (the ,base ,n-object) ,(car low)))
-                   `((>= (the ,base ,n-object) ,low))))
-           ,@(when high
-               (if (consp high)
-                   `((< (the ,base ,n-object) ,(car high)))
-                   `((<= (the ,base ,n-object) ,high))))))))
-
-#!+negative-zero-is-not-zero
+;;; Return a form that tests the variable N-OBJECT for being in the
+;;; binds specified by TYPE. BASE is the name of the base type, for
+;;; declaration. We make SAFETY locally 0 to inhibit any checking of
+;;; this assertion.
 (defun transform-numeric-bound-test (n-object type base)
   (declare (type numeric-type type))
   (let ((low (numeric-type-low type))
-       (high (numeric-type-high type))
-       (float-type-p (csubtypep type (specifier-type 'float)))
-       (x (gensym))
-       (y (gensym)))
+        (high (numeric-type-high type)))
     `(locally
        (declare (optimize (safety 0)))
        (and ,@(when low
-               (if (consp low)
-                   `((let ((,x (the ,base ,n-object))
-                           (,y ,(car low)))
-                       ,(if (not float-type-p)
-                           `(> ,x ,y)
-                           `(if (and (zerop ,x) (zerop ,y))
-                                (> (float-sign ,x) (float-sign ,y))
-                                (> ,x ,y)))))
-                   `((let ((,x (the ,base ,n-object))
-                           (,y ,low))
-                       ,(if (not float-type-p)
-                           `(>= ,x ,y)
-                           `(if (and (zerop ,x) (zerop ,y))
-                                (>= (float-sign ,x) (float-sign ,y))
-                                (>= ,x ,y)))))))
-           ,@(when high
-               (if (consp high)
-                   `((let ((,x (the ,base ,n-object))
-                           (,y ,(car high)))
-                       ,(if (not float-type-p)
-                            `(< ,x ,y)
-                            `(if (and (zerop ,x) (zerop ,y))
-                                 (< (float-sign ,x) (float-sign ,y))
-                                 (< ,x ,y)))))
-                   `((let ((,x (the ,base ,n-object))
-                           (,y ,high))
-                       ,(if (not float-type-p)
-                            `(<= ,x ,y)
-                            `(if (and (zerop ,x) (zerop ,y))
-                                 (<= (float-sign ,x) (float-sign ,y))
-                                 (<= ,x ,y)))))))))))
+                (if (consp low)
+                    `((> (truly-the ,base ,n-object) ,(car low)))
+                    `((>= (truly-the ,base ,n-object) ,low))))
+            ,@(when high
+                (if (consp high)
+                    `((< (truly-the ,base ,n-object) ,(car high)))
+                    `((<= (truly-the ,base ,n-object) ,high))))))))
 
 ;;; Do source transformation of a test of a known numeric type. We can
 ;;; assume that the type doesn't have a corresponding predicate, since
 ;;; realpart and the imagpart must be the same.
 (defun source-transform-numeric-typep (object type)
   (let* ((class (numeric-type-class type))
-        (base (ecase class
-                (integer (containing-integer-type type))
-                (rational 'rational)
-                (float (or (numeric-type-format type) 'float))
-                ((nil) 'real))))
+         (base (ecase class
+                 (integer (containing-integer-type
+                           (if (numeric-type-complexp type)
+                               (modified-numeric-type type
+                                                      :complexp :real)
+                               type)))
+                 (rational 'rational)
+                 (float (or (numeric-type-format type) 'float))
+                 ((nil) 'real))))
     (once-only ((n-object object))
       (ecase (numeric-type-complexp type)
-       (:real
-        `(and (typep ,n-object ',base)
-              ,(transform-numeric-bound-test n-object type base)))
-       (:complex
-        `(and (complexp ,n-object)
-              ,(once-only ((n-real `(realpart (the complex ,n-object)))
-                           (n-imag `(imagpart (the complex ,n-object))))
-                 `(progn
-                    ,n-imag ; ignorable
-                    (and (typep ,n-real ',base)
-                         ,@(when (eq class 'integer)
-                             `((typep ,n-imag ',base)))
-                         ,(transform-numeric-bound-test n-real type base)
-                         ,(transform-numeric-bound-test n-imag type
-                                                        base))))))))))
+        (:real
+         (if (and #!-(or x86 x86-64) ;; Not implemented elsewhere yet
+                  nil
+                  (eql (numeric-type-class type) 'integer)
+                  (eql (numeric-type-low type) 0)
+                  (fixnump (numeric-type-high type)))
+             `(fixnum-mod-p ,n-object ,(numeric-type-high type))
+             `(and (typep ,n-object ',base)
+                   ,(transform-numeric-bound-test n-object type base))))
+        (:complex
+         `(and (complexp ,n-object)
+               ,(once-only ((n-real `(realpart (truly-the complex ,n-object)))
+                            (n-imag `(imagpart (truly-the complex ,n-object))))
+                  `(progn
+                     ,n-imag ; ignorable
+                     (and (typep ,n-real ',base)
+                          ,@(when (eq class 'integer)
+                              `((typep ,n-imag ',base)))
+                          ,(transform-numeric-bound-test n-real type base)
+                          ,(transform-numeric-bound-test n-imag type
+                                                         base))))))))))
 
 ;;; Do the source transformation for a test of a hairy type. AND,
 ;;; SATISFIES and NOT are converted into the obvious code. We convert
   (declare (type hairy-type type))
   (let ((spec (hairy-type-specifier type)))
     (cond ((unknown-type-p type)
-          (when (policy nil (> speed brevity))
-            (compiler-note "can't open-code test of unknown type ~S"
-                           (type-specifier type)))
-          `(%typep ,object ',spec))
-         (t
-          (ecase (first spec)
-            (satisfies `(if (funcall #',(second spec) ,object) t nil))
-            ((not and)
-             (once-only ((n-obj object))
-               `(,(first spec) ,@(mapcar #'(lambda (x)
-                                             `(typep ,n-obj ',x))
-                                         (rest spec))))))))))
-
-;;; Do source transformation for Typep of a known union type. If a
+           (when (policy *lexenv* (> speed inhibit-warnings))
+             (compiler-notify "can't open-code test of unknown type ~S"
+                              (type-specifier type)))
+           `(%typep ,object ',spec))
+          (t
+           (ecase (first spec)
+             (satisfies
+              `(if (funcall (global-function ,(second spec)) ,object) t nil))
+             ((not and)
+              (once-only ((n-obj object))
+                `(,(first spec) ,@(mapcar (lambda (x)
+                                            `(typep ,n-obj ',x))
+                                          (rest spec))))))))))
+
+(defun source-transform-negation-typep (object type)
+  (declare (type negation-type type))
+  (let ((spec (type-specifier (negation-type-type type))))
+    `(not (typep ,object ',spec))))
+
+;;; Do source transformation for TYPEP of a known union type. If a
 ;;; union type contains LIST, then we pull that out and make it into a
-;;; single LISTP call. Note that if SYMBOL is in the union, then LIST
-;;; will be a subtype even without there being any (member NIL). We
-;;; just drop through to the general code in this case, rather than
-;;; trying to optimize it.
+;;; single LISTP call.  Note that if SYMBOL is in the union, then LIST
+;;; will be a subtype even without there being any (member NIL).  We
+;;; currently just drop through to the general code in this case,
+;;; rather than trying to optimize it (but FIXME CSR 2004-04-05: it
+;;; wouldn't be hard to optimize it after all).
 (defun source-transform-union-typep (object type)
   (let* ((types (union-type-types type))
-        (ltype (specifier-type 'list))
-        (mtype (find-if #'member-type-p types)))
-    (cond ((and mtype (csubtypep ltype type))
-          (let ((members (member-type-members mtype)))
-            (once-only ((n-obj object))
-              `(if (listp ,n-obj)
-                   t
-                   (typep ,n-obj
-                          '(or ,@(mapcar #'type-specifier
-                                         (remove (specifier-type 'cons)
-                                                 (remove mtype types)))
-                               (member ,@(remove nil members))))))))
-         (t
-          (once-only ((n-obj object))
-            `(or ,@(mapcar #'(lambda (x)
-                               `(typep ,n-obj ',(type-specifier x)))
-                           types)))))))
-
-;;; MNA: cons compound-type patch
-;;; FIXIT: all commented out
-; ;;; Source-Transform-Cons-Typep
-; ;;;
-; ;;; If necessary recurse to check the cons type.
-; ;;;
-; (defun source-transform-cons-typep (object type)
-;   (let* ((car-type (cons-type-car-type type))
-;       (cdr-type (cons-type-cdr-type type)))
-;     (let ((car-test-p (not (or (type= car-type *wild-type*)
-;                             (type= car-type (specifier-type t)))))
-;        (cdr-test-p (not (or (type= cdr-type *wild-type*)
-;                             (type= cdr-type (specifier-type t))))))
-;       (if (and (not car-test-p) (not cdr-test-p))
-;         `(consp ,object)
-;         (once-only ((n-obj object))
-;                    `(and (consp ,n-obj)
-;                      ,@(if car-test-p
-;                            `((typep (car ,n-obj)
-;                               ',(type-specifier car-type))))
-;                      ,@(if cdr-test-p
-;                            `((typep (cdr ,n-obj)
-;                               ',(type-specifier cdr-type))))))))))
+         (type-cons (specifier-type 'cons))
+         (mtype (find-if #'member-type-p types))
+         (members (when mtype (member-type-members mtype))))
+    (if (and mtype
+             (memq nil members)
+             (memq type-cons types))
+        (once-only ((n-obj object))
+          `(or (listp ,n-obj)
+               (typep ,n-obj
+                      '(or ,@(mapcar #'type-specifier
+                                     (remove type-cons
+                                             (remove mtype types)))
+                        (member ,@(remove nil members))))))
+        (once-only ((n-obj object))
+          `(or ,@(mapcar (lambda (x)
+                           `(typep ,n-obj ',(type-specifier x)))
+                         types))))))
+
+;;; Do source transformation for TYPEP of a known intersection type.
+(defun source-transform-intersection-typep (object type)
+  (once-only ((n-obj object))
+    `(and ,@(mapcar (lambda (x)
+                      `(typep ,n-obj ',(type-specifier x)))
+                    (intersection-type-types type)))))
+
+;;; If necessary recurse to check the cons type.
+(defun source-transform-cons-typep (object type)
+  (let* ((car-type (cons-type-car-type type))
+         (cdr-type (cons-type-cdr-type type)))
+    (let ((car-test-p (not (type= car-type *universal-type*)))
+          (cdr-test-p (not (type= cdr-type *universal-type*))))
+      (if (and (not car-test-p) (not cdr-test-p))
+          `(consp ,object)
+          (once-only ((n-obj object))
+            `(and (consp ,n-obj)
+                  ,@(if car-test-p
+                        `((typep (car ,n-obj)
+                                 ',(type-specifier car-type))))
+                  ,@(if cdr-test-p
+                        `((typep (cdr ,n-obj)
+                                 ',(type-specifier cdr-type))))))))))
+
+(defun source-transform-character-set-typep (object type)
+  (let ((pairs (character-set-type-pairs type)))
+    (if (and (= (length pairs) 1)
+            (= (caar pairs) 0)
+            (= (cdar pairs) (1- sb!xc:char-code-limit)))
+       `(characterp ,object)
+       (once-only ((n-obj object))
+         (let ((n-code (gensym "CODE")))
+           `(and (characterp ,n-obj)
+                 (let ((,n-code (sb!xc:char-code ,n-obj)))
+                   (or
+                    ,@(loop for pair in pairs
+                            collect
+                            `(<= ,(car pair) ,n-code ,(cdr pair)))))))))))
+
+#!+sb-simd-pack
+(defun source-transform-simd-pack-typep (object type)
+  (if (type= type (specifier-type 'simd-pack))
+      `(simd-pack-p ,object)
+      (once-only ((n-obj object))
+        (let ((n-tag (gensym "TAG")))
+          `(and
+            (simd-pack-p ,n-obj)
+            (let ((,n-tag (%simd-pack-tag ,n-obj)))
+              (or ,@(loop
+                      for type in (simd-pack-type-element-type type)
+                      for index = (position type *simd-pack-element-types*)
+                      collect `(eql ,n-tag ,index)))))))))
 
 ;;; Return the predicate and type from the most specific entry in
 ;;; *TYPE-PREDICATES* that is a supertype of TYPE.
 (defun find-supertype-predicate (type)
   (declare (type ctype type))
   (let ((res nil)
-       (res-type nil))
+        (res-type nil))
     (dolist (x *backend-type-predicates*)
       (let ((stype (car x)))
-       (when (and (csubtypep type stype)
-                  (or (not res-type)
-                      (csubtypep stype res-type)))
-         (setq res-type stype)
-         (setq res (cdr x)))))
+        (when (and (csubtypep type stype)
+                   (or (not res-type)
+                       (csubtypep stype res-type)))
+          (setq res-type stype)
+          (setq res (cdr x)))))
     (values res res-type)))
 
 ;;; Return forms to test that OBJ has the rank and dimensions
 ;;; specified by TYPE, where STYPE is the type we have checked against
-;;; (which is the same but for dimensions.)
+;;; (which is the same but for dimensions and element type).
+;;;
+;;; Secondary return value is true if passing the generated tests implies that
+;;; the array has a header.
 (defun test-array-dimensions (obj type stype)
   (declare (type array-type type stype))
   (let ((obj `(truly-the ,(type-specifier stype) ,obj))
-       (dims (array-type-dimensions type)))
-    (unless (eq dims '*)
-      (collect ((res))
-       (when (eq (array-type-dimensions stype) '*)
-         (res `(= (array-rank ,obj) ,(length dims))))
-       (do ((i 0 (1+ i))
-            (dim dims (cdr dim)))
-           ((null dim))
-         (let ((dim (car dim)))
-           (unless (eq dim '*)
-             (res `(= (array-dimension ,obj ,i) ,dim)))))
-       (res)))))
-
-;;; If we can find a type predicate that tests for the type w/o
+        (dims (array-type-dimensions type)))
+    (unless (or (eq dims '*)
+                (equal dims (array-type-dimensions stype)))
+      (cond ((cdr dims)
+             (values `((array-header-p ,obj)
+                       ,@(when (eq (array-type-dimensions stype) '*)
+                               `((= (%array-rank ,obj) ,(length dims))))
+                       ,@(loop for d in dims
+                               for i from 0
+                               unless (eq '* d)
+                               collect `(= (%array-dimension ,obj ,i) ,d)))
+                     t))
+            ((not dims)
+             (values `((array-header-p ,obj)
+                       (= (%array-rank ,obj) 0))
+                     t))
+            ((not (array-type-complexp type))
+             (if (csubtypep stype (specifier-type 'vector))
+                 (values (unless (eq '* (car dims))
+                           `((= (vector-length ,obj) ,@dims)))
+                         nil)
+                 (values (if (eq '* (car dims))
+                             `((not (array-header-p ,obj)))
+                             `((not (array-header-p ,obj))
+                               (= (vector-length ,obj) ,@dims)))
+                         nil)))
+            (t
+             (values (unless (eq '* (car dims))
+                       `((if (array-header-p ,obj)
+                             (= (%array-dimension ,obj 0) ,@dims)
+                             (= (vector-length ,obj) ,@dims))))
+                     nil))))))
+
+;;; Return forms to test that OBJ has the element-type specified by type
+;;; specified by TYPE, where STYPE is the type we have checked against (which
+;;; is the same but for dimensions and element type). If HEADERP is true, OBJ
+;;; is guaranteed to be an array-header.
+(defun test-array-element-type (obj type stype headerp)
+  (declare (type array-type type stype))
+  (let ((obj `(truly-the ,(type-specifier stype) ,obj))
+        (eltype (array-type-specialized-element-type type)))
+    (unless (or (type= eltype (array-type-specialized-element-type stype))
+                (eq eltype *wild-type*))
+      (let ((typecode (sb!vm:saetp-typecode (find-saetp-by-ctype eltype))))
+        (with-unique-names (data)
+         (if (and headerp (not (array-type-complexp stype)))
+             ;; If we know OBJ is an array header, and that the array is
+             ;; simple, we also know there is exactly one indirection to
+             ;; follow.
+             `((eq (%other-pointer-widetag (%array-data-vector ,obj)) ,typecode))
+             `((do ((,data ,(if headerp `(%array-data-vector ,obj) obj)
+                           (%array-data-vector ,data)))
+                   ((not (array-header-p ,data))
+                    (eq (%other-pointer-widetag ,data) ,typecode))))))))))
+
+;;; If we can find a type predicate that tests for the type without
 ;;; dimensions, then use that predicate and test for dimensions.
 ;;; Otherwise, just do %TYPEP.
 (defun source-transform-array-typep (obj type)
   (multiple-value-bind (pred stype) (find-supertype-predicate type)
     (if (and (array-type-p stype)
-            ;; (If the element type hasn't been defined yet, it's
-            ;; not safe to assume here that it will eventually
-            ;; have (UPGRADED-ARRAY-ELEMENT-TYPE type)=T, so punt.)
-            (not (unknown-type-p (array-type-element-type type)))
-            (type= (array-type-specialized-element-type stype)
-                   (array-type-specialized-element-type type))
-            (eq (array-type-complexp stype) (array-type-complexp type)))
-       (once-only ((n-obj obj))
-         `(and (,pred ,n-obj)
-               ,@(test-array-dimensions n-obj type stype)))
-       `(%typep ,obj ',(type-specifier type)))))
+             ;; (If the element type hasn't been defined yet, it's
+             ;; not safe to assume here that it will eventually
+             ;; have (UPGRADED-ARRAY-ELEMENT-TYPE type)=T, so punt.)
+             (not (unknown-type-p (array-type-element-type type)))
+             (or (eq (array-type-complexp stype) (array-type-complexp type))
+                 (and (eql (array-type-complexp stype) :maybe)
+                      (eql (array-type-complexp type) t))))
+        (once-only ((n-obj obj))
+          (multiple-value-bind (tests headerp)
+              (test-array-dimensions n-obj type stype)
+            `(and (,pred ,n-obj)
+                  ,@(when (and (eql (array-type-complexp stype) :maybe)
+                               (eql (array-type-complexp type) t))
+                      ;; KLUDGE: this is a bit lame; if we get here,
+                      ;; we already know that N-OBJ is an array, but
+                      ;; (NOT SIMPLE-ARRAY) doesn't know that.  On the
+                      ;; other hand, this should get compiled down to
+                      ;; two widetag tests, so it's only a bit lame.
+                      `((typep ,n-obj '(not simple-array))))
+                  ,@tests
+                  ,@(test-array-element-type n-obj type stype headerp))))
+        `(%typep ,obj ',(type-specifier type)))))
 
 ;;; Transform a type test against some instance type. The type test is
 ;;; flushed if the result is known at compile time. If not properly
 ;;; then we also check whether the layout for the object is invalid
 ;;; and signal an error if so. Otherwise, look up the indirect
 ;;; class-cell and call CLASS-CELL-TYPEP at runtime.
-;;;
-;;; KLUDGE: The :WHEN :BOTH option here is probably a suboptimal
-;;; solution to the problem of %INSTANCE-TYPEP forms in byte compiled
-;;; code; it'd probably be better just to have %INSTANCE-TYPEP forms
-;;; never be generated in byte compiled code, or maybe to have a DEFUN
-;;; %INSTANCE-TYPEP somewhere to handle them if they are. But it's not
-;;; terribly important because mostly, %INSTANCE-TYPEP forms *aren't*
-;;; generated in byte compiled code. (As of sbcl-0.6.5, they could
-;;; sometimes be generated when byte compiling inline functions, but
-;;; it's quite uncommon.) -- WHN 20000523
-(deftransform %instance-typep ((object spec) * * :when :both)
-  (assert (constant-continuation-p spec))
-  (let* ((spec (continuation-value spec))
-        (class (specifier-type spec))
-        (name (sb!xc:class-name class))
-        (otype (continuation-type object))
-        (layout (let ((res (info :type :compiler-layout name)))
-                  (if (and res (not (layout-invalid res)))
-                      res
-                      nil))))
-    (/noshow "entering DEFTRANSFORM %INSTANCE-TYPEP" otype spec class name layout)
+(deftransform %instance-typep ((object spec) (* *) * :node node)
+  (aver (constant-lvar-p spec))
+  (let* ((spec (lvar-value spec))
+         (class (specifier-type spec))
+         (name (classoid-name class))
+         (otype (lvar-type object))
+         (layout (let ((res (info :type :compiler-layout name)))
+                   (if (and res (not (layout-invalid res)))
+                       res
+                       nil))))
     (cond
       ;; Flush tests whose result is known at compile time.
-      ((not (types-intersect otype class))
-       (/noshow "flushing constant NIL")
+      ((not (types-equal-or-intersect otype class))
        nil)
       ((csubtypep otype class)
-       (/noshow "flushing constant T")
        t)
       ;; If not properly named, error.
-      ((not (and name (eq (sb!xc:find-class name) class)))
+      ((not (and name (eq (find-classoid name) class)))
        (compiler-error "can't compile TYPEP of anonymous or undefined ~
-                       class:~%  ~S"
-                      class))
+                        class:~%  ~S"
+                       class))
       (t
+       ;; Delay the type transform to give type propagation a chance.
+       (delay-ir1-transform node :constraint)
+
        ;; Otherwise transform the type test.
        (multiple-value-bind (pred get-layout)
-          (cond
-            ((csubtypep class (specifier-type 'funcallable-instance))
-             (values 'funcallable-instance-p '%funcallable-instance-layout))
-            ((csubtypep class (specifier-type 'instance))
-             (values '%instancep '%instance-layout))
-            (t
-             (values '(lambda (x) (declare (ignore x)) t) 'layout-of)))
-        (/noshow pred get-layout)
-        (cond
-          ((and (eq (class-state class) :sealed) layout
-                (not (class-subclasses class)))
-           ;; Sealed and has no subclasses.
-           (/noshow "sealed and has no subclasses")
-           (let ((n-layout (gensym)))
-             `(and (,pred object)
-                   (let ((,n-layout (,get-layout object)))
-                     ,@(when (policy nil (>= safety speed))
-                             `((when (layout-invalid ,n-layout)
-                                 (%layout-invalid-error object ',layout))))
-                     (eq ,n-layout ',layout)))))
-          ((and (typep class 'basic-structure-class) layout)
-           (/noshow "structure type tests; hierarchical layout depths")
-           ;; structure type tests; hierarchical layout depths
-           (let ((depthoid (layout-depthoid layout))
-                 (n-layout (gensym)))
-             `(and (,pred object)
-                   (let ((,n-layout (,get-layout object)))
-                     ,@(when (policy nil (>= safety speed))
-                             `((when (layout-invalid ,n-layout)
-                                 (%layout-invalid-error object ',layout))))
-                     (if (eq ,n-layout ',layout)
-                         t
-                         (and (> (layout-depthoid ,n-layout)
-                                 ,depthoid)
-                              (locally (declare (optimize (safety 0)))
-                                (eq (svref (layout-inherits ,n-layout)
-                                           ,depthoid)
-                                    ',layout))))))))
-          (t
-           (/noshow "default case -- ,PRED and CLASS-CELL-TYPEP")
-           `(and (,pred object)
-                 (class-cell-typep (,get-layout object)
-                                   ',(find-class-cell name)
-                                   object)))))))))
-
-#|
-;;; Return (VALUES BEST-GUESS EXACT?), where BEST-GUESS is a CTYPE
-;;; which corresponds to the value returned by
-;;; CL:UPGRADED-ARRAY-ELEMENT-TYPE, and EXACT? tells whether that
-;;; result might change when we encounter a DEFTYPE.
-(declaim (maybe-inline upgraded-array-element-ctype-2))
-(defun upgraded-array-element-ctype-2 (spec)
-  (let ((ctype (specifier-type `(array ,spec))))
-    (values (array-type-specialized-element-type
-            (specifier-type `(array ,spec)))
-           (not (unknown-type-p (array-type-element-type ctype))))))
-|#
+           (cond
+             ((csubtypep class (specifier-type 'funcallable-instance))
+              (values 'funcallable-instance-p '%funcallable-instance-layout))
+             ((csubtypep class (specifier-type 'instance))
+              (values '%instancep '%instance-layout))
+             (t
+              (values '(lambda (x) (declare (ignore x)) t) 'layout-of)))
+         (cond
+           ((and (eq (classoid-state class) :sealed) layout
+                 (not (classoid-subclasses class)))
+            ;; Sealed and has no subclasses.
+            `(and (,pred object)
+                  (eq (,get-layout object) ',layout)))
+           ((and (typep class 'structure-classoid) layout)
+            ;; structure type tests; hierarchical layout depths
+            (let ((depthoid (layout-depthoid layout))
+                  (n-layout (gensym)))
+              `(and (,pred object)
+                    (let ((,n-layout (,get-layout object)))
+                      ;; we used to check for invalid layouts here,
+                      ;; but in fact that's both unnecessary and
+                      ;; wrong; it's unnecessary because structure
+                      ;; classes can't be redefined, and it's wrong
+                      ;; because it is quite legitimate to pass an
+                      ;; object with an invalid layout to a structure
+                      ;; type test.
+                      (if (eq ,n-layout ',layout)
+                          t
+                          (and (> (layout-depthoid ,n-layout)
+                                  ,depthoid)
+                               (locally (declare (optimize (safety 0)))
+                                 ;; Use DATA-VECTOR-REF directly,
+                                 ;; since that's what SVREF in a
+                                 ;; SAFETY 0 lexenv will eventually be
+                                 ;; transformed to. This can give a
+                                 ;; large compilation speedup, since
+                                 ;; %INSTANCE-TYPEPs are frequently
+                                 ;; created during GENERATE-TYPE-CHECKS,
+                                 ;; and the normal aref transformation path
+                                 ;; is pretty heavy.
+                                 (eq (data-vector-ref (layout-inherits ,n-layout)
+                                                      ,depthoid)
+                                     ',layout))))))))
+           ((and layout (>= (layout-depthoid layout) 0))
+            ;; hierarchical layout depths for other things (e.g.
+            ;; CONDITION, STREAM)
+            (let ((depthoid (layout-depthoid layout))
+                  (n-layout (gensym))
+                  (n-inherits (gensym)))
+              `(and (,pred object)
+                    (let ((,n-layout (,get-layout object)))
+                      (when (layout-invalid ,n-layout)
+                        (setq ,n-layout (update-object-layout-or-invalid
+                                         object ',layout)))
+                      (if (eq ,n-layout ',layout)
+                          t
+                          (let ((,n-inherits (layout-inherits ,n-layout)))
+                            (declare (optimize (safety 0)))
+                            (and (> (length ,n-inherits) ,depthoid)
+                                 ;; See above.
+                                 (eq (data-vector-ref ,n-inherits ,depthoid)
+                                     ',layout))))))))
+           (t
+            (/noshow "default case -- ,PRED and CLASS-CELL-TYPEP")
+            `(and (,pred object)
+                  (classoid-cell-typep (,get-layout object)
+                                       ',(find-classoid-cell name :create t)
+                                       object)))))))))
 
 ;;; If the specifier argument is a quoted constant, then we consider
 ;;; converting into a simple predicate or other stuff. If the type is
 ;;; If the type is TYPE= to a type that has a predicate, then expand
 ;;; to that predicate. Otherwise, we dispatch off of the type's type.
 ;;; These transformations can increase space, but it is hard to tell
-;;; when, so we ignore policy and always do them. When byte-compiling,
-;;; we only do transforms that have potential for control
-;;; simplification. Instance type tests are converted to
-;;; %INSTANCE-TYPEP to allow type propagation.
-(def-source-transform typep (object spec)
-  (if (and (consp spec) (eq (car spec) 'quote))
-      (let ((type (specifier-type (cadr spec))))
-       (or (let ((pred (cdr (assoc type *backend-type-predicates*
-                                   :test #'type=))))
-             (when pred `(,pred ,object)))
-           (typecase type
-             (hairy-type
-              (source-transform-hairy-typep object type))
-             (union-type
-              (source-transform-union-typep object type))
-             (member-type
-              `(member ,object ',(member-type-members type)))
-             (args-type
-              (compiler-warning "illegal type specifier for TYPEP: ~S"
-                                (cadr spec))
-              `(%typep ,object ,spec))
-             (t nil))
-           (and (not (byte-compiling))
-                (typecase type
-                  (numeric-type
-                   (source-transform-numeric-typep object type))
-                  (sb!xc:class
-                   `(%instance-typep ,object ,spec))
-                  (array-type
-                   (source-transform-array-typep object type))
-                   ;; MNA: cons compound-type patch
-                   ;; FIXIT: all commented
-;                    (cons-type
-;                     (source-transform-cons-typep object type))                   
-                  (t nil)))
-           `(%typep ,object ,spec)))
+;;; when, so we ignore policy and always do them.
+(defun %source-transform-typep (object type)
+  (let ((ctype (careful-specifier-type type)))
+    (or (when (not ctype)
+          (compiler-warn "illegal type specifier for TYPEP: ~S" type)
+          (return-from %source-transform-typep (values nil t)))
+        (multiple-value-bind (constantp value) (type-singleton-p ctype)
+          (and constantp
+               `(eql ,object ',value)))
+        (let ((pred (cdr (assoc ctype *backend-type-predicates*
+                                :test #'type=))))
+          (when pred `(,pred ,object)))
+        (typecase ctype
+          (hairy-type
+           (source-transform-hairy-typep object ctype))
+          (negation-type
+           (source-transform-negation-typep object ctype))
+          (union-type
+           (source-transform-union-typep object ctype))
+          (intersection-type
+           (source-transform-intersection-typep object ctype))
+          (member-type
+           `(if (member ,object ',(member-type-members ctype)) t))
+          (args-type
+           (compiler-warn "illegal type specifier for TYPEP: ~S" type)
+           (return-from %source-transform-typep (values nil t)))
+          (t nil))
+        (typecase ctype
+          (numeric-type
+           (source-transform-numeric-typep object ctype))
+          (classoid
+           `(%instance-typep ,object ',type))
+          (array-type
+           (source-transform-array-typep object ctype))
+          (cons-type
+           (source-transform-cons-typep object ctype))
+          (character-set-type
+           (source-transform-character-set-typep object ctype))
+          #!+sb-simd-pack
+          (simd-pack-type
+           (source-transform-simd-pack-typep object ctype))
+          (t nil))
+        `(%typep ,object ',type))))
+
+(defun source-transform-typep (object type)
+  (let ((name (gensym "OBJECT")))
+    (multiple-value-bind (transform error)
+        (%source-transform-typep name type)
+      (if error
+          (values nil t)
+          (values `(let ((,name ,object))
+                     (%typep-wrapper ,transform ,name ',type)))))))
+
+(define-source-transform typep (object spec &optional env)
+  ;; KLUDGE: It looks bad to only do this on explicitly quoted forms,
+  ;; since that would overlook other kinds of constants. But it turns
+  ;; out that the DEFTRANSFORM for TYPEP detects any constant
+  ;; lvar, transforms it into a quoted form, and gives this
+  ;; source transform another chance, so it all works out OK, in a
+  ;; weird roundabout way. -- WHN 2001-03-18
+  (if (and (not env)
+           (consp spec)
+           (eq (car spec) 'quote)
+           (or (not *allow-instrumenting*)
+               (policy *lexenv* (= store-coverage-data 0))))
+      (source-transform-typep object (cadr spec))
       (values nil t)))
 \f
 ;;;; coercion
 
-;;; old working version
-(deftransform coerce ((x type) (* *) * :when :both)
-  (unless (constant-continuation-p type)
-    (give-up-ir1-transform))
-  (let ((tspec (specifier-type (continuation-value type))))
-    (if (csubtypep (continuation-type x) tspec)
-       'x
-       `(the ,(continuation-value type)
-             ,(cond ((csubtypep tspec (specifier-type 'double-float))
-                     '(%double-float x))       
-                    ;; FIXME: If LONG-FLOAT is to be supported, we
-                    ;; need to pick it off here before falling through
-                    ;; to %SINGLE-FLOAT.
-                    ((csubtypep tspec (specifier-type 'float))
-                     '(%single-float x))
-                    (t
-                     (give-up-ir1-transform)))))))
-
-;;; KLUDGE: new broken version -- 20000504
-#+nil
-(deftransform coerce ((x type) (* *) * :when :both)
-  (unless (constant-continuation-p type)
+;;; Constant-folding.
+;;;
+#-sb-xc-host
+(defoptimizer (coerce optimizer) ((x type) node)
+  (when (and (constant-lvar-p x) (constant-lvar-p type))
+    (let ((value (lvar-value x)))
+      (when (or (numberp value) (characterp value))
+        (constant-fold-call node)
+        t))))
+
+;;; Drops dimension information from vector types.
+(defun simplify-vector-type (type)
+  (aver (csubtypep type (specifier-type '(array * (*)))))
+  (let* ((array-type
+          (if (csubtypep type (specifier-type 'simple-array))
+              'simple-array
+              'array))
+         (complexp
+          (not
+           (or (eq 'simple-array array-type)
+               (neq *empty-type*
+                    (type-intersection type (specifier-type 'simple-array)))))))
+    (dolist (etype
+              #+sb-xc-host '(t bit character)
+              #-sb-xc-host sb!kernel::*specialized-array-element-types*
+              #+sb-xc-host (values nil nil nil)
+              #-sb-xc-host (values `(,array-type * (*)) t complexp))
+      (when etype
+        (let ((simplified (specifier-type `(,array-type ,etype (*)))))
+          (when (csubtypep type simplified)
+            (return (values (type-specifier simplified)
+                            etype
+                            complexp))))))))
+
+(deftransform coerce ((x type) (* *) * :node node)
+  (unless (constant-lvar-p type)
     (give-up-ir1-transform))
-  (let ((tspec (specifier-type (continuation-value type))))
-    (if (csubtypep (continuation-type x) tspec)
-       'x
-       `(if #+nil (typep x type) #-nil nil
-            x
-            (the ,(continuation-value type)
-                 ,(cond ((csubtypep tspec (specifier-type 'double-float))
-                         '(%double-float x))   
-                        ;; FIXME: If LONG-FLOAT is to be supported,
-                        ;; we need to pick it off here before falling
-                        ;; through to %SINGLE-FLOAT.
-                        ((csubtypep tspec (specifier-type 'float))
-                         '(%single-float x))
-                        #+nil
-                        ((csubtypep tspec (specifier-type 'list))
-                         '(coerce-to-list x))
-                        #+nil
-                        ((csubtypep tspec (specifier-type 'string))
-                         '(coerce-to-simple-string x))
-                        #+nil
-                        ((csubtypep tspec (specifier-type 'bit-vector))
-                         '(coerce-to-bit-vector x))
-                        #+nil
-                        ((csubtypep tspec (specifier-type 'vector))
-                         '(coerce-to-vector x type))
-                        (t
-                         (give-up-ir1-transform))))))))
+  (let* ((tval (lvar-value type))
+         (tspec (ir1-transform-specifier-type tval)))
+    (if (csubtypep (lvar-type x) tspec)
+        'x
+        ;; Note: The THE forms we use to wrap the results make sure that
+        ;; specifiers like (SINGLE-FLOAT 0.0 1.0) can raise a TYPE-ERROR.
+        (cond
+          ((csubtypep tspec (specifier-type 'double-float))
+           `(the ,tval (%double-float x)))
+          ;; FIXME: #!+long-float (t ,(error "LONG-FLOAT case needed"))
+          ((csubtypep tspec (specifier-type 'float))
+           `(the ,tval (%single-float x)))
+           ;; Special case STRING and SIMPLE-STRING as they are union types
+           ;; in SBCL.
+           ((member tval '(string simple-string))
+            `(the ,tval
+               (if (typep x ',tval)
+                   x
+                   (replace (make-array (length x) :element-type 'character) x))))
+           ;; Special case VECTOR
+           ((eq tval 'vector)
+            `(the ,tval
+               (if (vectorp x)
+                   x
+                   (replace (make-array (length x)) x))))
+           ;; Handle specialized element types for 1D arrays.
+           ((csubtypep tspec (specifier-type '(array * (*))))
+            ;; Can we avoid checking for dimension issues like (COERCE FOO
+            ;; '(SIMPLE-VECTOR 5)) returning a vector of length 6?
+            ;;
+            ;; CLHS actually allows this for all code with SAFETY < 3,
+            ;; but we're a conservative bunch.
+            (if (or (policy node (zerop safety)) ; no need in unsafe code
+                    (and (array-type-p tspec)    ; no need when no dimensions
+                         (equal (array-type-dimensions tspec) '(*))))
+                ;; We can!
+                (multiple-value-bind (vtype etype complexp) (simplify-vector-type tspec)
+                  (unless vtype
+                    (give-up-ir1-transform))
+                  `(the ,vtype
+                     (if (typep x ',vtype)
+                         x
+                         (replace
+                          (make-array (length x) :element-type ',etype
+                                      ,@(when complexp
+                                              (list :fill-pointer t
+                                                    :adjustable t)))
+                          x))))
+                ;; No, duh. Dimension checking required.
+                (give-up-ir1-transform
+                 "~@<~S specifies dimensions other than (*) in safe code.~:@>"
+                 tval)))
+           (t
+            (give-up-ir1-transform
+             "~@<open coding coercion to ~S not implemented.~:@>"
+             tval))))))