join.scm 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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/simp/join.scm
  8. (define-module (ps-compiler simp join)
  9. #:use-module (prescheme scheme48)
  10. #:use-module (ps-compiler node arch)
  11. #:use-module (ps-compiler node let-nodes)
  12. #:use-module (ps-compiler node node)
  13. #:use-module (ps-compiler node node-util)
  14. #:use-module (ps-compiler node primop)
  15. #:use-module (ps-compiler node variable)
  16. #:export (substitute-join-arguments))
  17. ;; Call JOIN-SUBSTITUTE on all variable/value pairs.
  18. (define (substitute-join-arguments lambda-proc call)
  19. (let ((vec (call-args call))
  20. (vars (lambda-variables lambda-proc)))
  21. (do ((vars vars (cdr vars))
  22. (i 1 (+ i 1))
  23. (c? #f (or (join-substitute (car vars) (vector-ref vec i))
  24. c?)))
  25. ((null? vars) c?))))
  26. ;; Does VAL take only one argument and is that argument passed to $TEST?
  27. ;; Is VAR applied to constants?
  28. ;; Then two possiblities are checked for:
  29. ;; Does the tree rooted at the least-common-ancestor of VAR's references
  30. ;; contain no side-effects and necessarily passed control to VAR?
  31. ;; or
  32. ;; Does the join point contain no side-effects above the test?
  33. ;;
  34. ;; If so, make the transformation described below.
  35. (define (join-substitute var val)
  36. (let ((ref (and (lambda-node? val)
  37. (simple-test-procedure val))))
  38. (and ref
  39. (applied-to-useful-value? var ref)
  40. (let ((lca (least-common-ancestor (variable-refs var))))
  41. (cond ((or (suitable-join-conditional? lca var)
  42. (suitable-join-point? val (node-parent ref)))
  43. (really-join-substitute var val lca (node-parent ref))
  44. #t)
  45. (else #f))))))
  46. ;; Check that VAL (a lambda-node) takes one argument, is jumped to, tests its
  47. ;; argument, and that all references to the argument are at or below the test.
  48. (define (simple-test-procedure val)
  49. (let ((vars (lambda-variables val)))
  50. (if (or (null? vars)
  51. (not (null? (cdr vars)))
  52. (not (car vars))
  53. (not (calls-known? val))
  54. (neq? 'jump (lambda-type val)))
  55. #f
  56. (let* ((var (car vars))
  57. (ref (any simple-cond-ref (variable-refs var))))
  58. (if (and ref (all-refs-below? var (node-parent ref)))
  59. ref
  60. #f)))))
  61. (define (simple-cond-ref ref)
  62. (if (primop-conditional? (call-primop (node-parent ref)))
  63. ref
  64. #f))
  65. (define (all-refs-below? var node)
  66. (set-node-flag! node #t)
  67. (set-node-flag! (variable-binder var) #t)
  68. (let ((res (every? (lambda (r)
  69. (eq? node (marked-ancestor r)))
  70. (variable-refs var))))
  71. (set-node-flag! node #f)
  72. (set-node-flag! (variable-binder var) #f)
  73. res))
  74. ;; Is VAR applied to something that can be used to simplify the conditional?
  75. (define (applied-to-useful-value? var ref)
  76. (let ((call (node-parent ref))
  77. (index (node-index ref)))
  78. (any? (lambda (r)
  79. (simplify-conditional? call index (call-arg (node-parent r) 1)))
  80. (variable-refs var))))
  81. ;; CALL is the least-common-ancestor of the references to VAR. Check that
  82. ;; the tree rooted at CALL contains no side-effects and that the control flow
  83. ;; necessarily passes to VAR. (Could check for undefined-effect here...)
  84. ;; could do check that jumped-to proc if not VAR jumped to VAR eventually
  85. (define (suitable-join-conditional? call var)
  86. (let label ((call call))
  87. (cond ((call-side-effects? call)
  88. #f)
  89. ((= 0 (call-exits call))
  90. (and (eq? 'jump (primop-id (call-primop call)))
  91. (eq? var (reference-variable (called-node call)))))
  92. (else
  93. (let loop ((i 0))
  94. (cond ((>= i (call-exits call))
  95. #t)
  96. ((not (label (lambda-body (call-arg call i))))
  97. #f)
  98. (else
  99. (loop (+ i 1)))))))))
  100. ;; #t if CALL performs side-effects. The continuations to CALL are ignored.
  101. (define (call-side-effects? call)
  102. (or (primop-side-effects (call-primop call))
  103. (let loop ((i (call-exits call)))
  104. (cond ((>= i (call-arg-count call))
  105. #f)
  106. ((side-effects? (call-arg call i))
  107. #t)
  108. (else
  109. (loop (+ i 1)))))))
  110. ;; The alternative to the above test: does the join point contain no side-effects
  111. ;; above the test?
  112. (define (suitable-join-point? join test)
  113. (let label ((call (lambda-body join)))
  114. (cond ((eq? call test)
  115. #t)
  116. ((call-side-effects? call)
  117. #f)
  118. (else
  119. (let loop ((i 0))
  120. (cond ((>= i (call-exits call))
  121. #t)
  122. ((not (label (lambda-body (call-arg call i))))
  123. #f)
  124. (else
  125. (loop (+ i 1)))))))))
  126. ;; (let ((j (lambda (v) ; VAR VAL
  127. ;; .a.
  128. ;; ($test c1 c2 ... v ...) ; TEST
  129. ;; .b.)))
  130. ;; .c.
  131. ;; (... (j x) ...) ; CALL
  132. ;; .d.)
  133. ;; ==>
  134. ;; .c.
  135. ;; (.a.
  136. ;; (let ((v1 (lambda (x) c1[x/v]))
  137. ;; (v2 (lambda (x) c2[x/v])))
  138. ;; (... ((lambda (v)
  139. ;; ($test (lambda () (v1 v)) (lambda () (v2 v)) ... v ...))
  140. ;; x)
  141. ;; ...))
  142. ;; .b.)
  143. ;; .d.
  144. ;;
  145. ;; CALL is the least common ancestor of the references to VAR, which is bound to
  146. ;; VAL, a procedure. TEST is a conditional that tests the argument passed to
  147. ;; VAL.
  148. ;;
  149. ;; (lambda-body VAL) is moved to where CALL is.
  150. ;; In the body of VAL, TEST is replaced by a LET that binds TEST's continuations
  151. ;; and then executes CALL. TEST's continuations are replaced by calls to
  152. ;; the variables bound by the LET.
  153. ;; Finally, references to VAR are replaced by a procedure whose body is TEST,
  154. ;; which is the point of the whole exercise.
  155. (define (really-join-substitute var val call test)
  156. (let ((value-var (car (lambda-variables val))))
  157. (receive (cont-call conts)
  158. (move-continuations test call value-var)
  159. (let ((test-parent (node-parent test))
  160. (val-parent (node-parent val))
  161. (val-index (node-index val)))
  162. (parameterize-continuations conts value-var)
  163. (detach-body test)
  164. (move-body cont-call
  165. (lambda (cont-call)
  166. (attach-body test-parent cont-call)
  167. (detach-body (lambda-body val))))
  168. (attach-body val test)
  169. (mark-changed (call-arg test 1)) ;; marks test as changed.
  170. (mark-changed cont-call)
  171. (substitute var val #t)
  172. (attach val-parent val-index (make-literal-node #f #f))
  173. (values)))))
  174. ;; Move the continuations of CALL to a LET call just above TO. Returns a list
  175. ;; of the variables now bound to the continuations and the continuations
  176. ;; themselves.
  177. (define (move-continuations call to arg-var)
  178. (let ((count (call-exits call)))
  179. (let loop ((i (- count 1)) (vs '()) (es '()))
  180. (cond ((< i 0)
  181. (let ((new-call (make-call-node (get-primop (enum primop-enum let))
  182. (+ count 1)
  183. 1))
  184. (new-proc (make-lambda-node 'j 'cont vs)))
  185. (attach-call-args new-call (cons new-proc es))
  186. (insert-body new-call new-proc (node-parent to))
  187. (values new-call es)))
  188. (else
  189. (let ((var (make-variable 'e (node-type (call-arg call i))))
  190. (cont (detach (call-arg call i))))
  191. (let-nodes ((new-cont () c1)
  192. (c1 (jump 0 (* var) (* arg-var))))
  193. (attach call i new-cont))
  194. (change-lambda-type cont 'jump)
  195. (loop (- i 1) (cons var vs) (cons cont es))))))))
  196. ;; Add a new variable to each of CONTS and substitute a reference to the correct
  197. ;; variable for each reference to VAR within CONTS.
  198. (define (parameterize-continuations conts var)
  199. (for-each (lambda (n)
  200. (let ((var (copy-variable var)))
  201. (set-lambda-variables! n (cons var (lambda-variables n)))
  202. (set-variable-binder! var n)
  203. (set-node-flag! n #t)))
  204. conts)
  205. (let ((backstop (variable-binder var)))
  206. (set-node-flag! backstop #t)
  207. (walk-refs-safely
  208. (lambda (n)
  209. (let ((cont (marked-ancestor n)))
  210. (if (not (eq? cont backstop))
  211. (replace n (make-reference-node (car (lambda-variables cont)))))))
  212. var)
  213. (set-node-flag! backstop #f)
  214. (for-each (lambda (n) (set-node-flag! n #f)) conts)
  215. (values)))