0.pre7.14.flaky4.5:
[sbcl.git] / src / compiler / array-tran.lisp
index 2e22234..e1fddb3 100644 (file)
 
 (in-package "SB!C")
 \f
-;;;; DERIVE-TYPE optimizers
-
-;;; Array operations that use a specific number of indices implicitly
-;;; assert that the array is of that rank.
-(defun assert-array-rank (array rank)
-  (assert-continuation-type
-   array
-   (specifier-type `(array * ,(make-list rank :initial-element '*)))))
+;;;; utilities for optimizing array operations
+
+;;; Return UPGRADED-ARRAY-ELEMENT-TYPE for CONTINUATION, or do
+;;; GIVE-UP-IR1-TRANSFORM if the upgraded element type can't be
+;;; determined.
+(defun upgraded-element-type-specifier-or-give-up (continuation)
+  (let* ((element-ctype (extract-upgraded-element-type continuation))
+        (element-type-specifier (type-specifier element-ctype)))
+    (if (eq element-type-specifier '*)
+       (give-up-ir1-transform
+        "upgraded array element type not known at compile time")
+       element-type-specifier)))
 
 ;;; Array access functions return an object from the array, hence its
 ;;; type will be asserted to be array element type.
   (or (not arg)
       (and (constant-continuation-p arg)
           (not (continuation-value arg)))))
+\f
+;;;; DERIVE-TYPE optimizers
+
+;;; Array operations that use a specific number of indices implicitly
+;;; assert that the array is of that rank.
+(defun assert-array-rank (array rank)
+  (assert-continuation-type
+   array
+   (specifier-type `(array * ,(make-list rank :initial-element '*)))))
 
 (defoptimizer (array-in-bounds-p derive-type) ((array &rest indices))
   (assert-array-rank array (length indices))
                     (unsupplied-or-nil fill-pointer))))
     (specifier-type
      `(,(if simple 'simple-array 'array)
-       ,(cond ((not element-type) 't)
+       ,(cond ((not element-type) t)
              ((constant-continuation-p element-type)
               (continuation-value element-type))
              (t
                            :policy (and (> speed safety) (= safety 0)))
   'index)
 \f
-;;;; array accessors
+;;;; WITH-ARRAY-DATA
+
+;;; This checks to see whether the array is simple and the start and
+;;; end are in bounds. If so, it proceeds with those values.
+;;; Otherwise, it calls %WITH-ARRAY-DATA. Note that %WITH-ARRAY-DATA
+;;; may be further optimized.
+;;;
+;;; Given any ARRAY, bind DATA-VAR to the array's data vector and
+;;; START-VAR and END-VAR to the start and end of the designated
+;;; portion of the data vector. SVALUE and EVALUE are any start and
+;;; end specified to the original operation, and are factored into the
+;;; bindings of START-VAR and END-VAR. OFFSET-VAR is the cumulative
+;;; offset of all displacements encountered, and does not include
+;;; SVALUE.
+;;;
+;;; When FORCE-INLINE is set, the underlying %WITH-ARRAY-DATA form is
+;;; forced to be inline, overriding the ordinary judgment of the
+;;; %WITH-ARRAY-DATA DEFTRANSFORMs. Ordinarily the DEFTRANSFORMs are
+;;; fairly picky about their arguments, figuring that if you haven't
+;;; bothered to get all your ducks in a row, you probably don't care
+;;; that much about speed anyway! But in some cases it makes sense to
+;;; do type testing inside %WITH-ARRAY-DATA instead of outside, and
+;;; the DEFTRANSFORM can't tell that that's going on, so it can make
+;;; sense to use FORCE-INLINE option in that case.
+(def!macro with-array-data (((data-var array &key offset-var)
+                            (start-var &optional (svalue 0))
+                            (end-var &optional (evalue nil))
+                            &key force-inline)
+                           &body forms)
+  (once-only ((n-array array)
+             (n-svalue `(the index ,svalue))
+             (n-evalue `(the (or index null) ,evalue)))
+    `(multiple-value-bind (,data-var
+                          ,start-var
+                          ,end-var
+                          ,@(when offset-var `(,offset-var)))
+        (if (not (array-header-p ,n-array))
+            (let ((,n-array ,n-array))
+              (declare (type (simple-array * (*)) ,n-array))
+              ,(once-only ((n-len `(length ,n-array))
+                           (n-end `(or ,n-evalue ,n-len)))
+                 `(if (<= ,n-svalue ,n-end ,n-len)
+                      ;; success
+                      (values ,n-array ,n-svalue ,n-end 0)
+                      ;; failure: Make a NOTINLINE call to
+                      ;; %WITH-ARRAY-DATA with our bad data
+                      ;; to cause the error to be signalled.
+                      (locally
+                        (declare (notinline %with-array-data))
+                        (%with-array-data ,n-array ,n-svalue ,n-evalue)))))
+            (,(if force-inline '%with-array-data-macro '%with-array-data)
+             ,n-array ,n-svalue ,n-evalue))
+       ,@forms)))
+
+;;; This is the fundamental definition of %WITH-ARRAY-DATA, for use in
+;;; DEFTRANSFORMs and DEFUNs.
+(def!macro %with-array-data-macro (array
+                                  start
+                                  end
+                                  &key
+                                  (element-type '*)
+                                  unsafe?
+                                  fail-inline?)
+  (/show "in %WITH-ARRAY-DATA-MACRO, yes.." array start end)
+  (let ((size (gensym "SIZE-"))
+       (defaulted-end (gensym "DEFAULTED-END-"))
+       (data (gensym "DATA-"))
+       (cumulative-offset (gensym "CUMULATIVE-OFFSET-")))
+    `(let* ((,size (array-total-size ,array))
+           (,defaulted-end
+             (cond (,end
+                    (unless (or ,unsafe? (<= ,end ,size))
+                      ,(if fail-inline?
+                           `(error "End ~D is greater than total size ~D."
+                                   ,end ,size)
+                           `(failed-%with-array-data ,array ,start ,end)))
+                    ,end)
+                   (t ,size))))
+       (unless (or ,unsafe? (<= ,start ,defaulted-end))
+        ,(if fail-inline?
+             `(error "Start ~D is greater than end ~D." ,start ,defaulted-end)
+             `(failed-%with-array-data ,array ,start ,end)))
+       (do ((,data ,array (%array-data-vector ,data))
+           (,cumulative-offset 0
+                               (+ ,cumulative-offset
+                                  (%array-displacement ,data))))
+          ((not (array-header-p ,data))
+           (values (the (simple-array ,element-type 1) ,data)
+                   (the index (+ ,cumulative-offset ,start))
+                   (the index (+ ,cumulative-offset ,defaulted-end))
+                   (the index ,cumulative-offset)))
+        (declare (type index ,cumulative-offset))))))
 
-;;; Handle the 1-dimensional case of %WITH-ARRAY-DATA specially. It's
-;;; important to do this efficiently if we want people to be able to
-;;; use vectors with fill pointers anywhere near inner loops, and
-;;; hence it's important to do this efficiently if we want people to
-;;; be able to use sequence functions anywhere near inner loops.
 (deftransform %with-array-data ((array start end)
-                               (vector index index)
+                               ;; Note: This transform is limited to
+                               ;; VECTOR only because I happened to
+                               ;; create it in order to get sequence
+                               ;; function operations to be more
+                               ;; efficient. It might very well be
+                               ;; reasonable to allow general ARRAY
+                               ;; here, I just haven't tried to
+                               ;; understand the performance issues
+                               ;; involved. -- WHN
+                               (vector index (or index null))
                                *
                                :important t
                                :node node
                                :policy (> speed space))
-  "avoid full call to %WITH-ARRAY-DATA at runtime"
-  (let* ((element-ctype (extract-upgraded-element-type array))
-        (element-type-specifier (type-specifier element-ctype))
-        (simple-array-type `(simple-array ,element-type-specifier 1)))
-    (declare (type ctype element-ctype))
-    #|
-    (when (eq element-type-specifier '*)
-      (give-up-ir1-transform
-       "upgraded array element type not known at compile time"))
-    |#
-    `(let* (;; FIXME: Instead of doing this hairy expression for SIZE,
-           ;; it should just be (ARRAY-DIMENSION ARRAY 0), and there
-           ;; should be a DEFTRANSFORM for ARRAY-DIMENSION which
-           ;; expands that way.
-           (size (if (array-header-p array)
-                     (%array-dimension array 0)
-                     (length (the ,simple-array-type array))))
-           (end (if end
-                    (if (or ,(policy node (= safety 0))
-                            (<= (the index end) size))
-                        end
-                        (vector-data-start-out-of-range))
-                    size)))
-       (declare (type index end))
-       (unless (or ,(policy node (= safety 0))
-                  (<= start end))
-        (vector-data-end-out-of-range))
-       (do (;; cumulative displacement
-           (d 0 (truly-the index (+ d (%array-displacement array))))
-           ;; eventually becomes bare data vector
-           (v array (%array-data-vector v))) 
-          ((not (array-header-p v))
-           (values (the ,simple-array-type v)
-                   (truly-the index (+ d start))
-                   (truly-the index (+ d end))
-                   (the index d)))
-        (declare (type index d))))))
-(defun vector-data-start-out-of-range ()
-  (error "The start of vector data was out of range."))
-(defun vector-data-end-out-of-range ()
-  (error "The end of vector data was out of range."))
+  "inline non-SIMPLE-vector-handling logic"
+  (let ((element-type (upgraded-element-type-specifier-or-give-up array)))
+    `(%with-array-data-macro array start end
+                            :unsafe? ,(policy node (= safety 0))
+                            :element-type ,element-type)))
+\f
+;;;; array accessors
 
 ;;; We convert all typed array accessors into AREF and %ASET with type
 ;;; assertions on the array.
                `(lambda (,',array ,@n-indices
                                   ,@',(when new-value (list new-value)))
                   (let* (,@(let ((,index -1))
-                             (mapcar #'(lambda (name)
-                                         `(,name (array-dimension
-                                                  ,',array
-                                                  ,(incf ,index))))
+                             (mapcar (lambda (name)
+                                       `(,name (array-dimension
+                                                ,',array
+                                                ,(incf ,index))))
                                      dims))
                            (,',index
                             ,(if (null dims)