stack-gc.scm 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. ; Called when returning off of the end of the stack.
  2. (define (get-continuation-from-heap)
  3. *heap-continuation*)
  4. ; Copy CONT from heap onto stack just above *BOTTOM-OF-STACK*, linking it
  5. ; to *BOTTOM-OF-STACK* and *BOTTOM-OF-STACK* to CONT's continuation.
  6. ; There are STACK-ARG-COUNT arguments on top of the stack that need to be
  7. ; preserved.
  8. (define (copy-continuation-from-heap! cont stack-arg-count)
  9. (assert (continuation? cont))
  10. (let* ((stack-size (- (continuation-length cont) 2))
  11. (new-cont (address- *bottom-of-stack* (cells->a-units stack-size))))
  12. (copy-args-above-incoming-cont! new-cont stack-arg-count)
  13. (set! *cont* new-cont)
  14. (copy-memory! (address+ (address-after-header cont)
  15. (cells->a-units 3)) ; skip PC, code, cont
  16. (address1+ new-cont)
  17. (cells->bytes (- stack-size 1))) ; skip code pointer
  18. (set-stack-cont-code-pointer!
  19. new-cont
  20. (address+ (address-after-header (continuation-code cont))
  21. (extract-fixnum (continuation-pc cont))))
  22. (set! *heap-continuation* (continuation-cont cont))
  23. new-cont))
  24. ; Move STACK-ARG-COUNT values pointed to by *STACK* on top of NEW-CONT.
  25. (define (copy-args-above-incoming-cont! new-cont stack-arg-count)
  26. (if (= stack-arg-count 0)
  27. (set! *stack* new-cont)
  28. (let ((new-stack (address- new-cont (cells->a-units stack-arg-count))))
  29. (if (address< new-stack *stack*)
  30. (begin
  31. (copy-memory! *stack*
  32. new-stack
  33. (cells->bytes stack-arg-count))
  34. (set! *stack* new-stack))))))
  35. ;----------------
  36. ; Copying the stack into the heap because there is no more room on the
  37. ; stack. This preserves the continuation and then moves any arguments
  38. ; down on top of the current continuation.
  39. (define (s48-copy-stack-into-heap)
  40. (let ((key (ensure-space (current-stack-size)))
  41. (arg-count (operands-on-stack))
  42. (top *stack*))
  43. (preserve-continuation key)
  44. (set! *stack* *cont*)
  45. (move-stack-arguments! top arg-count)))
  46. ; Returns the number of arguments pushed above the topmost object on the stack.
  47. (define (operands-on-stack)
  48. (a-units->cells (address-difference *cont* *stack*)))
  49. ; Move NARGS values from TOP-OF-ARGS to the current top of the stack.
  50. (define (move-stack-arguments! top-of-args nargs)
  51. (let ((start-arg (address+ top-of-args (cells->a-units (- nargs 1)))))
  52. (do ((loc (the-pointer-before *stack*) (the-pointer-before loc))
  53. (arg start-arg (the-pointer-before arg)))
  54. ((address< arg top-of-args)
  55. (add-cells-to-stack! nargs))
  56. (store! loc (fetch arg)))))
  57. ; Copy NARGS arguments from the top of the stack to just above CONT, if
  58. ; they are not already there.
  59. (define (move-args-above-cont! nargs)
  60. (let ((top-of-args (pointer-to-stack-arguments)))
  61. (if (not (address= *cont*
  62. (address+ top-of-args
  63. (cells->a-units nargs))))
  64. (begin
  65. (set! *stack* *cont*)
  66. (move-stack-arguments! top-of-args nargs)))))
  67. ; Migrating the current continuation into the heap, saving the environment
  68. ; first. The heap space needed is no more than the current stack size.
  69. (define current-continuation-size current-stack-size)
  70. (define (copy-current-continuation-to-heap key)
  71. (preserve-continuation key))
  72. (define (preserve-continuation key)
  73. (if (false? (address->integer *cont*))
  74. false
  75. (really-preserve-continuation key)))
  76. (define (really-preserve-continuation key)
  77. (if (not (address= *cont* *bottom-of-stack*))
  78. (let ((temp *heap-continuation*))
  79. (let loop ((cont *cont*) (previous false))
  80. (if (address= cont *bottom-of-stack*)
  81. (set-continuation-cont! previous temp)
  82. (receive (new next)
  83. (copy-continuation-to-heap! cont key)
  84. (if (continuation? previous)
  85. (set-continuation-cont! previous new)
  86. (set! *heap-continuation* new))
  87. (loop next new))))
  88. (set! *cont* *bottom-of-stack*)))
  89. *heap-continuation*)
  90. ; The continuation has three values added: PC, code vector, and the next
  91. ; continuation (filled in by REALLY-PRESERVE-CONTINUATION).
  92. (define (copy-continuation-to-heap! cont key)
  93. (let* ((size (stack-continuation-size cont))
  94. (new (make-continuation (+ size 3) key)))
  95. (receive (pc byte-vector)
  96. (decode-return-pointer cont)
  97. (copy-memory! (address1+ cont)
  98. (address+ (address-after-header new)
  99. (cells->a-units 3))
  100. (cells->bytes size))
  101. (set-continuation-pc! new (enter-fixnum pc))
  102. (set-continuation-code! new byte-vector))
  103. (values new
  104. (address+ cont (cells->bytes (+ size 1))))))
  105. ; Use the offset found three bytes before POINTER to convert POINTER into
  106. ; a program counter and a code vector.
  107. (define (decode-return-pointer cont)
  108. (let* ((pointer (stack-cont-code-pointer cont))
  109. (pc (fetch-two-bytes (address- pointer 5))))
  110. (values pc
  111. (address->stob-descriptor (address- pointer pc)))))
  112. ; Returns the two-byte value found just ahead of where CONT's code pointer
  113. ; points. The RAW part refers to the fact that we don't check for a zero
  114. ; size, which is an escape value.
  115. (define (raw-stack-continuation-size cont)
  116. (fetch-two-bytes (address- (stack-cont-code-pointer cont)
  117. 2)))
  118. ; Get the raw size and check for zero, which means that the real size is found
  119. ; just after the VM's saved registers. This is used for interrupt and exception
  120. ; continuations.
  121. (define (stack-continuation-size cont)
  122. (let ((size (raw-stack-continuation-size cont)))
  123. (if (= size #xffff)
  124. (extract-fixnum (fetch (address1+ cont)))
  125. size)))
  126. ; Silly utility that should be elsewhere.
  127. (define (fetch-two-bytes pointer)
  128. (+ (shift-left (fetch-byte pointer) 8)
  129. (fetch-byte (address+ pointer 1))))
  130. ;----------------
  131. ; Tracing the stack for garbage collection - first trace any arguments pushed
  132. ; above the current continuation, then loop down the continuations, tracing
  133. ; each one along with its environment (if the environment has not yet been
  134. ; done).
  135. (define *stack-warning?* #t)
  136. (define (trace-stack)
  137. (if *stack-warning?*
  138. (stack-warning-message))
  139. (s48-trace-locations! *stack*
  140. (address+ *stack*
  141. (cells->a-units (operands-on-stack))))
  142. (let loop ((cont *cont*))
  143. (receive (pc code-vector)
  144. (decode-return-pointer cont)
  145. (trace-stack-continuation-contents cont code-vector pc)
  146. (if (not (address= cont *bottom-of-stack*))
  147. (loop (stack-cont-continuation cont)))))
  148. (set! *heap-continuation* (s48-trace-value *heap-continuation*)))
  149. ; We cannot count on any headers being in place, so we can't use
  150. ; the standard accessors because they have assertions that look at
  151. ; the headers.
  152. (define (trace-stack-continuation-contents cont code-vector pc)
  153. (let ((code-pointer (stack-cont-code-pointer cont)))
  154. (let ((new-code (s48-trace-value code-vector))
  155. (mask-size (fetch-byte (address- code-pointer 3)))
  156. (contents-pointer (address1+ cont)))
  157. (set-stack-cont-code-pointer! cont
  158. (address+ (address-after-header new-code)
  159. pc))
  160. (if (= mask-size 0)
  161. (s48-trace-locations! contents-pointer
  162. (address+ contents-pointer
  163. (cells->a-units
  164. (stack-continuation-size cont))))
  165. (s48-trace-continuation-contents! contents-pointer
  166. code-pointer
  167. mask-size))
  168. 0))) ; Argh! PreScheme compiler problem.
  169. ; This should be in arch.scm
  170. (define template-code-index 0)
  171. (define (raw-template-code template)
  172. (fetch (address+ (address-after-header template)
  173. template-code-index)))
  174. ; Reverse eta to allow redefinition during debugging.
  175. (add-gc-root! (lambda () (trace-stack)))
  176. ;----------------------------------------------------------------
  177. ; Temporary code to check that the stack is okay.
  178. (define (check-stack)
  179. (let lp ((index *stack*))
  180. (if (address< index
  181. (address+ *stack*
  182. (cells->a-units (operands-on-stack))))
  183. (begin
  184. (check-descriptor (fetch index))
  185. (lp (address1+ index)))))
  186. (let loop ((cont *cont*))
  187. (if (not (address= cont *bottom-of-stack*))
  188. (loop (check-continuation cont)))))
  189. (define (check-continuation cont)
  190. (receive (pc code-vector)
  191. (decode-return-pointer cont)
  192. (check-stack-continuation-contents cont code-vector pc))
  193. (stack-cont-continuation cont))
  194. (define (check-stack-continuation-contents cont code-vector pc)
  195. (let ((code-pointer (stack-cont-code-pointer cont)))
  196. (let ((mask-size (fetch-byte (address+ code-pointer gc-mask-size-offset)))
  197. (contents-pointer (address1+ cont)))
  198. (if (= mask-size 0)
  199. (check-locations contents-pointer
  200. (stack-cont-continuation cont))
  201. (check-continuation-contents contents-pointer
  202. code-pointer
  203. mask-size)))))
  204. (define (check-locations start end)
  205. (let loop ((addr start))
  206. (if (address< addr end)
  207. (begin
  208. (check-descriptor (fetch addr))
  209. (loop (address1+ addr))))))
  210. (define (check-continuation-contents contents-pointer code-pointer mask-size)
  211. (let ((mask-pointer (address+ code-pointer (+ gc-mask-offset 1))))
  212. (let byte-loop ((mask-ptr (address- mask-pointer mask-size))
  213. (trace-ptr contents-pointer))
  214. (if (not (address= mask-ptr mask-pointer))
  215. (let bit-loop ((mask (fetch-byte mask-ptr)) (ptr trace-ptr))
  216. (if (= mask 0)
  217. (byte-loop (address+ mask-ptr 1)
  218. (address+ trace-ptr (cells->a-units 8)))
  219. (begin
  220. (if (odd? mask)
  221. (check-descriptor (fetch ptr)))
  222. (bit-loop (arithmetic-shift-right mask 1)
  223. (address1+ ptr)))))))))
  224. (define (odd? x)
  225. (= (bitwise-and x 1)
  226. 1))
  227. (define (check-descriptor x)
  228. (if (or (header? x)
  229. (and (stob? x)
  230. (not (s48-stob-in-heap? x))))
  231. (begin
  232. (write-string "bad descriptor in stack" (current-error-port))
  233. (write-integer x (current-error-port))
  234. (write-integer (fetch (integer->address 0)) (current-error-port))
  235. (unspecific))))