hoist.scm 5.3 KB

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