hoist.scm 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. ;;; Ported from Scheme 48 1.9. See file COPYING for notices and license.
  2. ;;;
  3. ;;; Port Author: Andrew Whatson
  4. ;;;
  5. ;;; Original Authors: Richard Kelsey
  6. ;;;
  7. ;;; scheme48-1.9.2/ps-compiler/prescheme/hoist.scm
  8. ;;;
  9. ;;; Move nested procedures out to top level. We move them all out, then merge
  10. ;;; as many as possible back together (see merge.scm), and finally check to
  11. ;;; see if there are any out-of-scope references.
  12. (define-module (ps-compiler prescheme hoist)
  13. #:use-module (prescheme scheme48)
  14. #:use-module (ps-compiler node node)
  15. #:use-module (ps-compiler node node-util)
  16. #:use-module (ps-compiler node primop)
  17. #:use-module (ps-compiler node variable)
  18. #:use-module (ps-compiler prescheme form)
  19. #:use-module (ps-compiler prescheme type)
  20. #:use-module (ps-compiler prescheme merge)
  21. #:use-module (ps-compiler util util)
  22. #:export (hoist-nested-procedures
  23. check-hoisting))
  24. (define (hoist-nested-procedures forms)
  25. (set! *hoist-index* 0)
  26. (let loop ((forms forms) (done '()))
  27. (if (null? forms)
  28. (reverse done)
  29. (loop (cdr forms)
  30. (let ((form (car forms)))
  31. (if (eq? 'lambda (form-type form))
  32. (append (really-hoist-nested-procedures form)
  33. (cons form done))
  34. (cons form done)))))))
  35. (define (really-hoist-nested-procedures form)
  36. (let ((top (form-value form))
  37. (lambdas (form-lambdas form))
  38. (lambda-parent lambda-env) ;; Rename a couple of handy fields
  39. (lambda-kids lambda-block)
  40. (new-forms '()))
  41. ;; (format #t " ~S: ~S~%" (form-name form) lambdas)
  42. ;; (if (eq? 'read-list (form-name form))
  43. ;; (breakpoint "read-list"))
  44. (receive (procs others)
  45. (find-scoping lambdas
  46. lambda-env set-lambda-env!
  47. lambda-block set-lambda-block!)
  48. (set-form-lambdas! form (cons top (non-proc-lambdas (lambda-kids top))))
  49. (map (lambda (proc)
  50. (let ((var (replace-with-variable proc)))
  51. (make-hoist-form proc
  52. var
  53. (form-name form)
  54. (non-proc-lambdas (lambda-kids proc)))))
  55. (filter (lambda (p)
  56. (not (eq? p top)))
  57. procs)))))
  58. (define (non-proc-lambdas lambdas)
  59. (filter (lambda (l)
  60. (not (or (eq? 'proc (lambda-type l))
  61. (eq? 'known-proc (lambda-type l)))))
  62. lambdas))
  63. (define (make-hoist-form value var hoisted-from lambdas)
  64. (let ((form (make-form var #f #f)))
  65. (set-form-node! form value (cons value lambdas))
  66. (set-form-type! form 'lambda)
  67. (set-variable-flags! var
  68. (cons (cons 'hoisted hoisted-from)
  69. (variable-flags var)))
  70. form))
  71. (define (replace-with-variable node)
  72. (let ((var (make-hoist-variable node)))
  73. (case (primop-id (call-primop (node-parent node)))
  74. ((let)
  75. (substitute-var-for-proc (node-parent node) node var))
  76. ((letrec2)
  77. (substitute-var-for-proc-in-letrec (node-parent node) node var))
  78. (else
  79. (move node
  80. (lambda (node)
  81. (make-reference-node var)))))
  82. var))
  83. (define (make-hoist-variable node)
  84. (cond ((bound-to-variable node)
  85. => (lambda (var)
  86. (make-global-variable (generate-hoist-name (variable-name var))
  87. (variable-type var))))
  88. (else
  89. (let* ((vars (lambda-variables node))
  90. (type (make-arrow-type (map variable-type (cdr vars))
  91. (variable-type (car vars))))
  92. (id (generate-hoist-name (or (lambda-name node) 'hoist))))
  93. (make-global-variable id type)))))
  94. (define (substitute-var-for-proc call node value-var)
  95. (let ((proc (call-arg call 0)))
  96. (really-substitute-var-for-proc proc call node value-var)
  97. (if (null? (lambda-variables proc))
  98. (replace-body call (detach-body (lambda-body proc))))))
  99. (define (substitute-var-for-proc-in-letrec call node value-var)
  100. (let ((proc (node-parent call)))
  101. (really-substitute-var-for-proc proc call node value-var)
  102. (if (null? (cdr (lambda-variables proc)))
  103. (replace-body (node-parent proc)
  104. (detach-body (lambda-body (call-arg call 0)))))))
  105. (define (really-substitute-var-for-proc binder call node value-var)
  106. (let* ((index (node-index node))
  107. (var (list-ref (lambda-variables binder)
  108. (- (node-index node) 1))))
  109. (walk-refs-safely
  110. (lambda (ref)
  111. (replace ref (make-reference-node value-var)))
  112. var)
  113. (remove-variable binder var)
  114. (detach node)
  115. (remove-call-arg call index)))
  116. (define *hoist-index* 0)
  117. (define (generate-hoist-name sym)
  118. (let ((i *hoist-index*))
  119. (set! *hoist-index* (+ i 1))
  120. (concatenate-symbol sym "." i)))
  121. ;;----------------------------------------------------------------
  122. ;; Part 2: checking for variables moved out of scope.
  123. (define (check-hoisting forms)
  124. (let ((forms (filter (lambda (form)
  125. (or (eq? 'merged (form-type form))
  126. (eq? 'lambda (form-type form))))
  127. forms)))
  128. (for-each (lambda (form)
  129. (cond ((flag-assq 'hoisted (variable-flags (form-var form)))
  130. => (lambda (p)
  131. (check-hoisted-form form (cdr p))))))
  132. forms)))
  133. (define (check-hoisted-form form hoisted-from)
  134. (let ((vars (find-unbound-variables (form-value form) (form-head form))))
  135. (if (not (null? vars))
  136. (user-error "Procedure ~S in ~S is closed over: ~S~%"
  137. (form-name form)
  138. hoisted-from
  139. (map variable-name vars)))))
  140. (define (find-unbound-variables node form)
  141. (let ((unbound '())
  142. (mark (cons 0 0)))
  143. (let label ((n node))
  144. (cond ((lambda-node? n)
  145. (let ((flag (node-flag n)))
  146. (set-node-flag! n mark)
  147. (label (lambda-body n))
  148. (set-node-flag! n flag)))
  149. ((call-node? n)
  150. (let ((vec (call-args n)))
  151. (do ((i 0 (+ i 1)))
  152. ((= i (vector-length vec)))
  153. (label (vector-ref vec i)))))
  154. ((reference-node? n)
  155. (let* ((v (reference-variable n))
  156. (b (variable-binder v)))
  157. (cond ((and b
  158. (not (eq? mark (node-flag b)))
  159. (not (variable-flag v)))
  160. (set-variable-flag! v #t)
  161. (set! unbound (cons v unbound))))))))
  162. (filter (lambda (v)
  163. (set-variable-flag! v #f)
  164. (not (eq? form (form-head (node-form (variable-binder v))))))
  165. unbound)))