Fix SB-VM::SPACE-BYTES to avoid consing SAPs
[sbcl.git] / src / code / alloc.lisp
blob7a71c90ed9595f2ab4adc038156c5bb742ee3179
1 ;;;; Lisp-side allocation (used currently only for direct allocation
2 ;;;; to static and immobile spaces).
4 ;;;; This software is part of the SBCL system. See the README file for
5 ;;;; more information.
6 ;;;;
7 ;;;; This software is derived from the CMU CL system, which was
8 ;;;; written at Carnegie Mellon University and released into the
9 ;;;; public domain. The software is in the public domain and is
10 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
11 ;;;; files for more information.
13 (in-package "SB!VM")
15 #!-sb-fluid (declaim (inline store-word))
16 (defun store-word (word base &optional (offset 0) (lowtag 0))
17 (declare (type (unsigned-byte #.n-word-bits) word base offset)
18 (type (unsigned-byte #.n-lowtag-bits) lowtag))
19 (setf (sap-ref-word (int-sap base) (- (ash offset word-shift) lowtag)) word))
21 (defun allocate-static-vector (widetag length words)
22 (declare (type (unsigned-byte #.n-widetag-bits) widetag)
23 (type (unsigned-byte #.n-word-bits) words)
24 (type index length))
25 ;; WITHOUT-GCING implies WITHOUT-INTERRUPTS
26 (or
27 (without-gcing
28 (let* ((pointer (sap-int *static-space-free-pointer*))
29 (vector (logior pointer other-pointer-lowtag))
30 (nbytes (pad-data-block (+ words vector-data-offset)))
31 (new-pointer (+ pointer nbytes)))
32 (when (> static-space-end new-pointer)
33 (store-word widetag
34 vector 0 other-pointer-lowtag)
35 (store-word (fixnumize length)
36 vector vector-length-slot other-pointer-lowtag)
37 (store-word 0 new-pointer)
38 (setf *static-space-free-pointer* (int-sap new-pointer))
39 (%make-lisp-obj vector))))
40 (error 'simple-storage-condition
41 :format-control "Not enough memory left in static space to ~
42 allocate vector.")))
44 #!+immobile-space
45 (progn
47 (defglobal *immobile-space-mutex* (sb!thread:make-mutex :name "Immobile space"))
49 (eval-when (:compile-toplevel)
50 (assert (eql code-code-size-slot 1))
51 (assert (eql code-debug-info-slot 2)))
53 (define-alien-variable "varyobj_holes" long)
54 (define-alien-variable "varyobj_page_touched_bits" (* (unsigned 32)))
55 (define-alien-variable "varyobj_page_scan_start_offset" (* (unsigned 16)))
56 (define-alien-variable "varyobj_page_header_gens" (* (unsigned 8)))
57 (define-alien-routine "find_preceding_object" long (where long))
59 ;;; Lazily created freelist, used only when unallocate is called:
60 ;;; A cons whose car is a sorted list of hole sizes available
61 ;;; and whose cdr is a hashtable.
62 ;;; The keys in the hashtable are hole sizes, values are lists of holes.
63 ;;; A better structure would be just a sorted array of sizes
64 ;;; with each entry pointing to the holes which are threaded through
65 ;;; some bytes in the storage itself rather than through cons cells.
66 (!defglobal *immobile-freelist* nil)
68 ;;; Return the zero-based index within the varyobj subspace of immobile space.
69 (defun varyobj-page-index (address)
70 (declare (type (and fixnum unsigned-byte) address))
71 (values (floor (- address (+ immobile-space-start immobile-fixedobj-subspace-size))
72 immobile-card-bytes)))
74 (defun varyobj-page-address (index)
75 (+ immobile-space-start immobile-fixedobj-subspace-size
76 (* index immobile-card-bytes)))
78 ;;; Convert a zero-based varyobj page index into a scan start address.
79 (defun varyobj-page-scan-start (index)
80 (- (+ immobile-space-start immobile-fixedobj-subspace-size
81 (* (1+ index) immobile-card-bytes))
82 (* 2 n-word-bytes (deref varyobj-page-scan-start-offset index))))
84 (declaim (inline hole-p))
85 (defun hole-p (raw-address)
86 (eql (sap-ref-32 (int-sap raw-address) 0)
87 (logior (ash 2 n-widetag-bits) code-header-widetag)))
89 (defun freed-hole-p (address)
90 (and (hole-p address)
91 ;; A hole is not considered to have been freed until it is
92 ;; no longer in the chain of objects linked through
93 ;; the debug_info slot.
94 (eql (sap-ref-word (int-sap address)
95 (ash code-debug-info-slot word-shift))
96 nil-value)))
98 (declaim (inline hole-size))
99 (defun hole-size (hole-address) ; in bytes
100 (+ (sap-ref-lispobj (int-sap hole-address) (ash code-code-size-slot word-shift))
101 (ash 2 word-shift))) ; add 2 boxed words
103 (declaim (inline (setf hole-size)))
104 (defun (setf hole-size) (new-size hole) ; NEW-SIZE is in bytes
105 (setf (sap-ref-lispobj (int-sap hole) (ash code-code-size-slot word-shift))
106 (- new-size (ash 2 word-shift)))) ; account for 2 boxed words
108 (declaim (inline hole-end-address))
109 (defun hole-end-address (hole-address)
110 (+ hole-address (hole-size hole-address)))
112 (defun sorted-list-insert (item list key-fn)
113 (declare (function key-fn))
114 (let ((key (funcall key-fn item)) (tail list) prev)
115 (loop
116 (when (null tail)
117 (let ((new-tail (list item)))
118 (return (cond ((not prev) new-tail)
119 (t (setf (cdr prev) new-tail) list)))))
120 (let ((head (car tail)))
121 (when (< key (funcall key-fn head))
122 (rplaca tail item)
123 (rplacd tail (cons head (cdr tail)))
124 (return list)))
125 (setq prev tail tail (cdr tail)))))
127 ;;; These routines are not terribly efficient, but very straightforward
128 ;;; since we can assume the existence of hashtables.
129 (defun add-to-freelist (hole)
130 (let* ((size (hole-size hole))
131 (freelist *immobile-freelist*)
132 (table (cdr freelist))
133 (old (gethash (hole-size hole) table)))
134 ;; Check for double-free error
135 #!+immobile-space-debug (aver (not (member hole (gethash size table))))
136 (unless old
137 (setf (car freelist)
138 (sorted-list-insert size (car freelist) #'identity)))
139 (setf (gethash size table) (cons hole old))))
141 (defun remove-from-freelist (hole)
142 (let* ((key (hole-size hole))
143 (freelist *immobile-freelist*)
144 (table (cdr freelist))
145 (list (gethash key table))
146 (old-length (length list))
147 (new (delete hole list :count 1)))
148 (declare (ignorable old-length))
149 #!+immobile-space-debug (aver (= (length new) (1- old-length)))
150 (cond (new
151 (setf (gethash key table) new))
153 (setf (car freelist) (delete key (car freelist) :count 1))
154 (remhash key table)))))
156 (defun find-in-freelist (size test)
157 (let* ((freelist *immobile-freelist*)
158 (hole-size
159 (if (eq test '<=)
160 (let ((sizes (member size (car freelist) :test '<=)))
161 (unless sizes
162 (return-from find-in-freelist nil))
163 (car sizes))
164 size))
165 (found (car (gethash hole-size (cdr freelist)))))
166 (when found
167 (remove-from-freelist found))
168 found))
170 (defun set-immobile-space-free-pointer (free-ptr)
171 (declare (type (and fixnum unsigned-byte) free-ptr))
172 (setq *immobile-space-free-pointer* (int-sap free-ptr))
173 ;; When the free pointer is not page-aligned - it usually won't be -
174 ;; then we create an unboxed array from the pointer to the page end
175 ;; so that it appears as one contiguous object when scavenging.
176 ;; instead of a bunch of cons cells.
177 (when (logtest free-ptr (1- immobile-card-bytes))
178 (let ((n-trailing-bytes
179 (- (nth-value 1 (ceiling free-ptr immobile-card-bytes)))))
180 (setf (sap-ref-word (int-sap free-ptr) 0) simple-array-fixnum-widetag
181 (sap-ref-word (int-sap free-ptr) n-word-bytes)
182 ;; Convert bytes to words, subtract the header and vector length.
183 (ash (- (ash n-trailing-bytes (- word-shift)) 2)
184 n-fixnum-tag-bits)))))
186 (defun unallocate (hole)
187 #!+immobile-space-debug
188 (awhen *in-use-bits* (mark-range it hole (hole-size hole) nil))
189 (let* ((hole-end (hole-end-address hole))
190 (end-is-free-ptr (eql hole-end (sap-int *immobile-space-free-pointer*))))
191 ;; First, ensure that no page's scan-start points to this hole.
192 ;; For smaller-than-page objects, this will do nothing if the hole
193 ;; was not the scan-start. For larger-than-page, we have to update
194 ;; a range of pages. Example:
195 ;; | page1 | page2 | page3 | page4 |
196 ;; |-- hole A ------ | -- hole B --
197 ;; If page1 had an object preceding the hole, then it is not empty,
198 ;; but if it pointed to the hole, and the hole extended to the end
199 ;; of the first page, then that page is empty.
200 ;; Pages (1+ first-page) through (1- last-page) inclusive
201 ;; must become empty. last-page may or may not be depending
202 ;; on whether another object can be found on it.
203 (let ((first-page (varyobj-page-index hole))
204 (last-page (varyobj-page-index (1- hole-end))))
205 (when (and (eql (varyobj-page-scan-start first-page) hole)
206 (< first-page last-page))
207 (setf (deref varyobj-page-scan-start-offset first-page) 0))
208 (loop for page from (1+ first-page) below last-page
209 do (setf (deref varyobj-page-scan-start-offset page) 0))
210 ;; Only touch the offset for the last page if it pointed to this hole.
211 ;; If the following object is a hole that is in the pending free list,
212 ;; it's ok, but if it's a hole that is already in the freelist,
213 ;; it's not OK, so look beyond that object. We don't have to iterate,
214 ;; since there can't be two consecutive holes - so it's either the
215 ;; object after this hole, or the one after that.
216 (when (eql (varyobj-page-scan-start last-page) hole)
217 (let* ((page-end (varyobj-page-address (1+ last-page)))
218 (new-scan-start (cond (end-is-free-ptr page-end)
219 ((freed-hole-p hole-end)
220 (hole-end-address hole-end))
221 (t hole-end))))
222 (setf (deref varyobj-page-scan-start-offset last-page)
223 (if (< new-scan-start page-end)
224 ;; Compute new offset backwards relative to the page end.
225 (/ (- page-end new-scan-start) (* 2 n-word-bytes))
226 0))))) ; Page becomes empty
228 (unless *immobile-freelist*
229 (setf *immobile-freelist* (cons nil (make-hash-table :test #'eq))))
231 ;; find-preceding is the most expensive operation in this sequence
232 ;; of steps. Not sure how to improve it, but I doubt it's a problem.
233 (let* ((predecessor (find-preceding-object hole))
234 (pred-is-free (and (not (eql predecessor 0))
235 (freed-hole-p predecessor))))
236 (when pred-is-free
237 (remove-from-freelist predecessor)
238 (setf hole predecessor))
239 (when end-is-free-ptr
240 ;; Give back space below the free pointer for better space conservation.
241 ;; Consider when the hole touching the free pointer is equal in size
242 ;; to another hole that could have been used instead. Taking space at
243 ;; the free pointer diminishes the opportunity to use the frontier
244 ;; to later allocate a larger object that would not have fit
245 ;; into any existing hole.
246 (set-immobile-space-free-pointer hole)
247 (return-from unallocate))
248 (let* ((successor hole-end)
249 (succ-is-free (freed-hole-p successor)))
250 (when succ-is-free
251 (setf hole-end (hole-end-address successor))
252 (remove-from-freelist successor)))
253 ;; The hole must be an integral number of doublewords.
254 (aver (zerop (rem (- hole-end hole) 16)))
255 (setf (hole-size hole) (- hole-end hole))))
256 (add-to-freelist hole))
258 (defun allocate-immobile-bytes (n-bytes word0 word1 lowtag)
259 (declare (type (and fixnum unsigned-byte) n-bytes))
260 (setq n-bytes (logandc2 (+ n-bytes (1- (* 2 n-word-bytes)))
261 (1- (* 2 n-word-bytes))))
262 ;; Can't allocate fewer than 4 words due to min hole size.
263 (aver (>= n-bytes (* 4 n-word-bytes)))
264 (sb!thread::with-system-mutex (*immobile-space-mutex* :without-gcing t)
265 (unless (zerop varyobj-holes)
266 ;; If deferred sweep needs to happen, do so now.
267 ;; Concurrency could potentially be improved here: at most one thread
268 ;; should do this step, but it doesn't need to be exclusive with GC
269 ;; as long as we can atomically pop items off the list of holes.
270 (let ((hole-addr varyobj-holes))
271 (setf varyobj-holes 0)
272 (loop
273 (let ((next (sap-ref-word (int-sap hole-addr)
274 (ash code-debug-info-slot word-shift))))
275 (setf (sap-ref-word (int-sap hole-addr)
276 (ash code-debug-info-slot word-shift))
277 nil-value)
278 (unallocate hole-addr)
279 (if (eql (setq hole-addr next) 0) (return))))))
280 (let ((addr
281 (or (and *immobile-freelist*
282 (or (find-in-freelist n-bytes '=) ; 1. Exact match?
283 ;; 2. Try splitting a hole, adding some slack so that
284 ;; both pieces can potentially be used.
285 (let ((found (find-in-freelist (+ n-bytes 192) '<=)))
286 (when found
287 (let* ((actual-size (hole-size found))
288 (remaining (- actual-size n-bytes)))
289 (aver (zerop (rem actual-size 16)))
290 (setf (hole-size found) remaining) ; Shorten the lower piece
291 (add-to-freelist found)
292 (+ found remaining)))))) ; Consume the upper piece
293 ;; 3. Extend the frontier.
294 (let* ((addr (sap-int *immobile-space-free-pointer*))
295 (free-ptr (+ addr n-bytes)))
296 ;; The last page can't be used, because GC uses it as scratch space.
297 (when (> free-ptr (- immobile-space-end immobile-card-bytes))
298 (format t "~&Immobile space exhausted~%")
299 (sb!impl::%halt))
300 (set-immobile-space-free-pointer free-ptr)
301 addr))))
302 (aver (not (logtest addr lowtag-mask))) ; Assert proper alignment
303 ;; Compute the start and end of the first page consumed.
304 (let* ((page-start (logandc2 addr (1- immobile-card-bytes)))
305 (page-end (+ page-start immobile-card-bytes))
306 (index (varyobj-page-index addr))
307 (obj-end (+ addr n-bytes)))
308 ;; Mark the page as being used by a nursery object.
309 (setf (deref varyobj-page-header-gens index)
310 (logior (deref varyobj-page-header-gens index) 1))
311 ;; On the object's first page, set the scan start only if addr
312 ;; is lower than the current page-scan-start object.
313 ;; Note that offsets are expressed in doublewords backwards from
314 ;; page end, so that we can direct the scan start to any doubleword
315 ;; on the page or in the preceding 1MiB (approximately).
316 (when (< addr (varyobj-page-scan-start index))
317 (setf (deref varyobj-page-scan-start-offset index)
318 (ash (- page-end addr) (- (1+ word-shift)))))
319 ;; On subsequent pages, always set the scan start, since there can not
320 ;; be a lower-addressed object touching those pages.
321 (loop
322 (setq page-start page-end)
323 (incf page-end immobile-card-bytes)
324 (incf index)
325 (when (>= page-start obj-end) (return))
326 (setf (deref varyobj-page-scan-start-offset index)
327 (ash (- page-end addr) (- (1+ word-shift))))))
328 #!+immobile-space-debug ; "address sanitizer"
329 (awhen *in-use-bits* (mark-range it addr n-bytes t))
330 (setf (sap-ref-word (int-sap addr) 0) word0
331 (sap-ref-word (int-sap addr) n-word-bytes) word1)
332 ;; 0-fill the remainder of the object
333 (#!+64-bit system-area-ub64-fill
334 #!-64-bit system-area-ub32-fill
335 0 (int-sap addr) 2 (- (ash n-bytes (- word-shift)) 2))
336 (%make-lisp-obj (logior addr lowtag)))))
338 (defun allocate-immobile-vector (widetag length words)
339 (allocate-immobile-bytes (pad-data-block (+ words vector-data-offset))
340 widetag
341 (fixnumize length)
342 other-pointer-lowtag))
344 (defun allocate-immobile-simple-vector (n-elements)
345 (allocate-immobile-vector simple-vector-widetag n-elements n-elements))
346 (defun allocate-immobile-bit-vector (n-elements)
347 (allocate-immobile-vector simple-bit-vector-widetag n-elements
348 (ceiling n-elements n-word-bits)))
349 (defun allocate-immobile-byte-vector (n-elements)
350 (allocate-immobile-vector simple-array-unsigned-byte-8-widetag n-elements
351 (ceiling n-elements n-word-bytes)))
352 (defun allocate-immobile-word-vector (n-elements)
353 (allocate-immobile-vector #!+64-bit simple-array-unsigned-byte-64-widetag
354 #!-64-bit simple-array-unsigned-byte-32-widetag
355 n-elements n-elements))
357 ;;; This is called when we're already inside WITHOUT-GCing
358 (defun allocate-immobile-code (n-boxed-words n-unboxed-bytes)
359 (let* ((unrounded-header-n-words (+ code-constants-offset n-boxed-words))
360 (rounded-header-words (* 2 (ceiling unrounded-header-n-words 2)))
361 (total-bytes (+ (* rounded-header-words n-word-bytes)
362 (logandc2 (+ n-unboxed-bytes lowtag-mask) lowtag-mask)))
363 (code (allocate-immobile-bytes
364 total-bytes
365 (logior (ash rounded-header-words n-widetag-bits) code-header-widetag)
366 (ash n-unboxed-bytes n-fixnum-tag-bits)
367 other-pointer-lowtag)))
368 (setf (%code-debug-info code) nil)
369 code))
371 (deftype immobile-subspaces ()
372 '(member :fixed :variable))
374 (declaim (inline immobile-subspace-bounds))
375 ;;; Return fixnums in the same fashion as %SPACE-BOUNDS.
376 (defun immobile-subspace-bounds (subspace)
377 (case subspace
378 (:fixed (values (%make-lisp-obj immobile-space-start)
379 (%make-lisp-obj (sap-int *immobile-fixedobj-free-pointer*))))
380 (:variable (values (%make-lisp-obj (+ immobile-space-start
381 immobile-fixedobj-subspace-size))
382 (%make-lisp-obj (sap-int *immobile-space-free-pointer*))))))
384 (declaim (ftype (sfunction (function &rest immobile-subspaces) null)
385 map-immobile-objects))
386 (defun map-immobile-objects (function &rest subspaces) ; Perform no filtering
387 (do-rest-arg ((subspace) subspaces)
388 (multiple-value-bind (start end) (immobile-subspace-bounds subspace)
389 (map-objects-in-range function start end))))
391 (declaim (ftype (function (immobile-subspaces) (values t t t &optional))
392 immobile-fragmentation-information))
393 (defun immobile-fragmentation-information (subspace)
394 (binding* (((start free-pointer) (immobile-subspace-bounds subspace))
395 (used-bytes (ash (- free-pointer start) n-fixnum-tag-bits))
396 (holes '())
397 (hole-bytes 0))
398 (map-immobile-objects
399 (lambda (obj type size)
400 (declare (ignore type))
401 (let ((address (logandc2 (get-lisp-obj-address obj) lowtag-mask)))
402 (when (case subspace
403 (:fixed (consp obj))
404 (:variable (hole-p address)))
405 (push (cons address size) holes)
406 (incf hole-bytes size))))
407 subspace)
408 (values holes hole-bytes used-bytes)))
410 (defun show-fragmentation (&key (subspaces '(:fixed :variable))
411 (stream *standard-output*))
412 (dolist (subspace subspaces)
413 (format stream "~(~A~) subspace fragmentation:~%" subspace)
414 (multiple-value-bind (holes hole-bytes total-space-used)
415 (immobile-fragmentation-information subspace)
416 (loop for (start . size) in holes
417 do (format stream "~2@T~X..~X ~8:D~%" start (+ start size) size))
418 (format stream "~2@T~18@<~:D hole~:P~> ~8:D (~,2,2F% of ~:D ~
419 bytes used)~%"
420 (length holes) hole-bytes
421 (/ hole-bytes total-space-used) total-space-used))))
423 ) ; end PROGN