(fixnum index))
(%check-bound array bound index))
+(defun %with-array-data/fp (array start end)
+ (%with-array-data-macro array start end :check-bounds t :check-fill-pointer t))
+
(defun %with-array-data (array start end)
- (%with-array-data-macro array start end :fail-inline? t))
+ (%with-array-data-macro array start end :check-bounds t :check-fill-pointer nil))
(defun %data-vector-and-index (array index)
(if (array-header-p array)
(%with-array-data array index nil)
(values vector index))
(values array index)))
-
-(defun %simple-vector-compare-and-swap (vector index old new)
- #!+(or x86 x86-64)
- (%simple-vector-compare-and-swap vector index old new)
- #!-(or x86 x86-64)
- (let ((n-old (svref vector index)))
- (when (eq old n-old)
- (setf (svref vector index) new))
- n-old))
-
-;;; It'd waste space to expand copies of error handling in every
-;;; inline %WITH-ARRAY-DATA, so we have them call this function
-;;; instead. This is just a wrapper which is known never to return.
-(defun failed-%with-array-data (array start end)
- (declare (notinline %with-array-data))
- (%with-array-data array start end)
- (bug "called FAILED-%WITH-ARRAY-DATA with valid array parameters?"))
\f
;;;; MAKE-ARRAY
(eval-when (:compile-toplevel :execute)
;;; the type information is available. Finally, for each of these
;;; routines also provide a slow path, taken for arrays that are not
;;; vectors or not simple.
-(macrolet ((%define (table-name extra-params)
- `(funcall
- (the function
- (let ((tag 0)
- (offset
- #.(ecase sb!c:*backend-byte-order*
- (:little-endian
- (- sb!vm:other-pointer-lowtag))
- (:big-endian
- ;; I'm not completely sure of what this
- ;; 3 represents symbolically. It's
- ;; just what all the LOAD-TYPE vops
- ;; are doing.
- (- 3 sb!vm:other-pointer-lowtag)))))
- ;; WIDETAG-OF needs extra code to handle
- ;; LIST and FUNCTION lowtags. We're only
- ;; dispatching on other pointers, so let's
- ;; do the lowtag extraction manually.
- (when (sb!vm::%other-pointer-p array)
- (setf tag
- (sb!sys:sap-ref-8 (int-sap (get-lisp-obj-address array))
- offset)))
- ;; SYMBOL-GLOBAL-VALUE is a performance hack
- ;; for threaded builds.
- (svref (sb!vm::symbol-global-value ',table-name) tag)))
- array index ,@extra-params))
- (define (accessor-name slow-accessor-name table-name extra-params
- check-bounds)
- `(progn
- (defvar ,table-name)
- (defun ,accessor-name (array index ,@extra-params)
- (declare (optimize speed
- ;; (SAFETY 0) is ok. All calls to
- ;; these functions are generated by
- ;; the compiler, so argument count
- ;; checking isn't needed. Type checking
- ;; is done implicitly via the widetag
- ;; dispatch.
- (safety 0)))
- (%define ,table-name ,extra-params))
- (defun ,slow-accessor-name (array index ,@extra-params)
- (declare (optimize speed (safety 0)))
- (if (not (%array-displaced-p array))
- ;; The reasonably quick path of non-displaced complex
- ;; arrays.
- (let ((array (%array-data-vector array)))
- (%define ,table-name ,extra-params))
- ;; The real slow path.
- (with-array-data
- ((vector array)
- (index (locally
- (declare (optimize (speed 1) (safety 1)))
- (,@check-bounds index)))
- (end)
- :force-inline t)
- (declare (ignore end))
- (,accessor-name vector index ,@extra-params)))))))
+(macrolet ((def (name table-name)
+ `(progn
+ (defvar ,table-name)
+ (defmacro ,name (array-var)
+ `(the function
+ (let ((tag 0)
+ (offset
+ #.(ecase sb!c:*backend-byte-order*
+ (:little-endian
+ (- sb!vm:other-pointer-lowtag))
+ (:big-endian
+ (- (1- sb!vm:n-word-bytes) sb!vm:other-pointer-lowtag)))))
+ ;; WIDETAG-OF needs extra code to handle LIST and
+ ;; FUNCTION lowtags. We're only dispatching on
+ ;; other pointers, so let's do the lowtag
+ ;; extraction manually.
+ (when (sb!vm::%other-pointer-p ,array-var)
+ (setf tag
+ (sb!sys:sap-ref-8 (int-sap (get-lisp-obj-address ,array-var))
+ offset)))
+ ;; SYMBOL-GLOBAL-VALUE is a performance hack
+ ;; for threaded builds.
+ (svref (sb!vm::symbol-global-value ',',table-name) tag)))))))
+ (def !find-data-vector-setter *data-vector-setters*)
+ (def !find-data-vector-setter/check-bounds *data-vector-setters/check-bounds*)
+ (def !find-data-vector-reffer *data-vector-reffers*)
+ (def !find-data-vector-reffer/check-bounds *data-vector-reffers/check-bounds*))
+
+(macrolet ((%ref (accessor-getter extra-params)
+ `(funcall (,accessor-getter array) array index ,@extra-params))
+ (define (accessor-name slow-accessor-name accessor-getter
+ extra-params check-bounds)
+ `(progn
+ (defun ,accessor-name (array index ,@extra-params)
+ (declare (optimize speed
+ ;; (SAFETY 0) is ok. All calls to
+ ;; these functions are generated by
+ ;; the compiler, so argument count
+ ;; checking isn't needed. Type checking
+ ;; is done implicitly via the widetag
+ ;; dispatch.
+ (safety 0)))
+ (%ref ,accessor-getter ,extra-params))
+ (defun ,slow-accessor-name (array index ,@extra-params)
+ (declare (optimize speed (safety 0)))
+ (if (not (%array-displaced-p array))
+ ;; The reasonably quick path of non-displaced complex
+ ;; arrays.
+ (let ((array (%array-data-vector array)))
+ (%ref ,accessor-getter ,extra-params))
+ ;; The real slow path.
+ (with-array-data
+ ((vector array)
+ (index (locally
+ (declare (optimize (speed 1) (safety 1)))
+ (,@check-bounds index)))
+ (end)
+ :force-inline t)
+ (declare (ignore end))
+ (,accessor-name vector index ,@extra-params)))))))
(define hairy-data-vector-ref slow-hairy-data-vector-ref
- *data-vector-reffers* nil (progn))
+ !find-data-vector-reffer
+ nil (progn))
(define hairy-data-vector-set slow-hairy-data-vector-set
- *data-vector-setters* (new-value) (progn))
+ !find-data-vector-setter
+ (new-value) (progn))
(define hairy-data-vector-ref/check-bounds
slow-hairy-data-vector-ref/check-bounds
- *data-vector-reffers/check-bounds* nil
- (%check-bound array (array-dimension array 0)))
+ !find-data-vector-reffer/check-bounds
+ nil (%check-bound array (array-dimension array 0)))
(define hairy-data-vector-set/check-bounds
slow-hairy-data-vector-set/check-bounds
- *data-vector-setters/check-bounds* (new-value)
- (%check-bound array (array-dimension array 0))))
+ !find-data-vector-setter/check-bounds
+ (new-value) (%check-bound array (array-dimension array 0))))
(defun hairy-ref-error (array index &optional new-value)
(declare (ignore index new-value))
(defun data-vector-ref (array index)
(hairy-data-vector-ref array index))
+(defun data-vector-ref-with-offset (array index offset)
+ (hairy-data-vector-ref array (+ index offset)))
+
;;; SUBSCRIPTS has a dynamic-extent list structure and is destroyed
(defun %array-row-major-index (array subscripts
&optional (invalid-index-error-p t))
t))
(defun array-row-major-index (array &rest subscripts)
- (declare (dynamic-extent subscripts))
+ (declare (truly-dynamic-extent subscripts))
(%array-row-major-index array subscripts))
(defun aref (array &rest subscripts)
#!+sb-doc
"Return the element of the ARRAY specified by the SUBSCRIPTS."
- (declare (dynamic-extent subscripts))
+ (declare (truly-dynamic-extent subscripts))
(row-major-aref array (%array-row-major-index array subscripts)))
(defun %aset (array &rest stuff)
- (declare (dynamic-extent stuff))
+ (declare (truly-dynamic-extent stuff))
(let ((subscripts (butlast stuff))
(new-value (car (last stuff))))
(setf (row-major-aref array (%array-row-major-index array subscripts))
#!-sb-fluid (declaim (inline (setf aref)))
(defun (setf aref) (new-value array &rest subscripts)
- (declare (dynamic-extent subscripts))
+ (declare (truly-dynamic-extent subscripts))
(declare (type array array))
(setf (row-major-aref array (%array-row-major-index array subscripts))
new-value))
(declare (array array))
(and (array-header-p array) (%array-fill-pointer-p array)))
+(defun fill-pointer-error (vector arg)
+ (cond (arg
+ (aver (array-has-fill-pointer-p vector))
+ (let ((max (%array-available-elements vector)))
+ (error 'simple-type-error
+ :datum arg
+ :expected-type (list 'integer 0 max)
+ :format-control "The new fill pointer, ~S, is larger than the length of the vector (~S.)"
+ :format-arguments (list arg max))))
+ (t
+ (error 'simple-type-error
+ :datum vector
+ :expected-type '(and vector (satisfies array-has-fill-pointer-p))
+ :format-control "~S is not an array with a fill pointer."
+ :format-arguments (list vector)))))
+
(defun fill-pointer (vector)
#!+sb-doc
"Return the FILL-POINTER of the given VECTOR."
- (declare (vector vector))
- (if (and (array-header-p vector) (%array-fill-pointer-p vector))
+ (if (array-has-fill-pointer-p vector)
(%array-fill-pointer vector)
- (error 'simple-type-error
- :datum vector
- :expected-type '(and vector (satisfies array-has-fill-pointer-p))
- :format-control "~S is not an array with a fill pointer."
- :format-arguments (list vector))))
+ (fill-pointer-error vector nil)))
(defun %set-fill-pointer (vector new)
- (declare (vector vector) (fixnum new))
- (if (and (array-header-p vector) (%array-fill-pointer-p vector))
- (if (> new (%array-available-elements vector))
- (error
- "The new fill pointer, ~S, is larger than the length of the vector."
- new)
- (setf (%array-fill-pointer vector) new))
- (error 'simple-type-error
- :datum vector
- :expected-type '(and vector (satisfies array-has-fill-pointer-p))
- :format-control "~S is not an array with a fill pointer."
- :format-arguments (list vector))))
+ (flet ((oops (x)
+ (fill-pointer-error vector x)))
+ (if (array-has-fill-pointer-p vector)
+ (if (> new (%array-available-elements vector))
+ (oops new)
+ (setf (%array-fill-pointer vector) new))
+ (oops nil))))
;;; FIXME: It'd probably make sense to use a MACROLET to share the
;;; guts of VECTOR-PUSH between VECTOR-PUSH-EXTEND. Such a macro
(cond ((= fill-pointer (%array-available-elements array))
nil)
(t
- (setf (aref array fill-pointer) new-el)
+ (locally (declare (optimize (safety 0)))
+ (setf (aref array fill-pointer) new-el))
(setf (%array-fill-pointer array) (1+ fill-pointer))
fill-pointer))))
(defun vector-push-extend (new-element
vector
&optional
- (extension (1+ (length vector))))
- (declare (vector vector) (fixnum extension))
+ (min-extension
+ (let ((length (length vector)))
+ (min (1+ length)
+ (- array-dimension-limit length)))))
+ (declare (vector vector) (fixnum min-extension))
(let ((fill-pointer (fill-pointer vector)))
(declare (fixnum fill-pointer))
(when (= fill-pointer (%array-available-elements vector))
- (adjust-array vector (+ fill-pointer extension)))
+ (adjust-array vector (+ fill-pointer (max 1 min-extension))))
;; disable bounds checking
(locally (declare (optimize (safety 0)))
(setf (aref vector fill-pointer) new-element))
initial-element-p))
(if (adjustable-array-p array)
(set-array-header array new-data new-length
- new-length 0 dimensions nil)
+ nil 0 dimensions nil)
(let ((new-array
(make-array-header
sb!vm:simple-array-widetag array-rank)))
(set-array-header new-array new-data new-length
- new-length 0 dimensions nil)))))))))))
+ nil 0 dimensions nil)))))))))))
(defun get-new-fill-pointer (old-array new-array-size fill-pointer)
(setf (%array-dimension array 0) dimensions))
(setf (%array-displaced-p array) displacedp)
array)
+
+;;; User visible extension
+(declaim (ftype (function (array) (values (simple-array * (*)) &optional))
+ array-storage-vector))
+(defun array-storage-vector (array)
+ "Returns the underlying storage vector of ARRAY, which must be a non-displaced array.
+
+In SBCL, if ARRAY is a of type \(SIMPLE-ARRAY * \(*)), it is its own storage
+vector. Multidimensional arrays, arrays with fill pointers, and adjustable
+arrays have an underlying storage vector with the same ARRAY-ELEMENT-TYPE as
+ARRAY, which this function returns.
+
+Important note: the underlying vector is an implementation detail. Even though
+this function exposes it, changes in the implementation may cause this
+function to be removed without further warning."
+ ;; KLUDGE: Without TRULY-THE the system is not smart enough to figure out that
+ ;; the return value is always of the known type.
+ (truly-the (simple-array * (*))
+ (if (array-header-p array)
+ (if (%array-displaced-p array)
+ (error "~S cannot be used with displaced arrays. Use ~S instead."
+ 'array-storage-vector 'array-displacement)
+ (%array-data-vector array))
+ array)))
\f
+;;;; used by SORT
+
+;;; temporary vector for stable sorting vectors, allocated for each new thread
+(defvar *merge-sort-temp-vector* (vector))
+(declaim (simple-vector *merge-sort-temp-vector*))
+
;;;; ZAP-ARRAY-DATA for ADJUST-ARRAY
;;; a temporary to be used when OLD-DATA and NEW-DATA are EQ.
(unless (typep initial-element element-type)
(error "~S can't be used to initialize an array of type ~S."
initial-element element-type)))
- (without-interrupts
- ;; Need to disable interrupts while using the temp-vector.
- ;; An interrupt handler that also happened to call
- ;; ADJUST-ARRAY could otherwise stomp on our data here.
- (let ((temp (zap-array-data-temp new-length
- initial-element initial-element-p)))
- (declare (simple-vector temp))
- (zap-array-data-aux old-data old-dims offset temp new-dims)
- (dotimes (i new-length)
- (setf (aref new-data i) (aref temp i)
- ;; zero out any garbage right away
- (aref temp i) 0)))))
+ (let ((temp (zap-array-data-temp new-length
+ initial-element initial-element-p)))
+ (declare (simple-vector temp))
+ (zap-array-data-aux old-data old-dims offset temp new-dims)
+ (dotimes (i new-length)
+ (setf (aref new-data i) (aref temp i)
+ ;; zero out any garbage right away
+ (aref temp i) 0))))
(t
;; When OLD-DATA and NEW-DATA are not EQ, NEW-DATA has
;; already been filled with any