1.0.28.51: better MAKE-ARRAY transforms
[sbcl.git] / src / compiler / array-tran.lisp
index 580bda6..c7481e2 100644 (file)
 \f
 ;;;; constructors
 
-;;; Convert VECTOR into a MAKE-ARRAY followed by SETFs of all the
-;;; elements.
+;;; Convert VECTOR into a MAKE-ARRAY.
 (define-source-transform vector (&rest elements)
-  (let ((len (length elements))
-        (n -1))
-    (once-only ((n-vec `(make-array ,len)))
-      `(progn
-         ,@(mapcar (lambda (el)
-                     (once-only ((n-val el))
-                       `(locally (declare (optimize (safety 0)))
-                          (setf (svref ,n-vec ,(incf n)) ,n-val))))
-                   elements)
-         ,n-vec))))
+  `(make-array ,(length elements) :initial-contents (list ,@elements)))
 
 ;;; Just convert it into a MAKE-ARRAY.
 (deftransform make-string ((length &key
                        ,@(when initial-element
                            '(:initial-element initial-element)))))
 
+;;; Prevent open coding DIMENSION and :INITIAL-CONTENTS arguments,
+;;; so that we can pick them apart.
+(define-source-transform make-array (&whole form &rest args)
+  (declare (ignore args))
+  (if (and (fun-lexically-notinline-p 'list)
+           (fun-lexically-notinline-p 'vector))
+      (values nil t)
+      `(locally (declare (notinline list vector))
+         ,form)))
+
+;;; This baby is a bit of a monster, but it takes care of any MAKE-ARRAY
+;;; call which creates a vector with a known element type -- and tries
+;;; to do a good job with all the different ways it can happen.
+(defun transform-make-array-vector (length element-type initial-element
+                                    initial-contents call)
+  (aver (or (not element-type) (constant-lvar-p element-type)))
+  (let* ((c-length (when (constant-lvar-p length)
+                     (lvar-value length)))
+         (elt-spec (if element-type
+                       (lvar-value element-type)
+                       t))
+         (elt-ctype (ir1-transform-specifier-type elt-spec))
+         (saetp (if (unknown-type-p elt-ctype)
+                    (give-up-ir1-transform "~S is an unknown type: ~S"
+                                           :element-type elt-spec)
+                    (find-saetp-by-ctype elt-ctype)))
+         (default-initial-element (sb!vm:saetp-initial-element-default saetp))
+         (n-bits (sb!vm:saetp-n-bits saetp))
+         (typecode (sb!vm:saetp-typecode saetp))
+         (n-pad-elements (sb!vm:saetp-n-pad-elements saetp))
+         (n-words-form
+          (if c-length
+              (ceiling (* (+ c-length n-pad-elements) n-bits)
+                       sb!vm:n-word-bits)
+              (let ((padded-length-form (if (zerop n-pad-elements)
+                                            'length
+                                            `(+ length ,n-pad-elements))))
+                (cond
+                  ((= n-bits 0) 0)
+                  ((>= n-bits sb!vm:n-word-bits)
+                   `(* ,padded-length-form
+                       ;; i.e., not RATIO
+                       ,(the fixnum (/ n-bits sb!vm:n-word-bits))))
+                  (t
+                   (let ((n-elements-per-word (/ sb!vm:n-word-bits n-bits)))
+                     (declare (type index n-elements-per-word)) ; i.e., not RATIO
+                     `(ceiling ,padded-length-form ,n-elements-per-word)))))))
+         (result-spec
+          `(simple-array ,(sb!vm:saetp-specifier saetp) (,(or c-length '*))))
+         (alloc-form
+          `(truly-the ,result-spec
+                      (allocate-vector ,typecode (the index length) ,n-words-form))))
+    (cond ((and initial-element initial-contents)
+           (abort-ir1-transform "Both ~S and ~S specified."
+                                :initial-contents :initial-element))
+          ;; :INITIAL-CONTENTS (LIST ...), (VECTOR ...) and `(1 1 ,x) with a
+          ;; constant LENGTH.
+          ((and initial-contents c-length
+                (lvar-matches initial-contents
+                              :fun-names '(list vector sb!impl::backq-list)
+                              :arg-count c-length))
+           (let ((parameters (eliminate-keyword-args
+                              call 1 '((:element-type element-type)
+                                       (:initial-contents initial-contents))))
+                 (elt-vars (make-gensym-list c-length))
+                 (lambda-list '(length)))
+             (splice-fun-args initial-contents :any c-length)
+             (dolist (p parameters)
+               (setf lambda-list
+                     (append lambda-list
+                             (if (eq p 'initial-contents)
+                                 elt-vars
+                                 (list p)))))
+             `(lambda ,lambda-list
+                (declare (type ,elt-spec ,@elt-vars)
+                         (ignorable ,@lambda-list))
+                (truly-the ,result-spec
+                 (initialize-vector ,alloc-form ,@elt-vars)))))
+          ;; constant :INITIAL-CONTENTS and LENGTH
+          ((and initial-contents c-length (constant-lvar-p initial-contents))
+           (let ((contents (lvar-value initial-contents)))
+             (unless (= c-length (length contents))
+               (abort-ir1-transform "~S has ~S elements, vector length is ~S."
+                                    :initial-contents (length contents) c-length))
+             (let ((parameters (eliminate-keyword-args
+                                call 1 '((:element-type element-type)
+                                         (:initial-contents initial-contents)))))
+               `(lambda (length ,@parameters)
+                  (declare (ignorable ,@parameters))
+                  (truly-the ,result-spec
+                   (initialize-vector ,alloc-form
+                                      ,@(map 'list (lambda (elt)
+                                                     `(the ,elt-spec ,elt))
+                                             contents)))))))
+          ;; any other :INITIAL-CONTENTS
+          (initial-contents
+           (let ((parameters (eliminate-keyword-args
+                              call 1 '((:element-type element-type)
+                                       (:initial-contents initial-contents)))))
+             `(lambda (length ,@parameters)
+                (declare (ignorable ,@parameters))
+                (unless (= length (length initial-contents))
+                  (error "~S has ~S elements, vector length is ~S."
+                         :initial-contents (length initial-contents) length))
+                (truly-the ,result-spec
+                           (replace ,alloc-form initial-contents)))))
+          ;; :INITIAL-ELEMENT, not EQL to the default
+          ((and initial-element
+                (or (not (constant-lvar-p initial-element))
+                    (not (eql default-initial-element (lvar-value initial-element)))))
+           (let ((parameters (eliminate-keyword-args
+                              call 1 '((:element-type element-type)
+                                       (:initial-element initial-element)))))
+             `(lambda (length ,@parameters)
+                (declare (ignorable ,@parameters))
+                (truly-the ,result-spec
+                           (fill ,alloc-form (the ,elt-spec initial-element))))))
+          ;; just :ELEMENT-TYPE, or maybe with :INITIAL-ELEMENT EQL to the
+          ;; default
+          (t
+           #-sb-xc-host
+           (unless (ctypep default-initial-element elt-ctype)
+             ;; This situation arises e.g. in (MAKE-ARRAY 4 :ELEMENT-TYPE
+             ;; '(INTEGER 1 5)) ANSI's definition of MAKE-ARRAY says "If
+             ;; INITIAL-ELEMENT is not supplied, the consequences of later
+             ;; reading an uninitialized element of new-array are undefined,"
+             ;; so this could be legal code as long as the user plans to
+             ;; write before he reads, and if he doesn't we're free to do
+             ;; anything we like. But in case the user doesn't know to write
+             ;; elements before he reads elements (or to read manuals before
+             ;; he writes code:-), we'll signal a STYLE-WARNING in case he
+             ;; didn't realize this.
+             (if initial-element
+                 (compiler-warn "~S ~S is not a ~S"
+                                :initial-element default-initial-element
+                                elt-spec)
+                 (compiler-style-warn "The default initial element ~S is not a ~S."
+                                      default-initial-element
+                                      elt-spec)))
+           (let ((parameters (eliminate-keyword-args
+                              call 1 '((:element-type element-type)))))
+             `(lambda (length ,@parameters)
+                (declare (ignorable ,@parameters))
+                ,alloc-form))))))
+
+(deftransform make-array ((dims &key
+                                element-type initial-element initial-contents)
+                          (integer &key
+                                   (:element-type (constant-arg *))
+                                   (:initial-element *)
+                                   (:initial-contents *))
+                          *
+                          :node call)
+  (transform-make-array-vector dims
+                               element-type
+                               initial-element
+                               initial-contents
+                               call))
+
+;;; The list type restriction does not ensure that the result will be a
+;;; multi-dimensional array. But the lack of adjustable, fill-pointer,
+;;; and displaced-to keywords ensures that it will be simple.
+;;;
+;;; FIXME: should we generalize this transform to non-simple (though
+;;; non-displaced-to) arrays, given that we have %WITH-ARRAY-DATA to
+;;; deal with those? Maybe when the DEFTRANSFORM
+;;; %DATA-VECTOR-AND-INDEX in the VECTOR case problem is solved? --
+;;; CSR, 2002-07-01
+(deftransform make-array ((dims &key
+                                element-type initial-element initial-contents)
+                          (list &key
+                                (:element-type (constant-arg *))
+                                (:initial-element *)
+                                (:initial-contents *))
+                          *
+                          :node call)
+  (block make-array
+    (when (lvar-matches dims :fun-names '(list) :arg-count 1)
+      (let ((length (car (splice-fun-args dims :any 1))))
+        (return-from make-array
+          (transform-make-array-vector length
+                                       element-type
+                                       initial-element
+                                       initial-contents
+                                       call))))
+    (unless (constant-lvar-p dims)
+      (give-up-ir1-transform
+       "The dimension list is not constant; cannot open code array creation."))
+    (let ((dims (lvar-value dims)))
+      (unless (every #'integerp dims)
+        (give-up-ir1-transform
+         "The dimension list contains something other than an integer: ~S"
+         dims))
+      (if (= (length dims) 1)
+          `(make-array ',(car dims)
+                       ,@(when element-type
+                               '(:element-type element-type))
+                       ,@(when initial-element
+                               '(:initial-element initial-element))
+                       ,@(when initial-contents
+                               '(:initial-contents initial-contents)))
+          (let* ((total-size (reduce #'* dims))
+                 (rank (length dims))
+                 (spec `(simple-array
+                         ,(cond ((null element-type) t)
+                                ((and (constant-lvar-p element-type)
+                                      (ir1-transform-specifier-type
+                                       (lvar-value element-type)))
+                                 (sb!xc:upgraded-array-element-type
+                                  (lvar-value element-type)))
+                                (t '*))
+                         ,(make-list rank :initial-element '*))))
+            `(let ((header (make-array-header sb!vm:simple-array-widetag ,rank))
+                   (data (make-array ,total-size
+                                     ,@(when element-type
+                                             '(:element-type element-type))
+                                     ,@(when initial-element
+                                             '(:initial-element initial-element)))))
+               ,@(when initial-contents
+                       ;; FIXME: This is could be open coded at least a bit too
+                       `((sb!impl::fill-data-vector data ',dims initial-contents)))
+               (setf (%array-fill-pointer header) ,total-size)
+               (setf (%array-fill-pointer-p header) nil)
+               (setf (%array-available-elements header) ,total-size)
+               (setf (%array-data-vector header) data)
+               (setf (%array-displaced-p header) nil)
+               (setf (%array-displaced-from header) nil)
+               ,@(let ((axis -1))
+                      (mapcar (lambda (dim)
+                                `(setf (%array-dimension header ,(incf axis))
+                                       ,dim))
+                              dims))
+               (truly-the ,spec header)))))))
+
 (deftransform make-array ((dims &key initial-element element-type
                                      adjustable fill-pointer)
                           (t &rest *))
                  (%data-vector-and-index array 0)
                (fill vector (the ,(sb!vm:saetp-specifier saetp) initial-element)))
              array)))))
-
-;;; The integer type restriction on the length ensures that it will be
-;;; a vector. The lack of :ADJUSTABLE, :FILL-POINTER, and
-;;; :DISPLACED-TO keywords ensures that it will be simple; the lack of
-;;; :INITIAL-ELEMENT relies on another transform to deal with that
-;;; kind of initialization efficiently.
-(deftransform make-array ((length &key element-type)
-                          (integer &rest *))
-  (let* ((eltype (cond ((not element-type) t)
-                       ((not (constant-lvar-p element-type))
-                        (give-up-ir1-transform
-                         "ELEMENT-TYPE is not constant."))
-                       (t
-                        (lvar-value element-type))))
-         (len (if (constant-lvar-p length)
-                  (lvar-value length)
-                  '*))
-         (eltype-type (ir1-transform-specifier-type eltype))
-         (result-type-spec
-          `(simple-array
-            ,(if (unknown-type-p eltype-type)
-                 (give-up-ir1-transform
-                  "ELEMENT-TYPE is an unknown type: ~S" eltype)
-                 (sb!xc:upgraded-array-element-type eltype))
-            (,len)))
-         (saetp (find-if (lambda (saetp)
-                           (csubtypep eltype-type (sb!vm:saetp-ctype saetp)))
-                         sb!vm:*specialized-array-element-type-properties*)))
-    (unless saetp
-      (give-up-ir1-transform
-       "cannot open-code creation of ~S" result-type-spec))
-    #-sb-xc-host
-    (unless (ctypep (sb!vm:saetp-initial-element-default saetp) eltype-type)
-      ;; This situation arises e.g. in (MAKE-ARRAY 4 :ELEMENT-TYPE
-      ;; '(INTEGER 1 5)) ANSI's definition of MAKE-ARRAY says "If
-      ;; INITIAL-ELEMENT is not supplied, the consequences of later
-      ;; reading an uninitialized element of new-array are undefined,"
-      ;; so this could be legal code as long as the user plans to
-      ;; write before he reads, and if he doesn't we're free to do
-      ;; anything we like. But in case the user doesn't know to write
-      ;; elements before he reads elements (or to read manuals before
-      ;; he writes code:-), we'll signal a STYLE-WARNING in case he
-      ;; didn't realize this.
-      (compiler-style-warn "The default initial element ~S is not a ~S."
-                           (sb!vm:saetp-initial-element-default saetp)
-                           eltype))
-    (let* ((n-bits-per-element (sb!vm:saetp-n-bits saetp))
-           (typecode (sb!vm:saetp-typecode saetp))
-           (n-pad-elements (sb!vm:saetp-n-pad-elements saetp))
-           (padded-length-form (if (zerop n-pad-elements)
-                                   'length
-                                   `(+ length ,n-pad-elements)))
-           (n-words-form
-            (cond
-              ((= n-bits-per-element 0) 0)
-              ((>= n-bits-per-element sb!vm:n-word-bits)
-               `(* ,padded-length-form
-                 (the fixnum ; i.e., not RATIO
-                   ,(/ n-bits-per-element sb!vm:n-word-bits))))
-              (t
-               (let ((n-elements-per-word (/ sb!vm:n-word-bits
-                                             n-bits-per-element)))
-                 (declare (type index n-elements-per-word)) ; i.e., not RATIO
-                 `(ceiling ,padded-length-form ,n-elements-per-word))))))
-      (values
-       `(truly-the ,result-type-spec
-         (allocate-vector ,typecode length ,n-words-form))
-       '((declare (type index length)))))))
-
-;;; The list type restriction does not ensure that the result will be a
-;;; multi-dimensional array. But the lack of adjustable, fill-pointer,
-;;; and displaced-to keywords ensures that it will be simple.
-;;;
-;;; FIXME: should we generalize this transform to non-simple (though
-;;; non-displaced-to) arrays, given that we have %WITH-ARRAY-DATA to
-;;; deal with those? Maybe when the DEFTRANSFORM
-;;; %DATA-VECTOR-AND-INDEX in the VECTOR case problem is solved? --
-;;; CSR, 2002-07-01
-(deftransform make-array ((dims &key element-type)
-                          (list &rest *))
-  (unless (or (null element-type) (constant-lvar-p element-type))
-    (give-up-ir1-transform
-     "The element-type is not constant; cannot open code array creation."))
-  (unless (constant-lvar-p dims)
-    (give-up-ir1-transform
-     "The dimension list is not constant; cannot open code array creation."))
-  (let ((dims (lvar-value dims)))
-    (unless (every #'integerp dims)
-      (give-up-ir1-transform
-       "The dimension list contains something other than an integer: ~S"
-       dims))
-    (if (= (length dims) 1)
-        `(make-array ',(car dims)
-                     ,@(when element-type
-                         '(:element-type element-type)))
-        (let* ((total-size (reduce #'* dims))
-               (rank (length dims))
-               (spec `(simple-array
-                       ,(cond ((null element-type) t)
-                              ((and (constant-lvar-p element-type)
-                                    (ir1-transform-specifier-type
-                                     (lvar-value element-type)))
-                               (sb!xc:upgraded-array-element-type
-                                (lvar-value element-type)))
-                              (t '*))
-                           ,(make-list rank :initial-element '*))))
-          `(let ((header (make-array-header sb!vm:simple-array-widetag ,rank)))
-             (setf (%array-fill-pointer header) ,total-size)
-             (setf (%array-fill-pointer-p header) nil)
-             (setf (%array-available-elements header) ,total-size)
-             (setf (%array-data-vector header)
-                   (make-array ,total-size
-                               ,@(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))
-                                  ,dim))
-                         dims))
-             (truly-the ,spec header))))))
 \f
 ;;;; miscellaneous properties of arrays