219 lines
6.8 KiB
Scheme
219 lines
6.8 KiB
Scheme
|
; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
|
||
|
|
||
|
; This is file gc.scm.
|
||
|
|
||
|
; Collector
|
||
|
|
||
|
(define (store-next! descriptor)
|
||
|
(store! *hp* descriptor)
|
||
|
(set! *hp* (addr1+ *hp*)))
|
||
|
|
||
|
(define (in-oldspace? descriptor)
|
||
|
(and (stob? descriptor)
|
||
|
(let ((a (address-after-header descriptor)))
|
||
|
(and (addr>= a *oldspace-begin*)
|
||
|
(addr< a *oldspace-end*)))))
|
||
|
|
||
|
; The interface to the GC consists of
|
||
|
; (BEGIN-COLLECTION) ; call first
|
||
|
; (TRACE-VALUE value) => copied value
|
||
|
; (TRACE-LOCATIONS start end) ; trace all pointers,
|
||
|
; (TRACE-IMPURE-AREAS)
|
||
|
; (DO-GC) ; then do the GC,
|
||
|
; (END-COLLECTION) ; and either finish
|
||
|
; (ABORT-COLLECTION) ; or abort
|
||
|
|
||
|
(define *gc-count* 0)
|
||
|
(define (gc-count) *gc-count*)
|
||
|
|
||
|
(define *saved-hp* 0)
|
||
|
(define *saved-limit* 0)
|
||
|
|
||
|
(define (begin-collection)
|
||
|
(swap-spaces)
|
||
|
(set! *saved-limit* *limit*)
|
||
|
(set! *saved-hp* *hp*)
|
||
|
(set! *limit* *newspace-end*)
|
||
|
(set! *hp* *newspace-begin*)
|
||
|
(set! *weak-pointer-hp* -1))
|
||
|
|
||
|
(define (swap-spaces)
|
||
|
(let ((b *newspace-begin*))
|
||
|
(set! *newspace-begin* *oldspace-begin*)
|
||
|
(set! *oldspace-begin* b))
|
||
|
(let ((e *newspace-end*))
|
||
|
(set! *newspace-end* *oldspace-end*)
|
||
|
(set! *oldspace-end* e)))
|
||
|
|
||
|
(define (trace-value value)
|
||
|
(if (in-oldspace? value)
|
||
|
(copy-object value)
|
||
|
value))
|
||
|
|
||
|
(define (trace-impure-areas)
|
||
|
(walk-impure-areas
|
||
|
(lambda (start end)
|
||
|
(trace-locations start end)
|
||
|
#t)))
|
||
|
|
||
|
(define (end-collection)
|
||
|
(set! *gc-count* (+ *gc-count* 1)))
|
||
|
|
||
|
; Undo the effects of the current collection (assuming that it did not
|
||
|
; modify any VM registers or the stack).
|
||
|
|
||
|
(define (abort-collection)
|
||
|
(swap-spaces)
|
||
|
(set! *limit* *saved-limit*)
|
||
|
(set! *hp* *saved-hp*)
|
||
|
(let loop ((addr *newspace-begin*))
|
||
|
(if (addr< addr *hp*)
|
||
|
(let* ((d (fetch addr))
|
||
|
(h (if (header? d)
|
||
|
d
|
||
|
(let ((h (stob-header d)))
|
||
|
(store! addr h) ; mend heart
|
||
|
h))))
|
||
|
(loop (addr1+ (addr+ addr (header-a-units h))))))))
|
||
|
|
||
|
; Scan the heap, copying pointed to objects, starting from START. Quit once
|
||
|
; the scanning pointer catches up with the heap pointer.
|
||
|
|
||
|
(define (do-gc)
|
||
|
(let loop ((start *newspace-begin*))
|
||
|
(let ((end *hp*))
|
||
|
(trace-locations start end)
|
||
|
(cond ((addr>= *hp* *limit*)
|
||
|
(error "GC error: ran out of space in new heap"))
|
||
|
((addr< end *hp*)
|
||
|
(loop end)))))
|
||
|
(clean-weak-pointers))
|
||
|
|
||
|
; Copy everything pointed to from somewhere between START and END.
|
||
|
|
||
|
(define (trace-locations start end)
|
||
|
(let loop ((addr start))
|
||
|
(if (addr< addr end)
|
||
|
(loop (copy-next addr)))))
|
||
|
|
||
|
; Copy the thing pointed to from ADDR, returning the next address to copy.
|
||
|
|
||
|
(define (copy-next addr)
|
||
|
(let ((thing (fetch addr))
|
||
|
(next (addr1+ addr)))
|
||
|
(cond ((b-vector-header? thing)
|
||
|
(addr+ next (header-a-units thing)))
|
||
|
((in-oldspace? thing)
|
||
|
(store! addr (copy-object thing))
|
||
|
next)
|
||
|
(else
|
||
|
next))))
|
||
|
|
||
|
; Copy THING if it has not already been copied.
|
||
|
; It is important that this be in-lined into COPY-NEXT.
|
||
|
|
||
|
(define (copy-object thing)
|
||
|
(let ((h (stob-header thing)))
|
||
|
(cond ((stob? h) ;***Broken heart
|
||
|
;; (assert (in-newspace? h))
|
||
|
h)
|
||
|
((and (vm-eq? weak-pointer-header h)
|
||
|
(in-oldspace? (d-vector-ref thing 0)))
|
||
|
(copy-weak-pointer thing))
|
||
|
(else
|
||
|
(store-next! h)
|
||
|
(let ((new (address->stob-descriptor *hp*)))
|
||
|
(stob-header-set! thing new) ;***Break heart
|
||
|
(let ((new-hp (addr+ *hp* (header-a-units h))))
|
||
|
(do ((o (address-after-header thing) (addr1+ o)))
|
||
|
((addr>= *hp* new-hp))
|
||
|
(store-next! (fetch o))))
|
||
|
new)))))
|
||
|
|
||
|
; Weak pointers are copied into contiguous blocks so that they can be
|
||
|
; scanned after the main GC has finished. They have their own heap pointer
|
||
|
; and heap limit.
|
||
|
|
||
|
(define *weak-pointer-hp* -1)
|
||
|
(define *weak-pointer-limit* 0)
|
||
|
|
||
|
; header + one slot
|
||
|
(define weak-pointer-size 2)
|
||
|
|
||
|
; The number of weak pointers in each block.
|
||
|
(define weak-pointer-alloc-count 128)
|
||
|
|
||
|
; The size of a block of weak pointers.
|
||
|
(define weak-pointer-alloc-quantum
|
||
|
(cells->a-units (* weak-pointer-alloc-count weak-pointer-size)))
|
||
|
|
||
|
; Used both to detect weak pointers and for setting the headers when the
|
||
|
; weak-pointer blocks are scanned.
|
||
|
(define weak-pointer-header
|
||
|
(make-header (enum stob weak-pointer) (cells->bytes (- weak-pointer-size 1))))
|
||
|
|
||
|
; A header used to stop the GC from scanning weak-pointer blocks.
|
||
|
(define weak-alloc-area-header
|
||
|
(make-header (enum stob code-vector)
|
||
|
(cells->bytes (- (* weak-pointer-alloc-count weak-pointer-size)
|
||
|
1)))) ; don't count the header
|
||
|
|
||
|
(define (copy-weak-pointer weak)
|
||
|
(if (or (= -1 *weak-pointer-hp*)
|
||
|
(>= *weak-pointer-hp* *weak-pointer-limit*))
|
||
|
(allocate-more-weak-pointer-space))
|
||
|
(let ((new (address->stob-descriptor (addr1+ *weak-pointer-hp*))))
|
||
|
(store! (addr1+ *weak-pointer-hp*) (d-vector-ref weak 0))
|
||
|
(set! *weak-pointer-hp* (addr1+ (addr1+ *weak-pointer-hp*)))
|
||
|
(stob-header-set! weak new) ;***Break heart
|
||
|
new))
|
||
|
|
||
|
; The weak pointer blocks are linked in their third slot (= the header space
|
||
|
; of the second weak pointer). The header for the first weak pointer contains
|
||
|
; a header for the block, and the value slots contain the (untraced) values.
|
||
|
|
||
|
(define (allocate-more-weak-pointer-space)
|
||
|
(let ((old *weak-pointer-hp*))
|
||
|
(set! *weak-pointer-hp* *hp*)
|
||
|
(set! *hp* (+ *hp* weak-pointer-alloc-quantum))
|
||
|
(set! *weak-pointer-limit* *hp*)
|
||
|
(store! *weak-pointer-hp* weak-alloc-area-header)
|
||
|
(store! (addr1+ (addr1+ *weak-pointer-hp*)) old)))
|
||
|
|
||
|
; If any weak pointers were found, then get the limits of the most recently
|
||
|
; allocated block and scan it and the rest of the blocks. Put a string header
|
||
|
; on the unused portion of the block the most recent block.
|
||
|
|
||
|
(define (clean-weak-pointers)
|
||
|
(if (not (= *weak-pointer-hp* -1))
|
||
|
(let ((start (addr- *weak-pointer-limit* weak-pointer-alloc-quantum))
|
||
|
(end *weak-pointer-hp*))
|
||
|
(scan-weak-pointer-blocks start end)
|
||
|
(if (not (>= end *weak-pointer-limit*))
|
||
|
(let ((unused-portion (addr- *weak-pointer-limit* (addr1+ end))))
|
||
|
(store! end (make-header (enum stob code-vector)
|
||
|
(cells->bytes
|
||
|
(a-units->cells unused-portion)))))))))
|
||
|
|
||
|
(define (scan-weak-pointer-blocks start end)
|
||
|
(let loop ((start start) (end end))
|
||
|
(let ((next (fetch (addr1+ (addr1+ start)))))
|
||
|
(scan-weak-pointer-block start end)
|
||
|
(if (not (= next -1))
|
||
|
(loop (addr- next weak-pointer-alloc-quantum) next)))))
|
||
|
|
||
|
; Go from START to END putting headers on the weak pointers and seeing if
|
||
|
; their contents were traced.
|
||
|
|
||
|
(define (scan-weak-pointer-block start end)
|
||
|
(do ((scan start (addr1+ (addr1+ scan))))
|
||
|
((>= scan end))
|
||
|
(store! scan weak-pointer-header)
|
||
|
(let ((value (fetch (addr1+ scan))))
|
||
|
(if (and (in-oldspace? value)
|
||
|
(stob? value))
|
||
|
(store! (addr1+ scan)
|
||
|
(let ((h (stob-header value)))
|
||
|
(if (stob? h) h false)))))))
|
||
|
|