1.0.28.19: faster ARRAY-DIMENSION for non-vectors
[sbcl.git] / src / compiler / array-tran.lisp
index 14eb3de..1aeb26e 100644 (file)
          "upgraded array element type not known at compile time")
         element-type-specifier)))
 
-;;; Array access functions return an object from the array, hence its
-;;; type is going to be the array upgraded element type.
+;;; Array access functions return an object from the array, hence its type is
+;;; going to be the array upgraded element type. Secondary return value is the
+;;; known supertype of the upgraded-array-element-type, if if the exact
+;;; U-A-E-T is not known. (If it is NIL, the primary return value is as good
+;;; as it gets.)
 (defun extract-upgraded-element-type (array)
   (let ((type (lvar-type array)))
     (cond
       ;; we can have values declared e.g. (AND SIMPLE-VECTOR UNKNOWN-TYPE),
       ;; which are represented in the compiler as INTERSECTION-TYPE, not
       ;; array type.
-      ((array-type-p type) (array-type-specialized-element-type type))
-      ;; fix for bug #396.  This type logic corresponds to the special
-      ;; case for strings in HAIRY-DATA-VECTOR-REF
-      ;; (generic/vm-tran.lisp)
-      ((csubtypep type (specifier-type 'simple-string))
+      ((array-type-p type)
+       (values (array-type-specialized-element-type type) nil))
+      ;; fix for bug #396. This type logic corresponds to the special case for
+      ;; strings in HAIRY-DATA-VECTOR-REF (generic/vm-tran.lisp)
+      ((csubtypep type (specifier-type 'string))
        (cond
-         ((csubtypep type (specifier-type '(simple-array character (*))))
-          (specifier-type 'character))
+         ((csubtypep type (specifier-type '(array character (*))))
+          (values (specifier-type 'character) nil))
          #!+sb-unicode
-         ((csubtypep type (specifier-type '(simple-array base-char (*))))
-          (specifier-type 'base-char))
-         ((csubtypep type (specifier-type '(simple-array nil (*))))
-          *empty-type*)
-         ;; see KLUDGE below.
-         (t *wild-type*)))
+         ((csubtypep type (specifier-type '(array base-char (*))))
+          (values (specifier-type 'base-char) nil))
+         ((csubtypep type (specifier-type '(array nil (*))))
+          (values *empty-type* nil))
+         (t
+          ;; See KLUDGE below.
+          (values *wild-type* (specifier-type 'character)))))
       (t
        ;; KLUDGE: there is no good answer here, but at least
        ;; *wild-type* won't cause HAIRY-DATA-VECTOR-{REF,SET} to be
        ;; erroneously optimized (see generic/vm-tran.lisp) -- CSR,
        ;; 2002-08-21
-       *wild-type*))))
+       (values *wild-type* nil)))))
 
 (defun extract-declared-element-type (array)
   (let ((type (lvar-type array)))
    (specifier-type `(array * ,(make-list rank :initial-element '*)))
    (lexenv-policy (node-lexenv (lvar-dest array)))))
 
+(defun derive-aref-type (array)
+  (multiple-value-bind (uaet other) (extract-upgraded-element-type array)
+    (or other uaet)))
+
 (defoptimizer (array-in-bounds-p derive-type) ((array &rest indices))
   (assert-array-rank array (length indices))
   *universal-type*)
 
 (defoptimizer (aref derive-type) ((array &rest indices) node)
   (assert-array-rank array (length indices))
-  (extract-upgraded-element-type array))
+  (derive-aref-type array))
 
 (defoptimizer (%aset derive-type) ((array &rest stuff))
   (assert-array-rank array (1- (length stuff)))
 
 (macrolet ((define (name)
              `(defoptimizer (,name derive-type) ((array index))
-                (extract-upgraded-element-type array))))
+                (derive-aref-type array))))
   (define hairy-data-vector-ref)
   (define hairy-data-vector-ref/check-bounds)
   (define data-vector-ref))
 
 #!+(or x86 x86-64)
 (defoptimizer (data-vector-ref-with-offset derive-type) ((array index offset))
-  (extract-upgraded-element-type array))
+  (derive-aref-type array))
 
 (macrolet ((define (name)
              `(defoptimizer (,name derive-type) ((array index new-value))
   *universal-type*)
 
 (defoptimizer (row-major-aref derive-type) ((array index))
-  (extract-upgraded-element-type array))
+  (derive-aref-type array))
 
 (defoptimizer (%set-row-major-aref derive-type) ((array index new-value))
   (assert-new-value-type new-value array))
                                ,@(when element-type
                                    '(:element-type element-type))))
              (setf (%array-displaced-p header) nil)
+             (setf (%array-displaced-from header) nil)
              ,@(let ((axis -1))
                  (mapcar (lambda (dim)
                            `(setf (%array-dimension header ,(incf axis))
 (deftransform array-rank ((array))
   (let ((array-type (lvar-type array)))
     (let ((dims (array-type-dimensions-or-give-up array-type)))
-      (if (not (listp dims))
-          (give-up-ir1-transform
-           "The array rank is not known at compile time: ~S"
-           dims)
-          (length dims)))))
+      (cond ((listp dims)
+             (length dims))
+            ((eq t (array-type-complexp array-type))
+             '(%array-rank array))
+            (t
+             `(if (array-header-p array)
+                  (%array-rank array)
+                  1))))))
 
 ;;; If we know the dimensions at compile time, just use it. Otherwise,
 ;;; if we can tell that the axis is in bounds, convert to
                                (array index))
   (unless (constant-lvar-p axis)
     (give-up-ir1-transform "The axis is not constant."))
-  (let ((array-type (lvar-type array))
+  ;; Dimensions may change thanks to ADJUST-ARRAY, so we need the
+  ;; conservative type.
+  (let ((array-type (lvar-conservative-type array))
         (axis (lvar-value axis)))
     (let ((dims (array-type-dimensions-or-give-up array-type)))
       (unless (listp dims)
                  ((t)
                   '(%array-dimension array 0))
                  ((nil)
-                  '(length array))
+                  '(vector-length array))
                  ((:maybe)
-                  (give-up-ir1-transform
-                   "can't tell whether array is simple"))))
+                  `(if (array-header-p array)
+                       (%array-dimension array axis)
+                       (vector-length array)))))
               (t
                '(%array-dimension array axis)))))))
 
 ;; without bloating the code. If we already know the type of the array
 ;; with sufficient precision, skip directly to DATA-VECTOR-REF.
 (deftransform aref ((array index) (t t) * :node node)
-  (let ((type (lvar-type array)))
-    (cond ((and (array-type-p type)
-                (null (array-type-complexp type))
-                (not (eql (extract-upgraded-element-type array)
-                          *wild-type*))
-                (eql (length (array-type-dimensions type)) 1))
-           `(data-vector-ref array (%check-bound array
-                                                 (array-dimension array 0)
-                                                 index)))
-          ((policy node (zerop insert-array-bounds-checks))
-           `(hairy-data-vector-ref array index))
-          (t
-           `(hairy-data-vector-ref/check-bounds array index)))))
+  (let* ((type (lvar-type array))
+         (element-ctype (extract-upgraded-element-type array)))
+    (cond
+      ((and (array-type-p type)
+            (null (array-type-complexp type))
+            (not (eql element-ctype *wild-type*))
+            (eql (length (array-type-dimensions type)) 1))
+       (let* ((declared-element-ctype (extract-declared-element-type array))
+              (bare-form
+               `(data-vector-ref array
+                 (%check-bound array (array-dimension array 0) index))))
+         (if (type= declared-element-ctype element-ctype)
+             bare-form
+             `(the ,(type-specifier declared-element-ctype) ,bare-form))))
+      ((policy node (zerop insert-array-bounds-checks))
+       `(hairy-data-vector-ref array index))
+      (t `(hairy-data-vector-ref/check-bounds array index)))))
 
 (deftransform %aset ((array index new-value) (t t t) * :node node)
   (if (policy node (zerop insert-array-bounds-checks))
 ;;; available, switch back to the normal one to give other transforms
 ;;; a stab at it.
 (macrolet ((define (name transform-to extra extra-type)
+             (declare (ignore extra-type))
              `(deftransform ,name ((array index ,@extra))
                 (let ((type (lvar-type array))
                       (element-type (extract-upgraded-element-type array)))