"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)))
(assert-new-value-type (car (last stuff)) array))
-(defoptimizer (hairy-data-vector-ref derive-type) ((array index))
- (extract-upgraded-element-type array))
-(defoptimizer (data-vector-ref derive-type) ((array index))
- (extract-upgraded-element-type array))
-
-(defoptimizer (data-vector-set derive-type) ((array index new-value))
- (assert-new-value-type new-value array))
-(defoptimizer (hairy-data-vector-set derive-type) ((array index new-value))
+(macrolet ((define (name)
+ `(defoptimizer (,name derive-type) ((array index))
+ (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))
+ (derive-aref-type array))
+
+(macrolet ((define (name)
+ `(defoptimizer (,name derive-type) ((array index new-value))
+ (assert-new-value-type new-value array))))
+ (define hairy-data-vector-set)
+ (define hairy-data-vector-set/check-bounds)
+ (define data-vector-set))
+
+#!+(or x86 x86-64)
+(defoptimizer (data-vector-set-with-offset derive-type) ((array index offset new-value))
(assert-new-value-type new-value array))
;;; Figure out the type of the data vector if we know the argument
;;; element type.
-(defoptimizer (%with-array-data derive-type) ((array start end))
+(defun derive-%with-array-data/mumble-type (array)
(let ((atype (lvar-type array)))
(when (array-type-p atype)
(specifier-type
`(simple-array ,(type-specifier
- (array-type-specialized-element-type atype))
- (*))))))
+ (array-type-specialized-element-type atype))
+ (*))))))
+(defoptimizer (%with-array-data derive-type) ((array start end))
+ (derive-%with-array-data/mumble-type array))
+(defoptimizer (%with-array-data/fp derive-type) ((array start end))
+ (derive-%with-array-data/mumble-type array))
(defoptimizer (array-row-major-index derive-type) ((array &rest indices))
(assert-array-rank array (length indices))
*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))
,@(mapcar (lambda (el)
(once-only ((n-val el))
`(locally (declare (optimize (safety 0)))
- (setf (svref ,n-vec ,(incf n))
- ,n-val))))
+ (setf (svref ,n-vec ,(incf n)) ,n-val))))
elements)
,n-vec))))
;;; compile-time or we are generating unsafe code, don't bother with
;;; the VOP.
(deftransform %check-bound ((array dimension index) * * :node node)
- (cond ((policy node (and (> speed safety) (= safety 0)))
+ (cond ((policy node (= insert-array-bounds-checks 0))
'index)
((not (constant-lvar-p dimension))
(give-up-ir1-transform))
(t
(let ((dim (lvar-value dimension)))
+ ;; FIXME: Can SPEED > SAFETY weaken this check to INTEGER?
`(the (integer 0 (,dim)) index)))))
\f
;;;; WITH-ARRAY-DATA
(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)
+ &key force-inline check-fill-pointer)
+ &body forms
+ &environment env)
(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)
- (failed-%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)))
+ (let ((check-bounds (policy env (plusp insert-array-bounds-checks))))
+ `(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 (if check-fill-pointer
+ `(length ,n-array)
+ `(array-total-size ,n-array)))
+ (n-end `(or ,n-evalue ,n-len)))
+ (if check-bounds
+ `(if (<= 0 ,n-svalue ,n-end ,n-len)
+ (values ,n-array ,n-svalue ,n-end 0)
+ ,(if check-fill-pointer
+ `(sequence-bounding-indices-bad-error ,n-array ,n-svalue ,n-evalue)
+ `(array-bounding-indices-bad-error ,n-array ,n-svalue ,n-evalue)))
+ `(values ,n-array ,n-svalue ,n-end 0))))
+ ,(if force-inline
+ `(%with-array-data-macro ,n-array ,n-svalue ,n-evalue
+ :check-bounds ,check-bounds
+ :check-fill-pointer ,check-fill-pointer)
+ (if check-fill-pointer
+ `(%with-array-data/fp ,n-array ,n-svalue ,n-evalue)
+ `(%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.
end
&key
(element-type '*)
- unsafe?
- fail-inline?)
+ check-bounds
+ check-fill-pointer)
(with-unique-names (size defaulted-end data cumulative-offset)
- `(let* ((,size (array-total-size ,array))
- (,defaulted-end
- (cond (,end
- (unless (or ,unsafe? (<= ,end ,size))
- ,(if fail-inline?
- `(error 'bounding-indices-bad-error
- :datum (cons ,start ,end)
- :expected-type `(cons (integer 0 ,',size)
- (integer ,',start ,',size))
- :object ,array)
- `(failed-%with-array-data ,array ,start ,end)))
- ,end)
- (t ,size))))
- (unless (or ,unsafe? (<= ,start ,defaulted-end))
- ,(if fail-inline?
- `(error 'bounding-indices-bad-error
- :datum (cons ,start ,end)
- :expected-type `(cons (integer 0 ,',size)
- (integer ,',start ,',size))
- :object ,array)
- `(failed-%with-array-data ,array ,start ,end)))
+ `(let* ((,size ,(if check-fill-pointer
+ `(length ,array)
+ `(array-total-size ,array)))
+ (,defaulted-end (or ,end ,size)))
+ ,@(when check-bounds
+ `((unless (<= ,start ,defaulted-end ,size)
+ ,(if check-fill-pointer
+ `(sequence-bounding-indices-bad-error ,array ,start ,end)
+ `(array-bounding-indices-bad-error ,array ,start ,end)))))
(do ((,data ,array (%array-data-vector ,data))
(,cumulative-offset 0
(+ ,cumulative-offset
(the index ,cumulative-offset)))
(declare (type index ,cumulative-offset))))))
+(defun transform-%with-array-data/muble (array node check-fill-pointer)
+ (let ((element-type (upgraded-element-type-specifier-or-give-up array))
+ (type (lvar-type array))
+ (check-bounds (policy node (plusp insert-array-bounds-checks))))
+ (if (and (array-type-p type)
+ (not (array-type-complexp type))
+ (listp (array-type-dimensions type))
+ (not (null (cdr (array-type-dimensions type)))))
+ ;; If it's a simple multidimensional array, then just return
+ ;; its data vector directly rather than going through
+ ;; %WITH-ARRAY-DATA-MACRO. SBCL doesn't generally generate
+ ;; code that would use this currently, but we have encouraged
+ ;; users to use WITH-ARRAY-DATA and we may use it ourselves at
+ ;; some point in the future for optimized libraries or
+ ;; similar.
+ (if check-bounds
+ `(let* ((data (truly-the (simple-array ,element-type (*))
+ (%array-data-vector array)))
+ (len (length data))
+ (real-end (or end len)))
+ (unless (<= 0 start data-end lend)
+ (sequence-bounding-indices-bad-error array start end))
+ (values data 0 real-end 0))
+ `(let ((data (truly-the (simple-array ,element-type (*))
+ (%array-data-vector array))))
+ (values data 0 (or end (length data)) 0)))
+ `(%with-array-data-macro array start end
+ :check-fill-pointer ,check-fill-pointer
+ :check-bounds ,check-bounds
+ :element-type ,element-type))))
+
+;; It might very well be reasonable to allow general ARRAY here, I
+;; just haven't tried to understand the performance issues involved.
+;; -- WHN, and also CSR 2002-05-26
(deftransform %with-array-data ((array start end)
- ;; It might very well be reasonable to
- ;; allow general ARRAY here, I just
- ;; haven't tried to understand the
- ;; performance issues involved. --
- ;; WHN, and also CSR 2002-05-26
- ((or vector simple-array) index (or index null))
+ ((or vector simple-array) index (or index null) t)
+ *
+ :node node
+ :policy (> speed space))
+ "inline non-SIMPLE-vector-handling logic"
+ (transform-%with-array-data/muble array node nil))
+(deftransform %with-array-data/fp ((array start end)
+ ((or vector simple-array) index (or index null) t)
*
:node node
:policy (> speed space))
"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)))
+ (transform-%with-array-data/muble array node t))
\f
;;;; array accessors
(deftransform aref ((array &rest indices))
(with-row-major-index (array indices index)
(hairy-data-vector-ref array index)))
+
(deftransform %aset ((array &rest stuff))
(let ((indices (butlast stuff)))
(with-row-major-index (array indices index new-value)
(hairy-data-vector-set array index new-value)))))
+;; For AREF of vectors we do the bounds checking in the callee. This
+;; lets us do a significantly more efficient check for simple-arrays
+;; 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))
+ (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))
+ `(hairy-data-vector-set array index new-value)
+ `(hairy-data-vector-set/check-bounds array index new-value)))
+
+;;; But if we find out later that there's some useful type information
+;;; 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)))
+ ;; If an element type has been declared, we want to
+ ;; use that information it for type checking (even
+ ;; if the access can't be optimized due to the array
+ ;; not being simple).
+ (when (and (eql element-type *wild-type*)
+ ;; This type logic corresponds to the special
+ ;; case for strings in HAIRY-DATA-VECTOR-REF
+ ;; (generic/vm-tran.lisp)
+ (not (csubtypep type (specifier-type 'simple-string))))
+ (when (or (not (array-type-p type))
+ ;; If it's a simple array, we might be able
+ ;; to inline the access completely.
+ (not (null (array-type-complexp type))))
+ (give-up-ir1-transform
+ "Upgraded element type of array is not known at compile time."))))
+ `(,',transform-to array
+ (%check-bound array
+ (array-dimension array 0)
+ index)
+ ,@',extra))))
+ (define hairy-data-vector-ref/check-bounds
+ hairy-data-vector-ref nil nil)
+ (define hairy-data-vector-set/check-bounds
+ hairy-data-vector-set (new-value) (*)))
+
;;; Just convert into a HAIRY-DATA-VECTOR-REF (or
;;; HAIRY-DATA-VECTOR-SET) after checking that the index is inside the
;;; array total size.