gc.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. ; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
  2. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  3. ; Authors: Richard Kelsey, Jonathan Rees, David Frese, Mike Sperber,
  4. ; Martin Gasbichler, Marcus Crestani
  5. ; Collector
  6. ; (S48-TRACE-LOCATIONS! start end) ; trace all roots
  7. ; (S48-TRACE-VALUE value) => copied value
  8. ; (S48-TRACE-STOB-CONTENTS! stob)
  9. (define *gc-count* 0)
  10. (define (s48-gc-count) *gc-count*)
  11. (define *gc-seconds* 0)
  12. (define *gc-mseconds* 0)
  13. (define (s48-gc-run-time)
  14. (values *gc-seconds* *gc-mseconds*))
  15. (define (s48-collect force-major?)
  16. (receive (start-seconds start-mseconds) (run-time)
  17. (set! *from-begin* (heap-begin))
  18. (set! *from-end* (heap-limit))
  19. (swap-spaces)
  20. (set-heap-pointer! (heap-begin))
  21. (set! *weak-pointer-hp* null-address)
  22. (s48-gc-root) ; trace the interpreter's roots
  23. (do-gc)
  24. (clean-weak-pointers)
  25. (s48-post-gc-cleanup #t ; it's always a major collection
  26. (in-trouble?))
  27. (set! *gc-count* (+ *gc-count* 1))
  28. (receive (end-seconds end-mseconds) (run-time)
  29. (if (>= end-mseconds start-mseconds)
  30. (begin
  31. (set! *gc-seconds* (+ *gc-seconds*
  32. (- end-seconds start-seconds)))
  33. (set! *gc-mseconds* (+ *gc-mseconds*
  34. (- end-mseconds start-mseconds))))
  35. (begin
  36. (set! *gc-seconds* (+ *gc-seconds*
  37. (- (- end-seconds start-seconds) 1)))
  38. (set! *gc-mseconds* (+ *gc-mseconds*
  39. (- (+ 1000 end-mseconds)
  40. start-mseconds))))))))
  41. (define *from-begin*)
  42. (define *from-end*)
  43. (define (in-oldspace? descriptor)
  44. (and (stob? descriptor)
  45. (let ((a (address-after-header descriptor)))
  46. (and (address>= a *from-begin*)
  47. (address< a *from-end*)))))
  48. (define (s48-trace-value stob)
  49. (if (in-oldspace? stob)
  50. (copy-object stob)
  51. stob))
  52. ; Scan the heap, copying pointed to objects, starting from START. Quit once
  53. ; the scanning pointer catches up with the heap pointer.
  54. (define (do-gc)
  55. (let loop ((start (heap-begin)))
  56. (let ((end (heap-pointer)))
  57. (s48-trace-locations! start end)
  58. (cond ((< (s48-available) 0)
  59. (error "GC error: ran out of space in new heap"))
  60. ((address< end (heap-pointer))
  61. (loop end))))))
  62. (define (s48-trace-stob-contents! stob)
  63. (let ((start (address-after-header stob))
  64. (size (bytes->a-units (header-length-in-bytes (stob-header stob)))))
  65. (s48-trace-locations! start (address+ start size))))
  66. ; Copy everything pointed to from somewhere between START (inclusive)
  67. ; and END (exclusive).
  68. (define (s48-trace-locations! start end)
  69. (let loop ((addr start) (frontier (heap-pointer)))
  70. (if (address< addr end)
  71. (let ((thing (fetch addr))
  72. (next (address1+ addr)))
  73. (cond ((header? thing)
  74. (cond ((b-vector-header? thing)
  75. (loop (address+ next (header-length-in-a-units thing))
  76. frontier))
  77. ((continuation-header? thing)
  78. (let ((size (header-length-in-a-units thing)))
  79. (set-heap-pointer! frontier)
  80. (trace-continuation next size)
  81. (loop (address+ next size)
  82. (heap-pointer))))
  83. ((transport-link-cell-header? thing)
  84. (let ((size (header-length-in-a-units thing)))
  85. (begin
  86. (set-heap-pointer! frontier)
  87. (trace-transport-link-cell next size)
  88. (loop (address+ next size)
  89. (heap-pointer)))))
  90. (else
  91. (loop next frontier))))
  92. ((in-oldspace? thing)
  93. (receive (new-thing frontier)
  94. (real-copy-object thing frontier)
  95. (store! addr new-thing)
  96. (loop next frontier)))
  97. (else
  98. (loop next frontier))))
  99. (set-heap-pointer! frontier))))
  100. ; 0) ; for the type-checker
  101. ; Copy THING if it has not already been copied.
  102. (define (copy-object thing)
  103. (receive (new-thing new-hp)
  104. (real-copy-object thing (heap-pointer))
  105. (set-heap-pointer! new-hp)
  106. new-thing))
  107. ; Non-heap-pointer version for better code in TRACE-LOCATIONS
  108. (define (real-copy-object thing frontier)
  109. (let ((h (stob-header thing)))
  110. (cond ((stob? h) ;***Broken heart
  111. ;; (assert (in-newspace? h))
  112. (values h frontier))
  113. ((and (vm-eq? weak-pointer-header h)
  114. (in-oldspace? (fetch (address-after-header thing))))
  115. (copy-weak-pointer thing frontier))
  116. (else
  117. (store! frontier h)
  118. (let* ((data-addr (address+ frontier (cells->a-units stob-overhead)))
  119. (new (address->stob-descriptor data-addr)))
  120. (stob-header-set! thing new) ;***Break heart
  121. (copy-memory! (address-after-header thing)
  122. data-addr
  123. (header-length-in-bytes h))
  124. (values new
  125. (address+ data-addr (header-length-in-a-units h))))))))
  126. (define (s48-extant? thing)
  127. (or (not (stob? thing))
  128. (not (in-oldspace? thing))
  129. (stob? (stob-header thing))))
  130. ;----------------
  131. ; Weak pointers
  132. ;
  133. ; Weak pointers are copied into contiguous blocks so that they can be
  134. ; scanned after the main GC has finished. They have their own heap pointer
  135. ; and heap limit.
  136. (define *weak-pointer-hp*)
  137. (define *weak-pointer-limit*)
  138. ; header + one slot
  139. (define weak-pointer-size 2)
  140. ; The number of weak pointers in each block.
  141. (define weak-pointer-alloc-count 128)
  142. ; The size of a block of weak pointers.
  143. (define weak-pointer-alloc-quantum
  144. (cells->a-units (* weak-pointer-alloc-count weak-pointer-size)))
  145. ; Used both to detect weak pointers and for setting the headers when the
  146. ; weak-pointer blocks are scanned.
  147. (define weak-pointer-header
  148. (make-header (enum stob weak-pointer) (cells->bytes (- weak-pointer-size 1))))
  149. ; A header used to stop the GC from scanning weak-pointer blocks.
  150. (define weak-alloc-area-header
  151. (make-header (enum stob byte-vector)
  152. (cells->bytes (- (* weak-pointer-alloc-count weak-pointer-size)
  153. 1)))) ; don't count the header
  154. (define (copy-weak-pointer weak frontier)
  155. (let ((frontier (if (or (null-address? *weak-pointer-hp*)
  156. (address>= *weak-pointer-hp* *weak-pointer-limit*))
  157. (allocate-more-weak-pointer-space frontier)
  158. frontier)))
  159. (let ((new (address->stob-descriptor
  160. (address+ *weak-pointer-hp* (cells->a-units stob-overhead)))))
  161. (store! (address1+ *weak-pointer-hp*) (fetch (address-after-header weak)))
  162. (set! *weak-pointer-hp* (address2+ *weak-pointer-hp*))
  163. (stob-header-set! weak new) ;***Break heart
  164. (values new frontier))))
  165. ; The weak pointer blocks are linked in their third slot (= the header space
  166. ; of the second weak pointer). The header for the first weak pointer contains
  167. ; a header for the block, and the value slots contain the (untraced) values.
  168. (define (allocate-more-weak-pointer-space frontier)
  169. (let ((old *weak-pointer-hp*)
  170. (new-frontier (address+ frontier weak-pointer-alloc-quantum)))
  171. (set! *weak-pointer-hp* frontier)
  172. (set! *weak-pointer-limit* new-frontier)
  173. (store! *weak-pointer-hp* weak-alloc-area-header)
  174. (store! (address2+ *weak-pointer-hp*) (address->integer old))
  175. new-frontier))
  176. ; If any weak pointers were found, then get the limits of the most recently
  177. ; allocated block and scan it and the rest of the blocks. Put a string header
  178. ; on the unused portion of the block the most recent block.
  179. (define (clean-weak-pointers)
  180. (if (not (null-address? *weak-pointer-hp*))
  181. (let ((start (address- *weak-pointer-limit* weak-pointer-alloc-quantum))
  182. (end *weak-pointer-hp*))
  183. (scan-weak-pointer-blocks start end)
  184. (if (not (address>= end *weak-pointer-limit*))
  185. (let ((unused-portion (address-difference *weak-pointer-limit*
  186. (address1+ end))))
  187. (store! end (make-header (enum stob byte-vector)
  188. (cells->bytes
  189. (a-units->cells unused-portion)))))))))
  190. (define (scan-weak-pointer-blocks start end)
  191. (let loop ((start start) (end end))
  192. (let ((next (integer->address (fetch (address2+ start)))))
  193. (scan-weak-pointer-block start end)
  194. (if (not (null-address? next))
  195. (loop (address- next weak-pointer-alloc-quantum) next)))))
  196. ; Go from START to END putting headers on the weak pointers and seeing if
  197. ; their contents were traced.
  198. (define (scan-weak-pointer-block start end)
  199. (do ((scan start (address2+ scan)))
  200. ((address>= scan end))
  201. (store! scan weak-pointer-header)
  202. (let ((value (fetch (address1+ scan))))
  203. (if (and (in-oldspace? value)
  204. (stob? value))
  205. (store! (address1+ scan)
  206. (let ((h (stob-header value)))
  207. (if (stob? h) h false)))))))
  208. ; transport link cells
  209. ;; Due to circular dependencies when this file tries to open the
  210. ;; structure primitivies where the setters and mutators for PAIRs and
  211. ;; TRANSPORT-LINK-CELLs are. So there is no other way than defining
  212. ;; the needed setters, mutators, and predicates another time:
  213. ;; record selector, mutator, and predicate maker
  214. (define (make-gc-selector index)
  215. (lambda (record)
  216. (fetch (address+ (address-after-header record) (cells->a-units index)))))
  217. (define (make-gc-mutator index)
  218. (lambda (record object)
  219. (store! (address+ (address-after-header record)
  220. (cells->a-units index)) object)))
  221. (define (make-gc-predicate stob-type)
  222. (lambda (thing)
  223. (if (stob? thing)
  224. (= (header-type (stob-header thing))
  225. stob-type)
  226. #f)))
  227. (define gc-pair? (make-gc-predicate (enum stob pair)))
  228. (define gc-car (make-gc-selector 0))
  229. (define gc-cdr (make-gc-selector 1))
  230. (define gc-set-car! (make-gc-mutator 0))
  231. (define gc-set-cdr! (make-gc-mutator 1))
  232. (define gc-tlc-key (make-gc-selector 0))
  233. (define gc-tlc-tconc (make-gc-selector 2))
  234. (define gc-set-tlc-tconc! (make-gc-mutator 2))
  235. ;; follow already forwarded objects
  236. (define (follow thing)
  237. (if (and (stob? thing)
  238. (stob? (stob-header thing)))
  239. (stob-header thing)
  240. thing))
  241. ;; trace a transport link cell
  242. (define (trace-transport-link-cell contents-pointer size)
  243. (s48-trace-locations! contents-pointer (address+ contents-pointer size))
  244. (let* ((tlc (address->stob-descriptor contents-pointer))
  245. (tconc (gc-tlc-tconc tlc)))
  246. (if (and (gc-pair? tconc) (stob? (gc-tlc-key tlc)))
  247. ;; The tconc's car and cdr may already be forwarded, so
  248. ;; follow them, if needed.
  249. (let ((tconc-car (follow (gc-car tconc)))
  250. (tconc-cdr (follow (gc-cdr tconc))))
  251. (if (and (gc-pair? tconc-car)
  252. (gc-pair? tconc-cdr))
  253. ;; the tlc's tconc field is a valid tconc queue
  254. ;; now allocate a new pair in the new space
  255. ;; that will become the tconc's new last element
  256. (let ((pair-address (heap-pointer)))
  257. (store! pair-address (make-header (enum stob pair) (cells->bytes 2)))
  258. (let* ((newpair (address->stob-descriptor
  259. (address+ pair-address (cells->a-units stob-overhead))))
  260. (new-frontier (address+ pair-address (cells->a-units 3))))
  261. (if (gc-pair? newpair)
  262. (begin
  263. ;; initialize the new pair's fields with #f
  264. (gc-set-car! newpair (enter-boolean #f))
  265. (gc-set-cdr! newpair (enter-boolean #f))
  266. ;; enqueue the tlc in the tconc queue
  267. (gc-set-car! tconc-cdr tlc)
  268. (gc-set-cdr! tconc-cdr newpair)
  269. (gc-set-cdr! tconc newpair)
  270. ;; reset the tlc's tconc field
  271. (gc-set-tlc-tconc! tlc (enter-boolean #f))
  272. (set-heap-pointer! new-frontier))))))))))
  273. (define (transport-link-cell-header? x)
  274. (= (header-type x) (enum stob transport-link-cell)))