call.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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/call.scm
  8. (define-module (ps-compiler simp call)
  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-equal)
  14. #:use-module (ps-compiler node node-util)
  15. #:use-module (ps-compiler node primop)
  16. #:use-module (ps-compiler node variable)
  17. #:use-module (ps-compiler param)
  18. #:use-module (ps-compiler simp simplify)
  19. #:use-module (ps-compiler util util)
  20. #:export (simplify-allocation
  21. simplify-known-call
  22. simplify-known-tail-call
  23. simplify-unknown-call
  24. simplify-return
  25. simplify-jump
  26. ;; simplify-undefined-value
  27. simplify-test expand-test simplify-test?))
  28. (define (simplify-jump call)
  29. (cond ((lambda-node? (call-arg call 0))
  30. (set-call-primop! call (get-primop (enum primop-enum let)))
  31. (set-call-exits! call 1)
  32. (set-node-simplified?! call #f))
  33. (else
  34. (default-simplifier call))))
  35. (define simplify-return simplify-jump)
  36. ;; If the procedure is a lambda-node:
  37. ;; 1. note that we know where the continuation lambda is used (and turn any
  38. ;; tail-calls using it into regular calls)
  39. ;; 2. change the primop to LET
  40. ;; 3. the procedure is now the continuation
  41. ;; 4. the continuation is now a jump lambda
  42. ;; 5. change the primop used to call the continuation to jump
  43. ;; 6. swap the cont and proc.
  44. ;; (CALL <cont> (LAMBDA (c . vars) ...) . args))
  45. ;; =>
  46. ;; (LET (LAMBDA (c . vars) ...) <cont> . args)
  47. ;; If the continuation just returns somewhere else, replace UNKNOWN-CALL
  48. ;; with UNKNOWN-TAIL-CALL.
  49. (define (simplify-known-call call)
  50. (let ((proc (call-arg call 1))
  51. (cont (call-arg call 0)))
  52. (cond ((lambda-node? proc)
  53. (determine-continuation-protocol cont (list proc))
  54. (set-call-primop! call (get-primop (enum primop-enum let)))
  55. (change-lambda-type proc 'cont)
  56. (change-lambda-type cont 'jump)
  57. (for-each (lambda (ref)
  58. (set-call-primop! (node-parent ref)
  59. (get-primop (enum primop-enum jump))))
  60. (variable-refs (car (lambda-variables proc))))
  61. (move cont
  62. (lambda (cont)
  63. (detach proc)
  64. (attach call 1 cont)
  65. proc)))
  66. ((trivial-continuation? cont)
  67. (replace cont (detach (call-arg (lambda-body cont) 0)))
  68. (set-call-primop! call (get-primop (enum primop-enum tail-call)))
  69. (set-call-exits! call 0))
  70. (else
  71. (default-simplifier call)))))
  72. ;; (CALL (CONT (v1 ... vN) (RETURN c v1 ... vN)) ...args...)
  73. (define (trivial-continuation? cont)
  74. (let ((body (lambda-body cont)))
  75. (and (calls-this-primop? body 'return)
  76. (= (length (lambda-variables cont))
  77. (- (call-arg-count body ) 1))
  78. (let loop ((vars (lambda-variables cont)) (i 1))
  79. (cond ((null? vars)
  80. #t)
  81. ((and (reference-node? (call-arg body i))
  82. (eq? (car vars)
  83. (reference-variable (call-arg body i))))
  84. (loop (cdr vars) (+ i 1)))
  85. (else #f))))))
  86. ;; The same as the above, except that the continuation is a reference node
  87. ;; and not a lambda, so we substitute it for the proc's continuation variable.
  88. (define (simplify-known-tail-call call)
  89. (let ((proc (call-arg call 1))
  90. (cont (call-arg call 0)))
  91. (cond ((lambda-node? proc)
  92. (set-call-primop! call (get-primop (enum primop-enum let)))
  93. (change-lambda-type proc 'cont)
  94. (substitute (car (lambda-variables proc)) cont #t)
  95. (set-lambda-variables! proc (cdr (lambda-variables proc)))
  96. (remove-call-arg call 0)
  97. (set-call-exits! call 1) ;; must be after REMOVE-CALL-ARG
  98. (mark-changed proc))
  99. (else
  100. (default-simplifier call)))))
  101. (define (simplify-test call)
  102. (simplify-arg call 2)
  103. (let ((value (call-arg call 2)))
  104. (cond ((literal-node? value)
  105. (fold-conditional call (if (eq? false-value (literal-value value))
  106. 1
  107. 0)))
  108. ((reference-node? value)
  109. (simplify-variable-test call (reference-variable value)))
  110. ((collapse-multiple-zero-bit-tests call)
  111. )
  112. (else
  113. (default-simplifier call)))))
  114. (define (simplify-variable-test call var)
  115. (cond ((flag-assq 'test (variable-flags var))
  116. => (lambda (pair)
  117. (fold-conditional call (cdr pair))))
  118. (else
  119. (let ((pair (cons 'test 0))
  120. (flags (variable-flags var)))
  121. (set-variable-flags! var (cons pair flags))
  122. (simplify-arg call 0)
  123. (set-cdr! pair 1)
  124. (simplify-arg call 1)
  125. (set-variable-flags! var flags)))))
  126. (define (fold-conditional call index)
  127. (replace-body call (detach-body (lambda-body (call-arg call index)))))
  128. ;; (if (and (= 0 (bitwise-and 'j x))
  129. ;; (= 0 (bitwise-and 'j y)))
  130. ;; ...)
  131. ;; =>
  132. ;; (if (= 0 (bitwise-and (bitwise-or x y) 'j))
  133. ;; ...)
  134. ;; This comes up in the Scheme48 VM.
  135. (define (collapse-multiple-zero-bit-tests test)
  136. (receive (mask first-arg)
  137. (zero-bit-test (call-arg test 2))
  138. (if mask
  139. (let ((false-exit (call-arg test 1))
  140. (true-exit (call-arg test 0)))
  141. (simplify-lambda-body true-exit)
  142. (simplify-lambda-body false-exit)
  143. (let ((call (lambda-body true-exit)))
  144. (if (and (eq? 'test (primop-id (call-primop call)))
  145. (node-equal? false-exit (call-arg call 1)))
  146. (receive (new-mask second-arg)
  147. (zero-bit-test (call-arg call 2))
  148. (if (and new-mask (= mask new-mask))
  149. (fold-zero-bit-tests test first-arg second-arg
  150. (call-arg call 0))
  151. #f))
  152. #f)))
  153. #f)))
  154. ;; = and bitwise-and always have any literal node as arg1
  155. ;;
  156. ;; 1. call to =
  157. ;; 2. first arg is literal 0
  158. ;; 3. second arg is call to and
  159. ;; 4. first arg of and-call is numeric literal
  160. ;; 5. second arg of and-call has no side-effects (reads are okay)
  161. ;; Returns #f or the two arguments to bitwise-and.
  162. (define (zero-bit-test call)
  163. (if (eq? '= (primop-id (call-primop call)))
  164. (let ((literal-0 (call-arg call 0))
  165. (bitwise-and-call (call-arg call 1)))
  166. (if (and (literal-node? literal-0)
  167. (number? (literal-value literal-0))
  168. (= 0 (literal-value literal-0))
  169. (call-node? bitwise-and-call)
  170. (eq? 'bitwise-and (primop-id (call-primop bitwise-and-call)))
  171. (literal-node? (call-arg bitwise-and-call 0))
  172. (number? (literal-value (call-arg bitwise-and-call 0)))
  173. (not (side-effects? (call-arg bitwise-and-call 1) 'read)))
  174. (values (literal-value (call-arg bitwise-and-call 0))
  175. (call-arg bitwise-and-call 1))
  176. (values #f #f)))
  177. (values #f #f)))
  178. (define (fold-zero-bit-tests test first-arg second-arg true-cont)
  179. (detach second-arg)
  180. (replace (call-arg test 0) (detach true-cont))
  181. (move first-arg
  182. (lambda (first-arg)
  183. (let-nodes ((call (bitwise-ior 0 first-arg second-arg)))
  184. call))))
  185. (define (expand-test call)
  186. (bug "Trying to expand a call to TEST (~D) ~S"
  187. (node-hash (node-parent (nontrivial-ancestor call)))
  188. call))
  189. ;; TEST can be simplified using any literal value.
  190. ;; The check for reference nodes is a heuristic. It will only help if the
  191. ;; two tests end up being sequential.
  192. (define (simplify-test? call index value)
  193. (cond ((literal-node? value)
  194. #t)
  195. ((reference-node? value)
  196. (any? (lambda (r)
  197. (eq? 'test (primop-id (call-primop (node-parent r)))))
  198. (variable-refs (reference-variable value))))
  199. (else
  200. #f)))
  201. (define (simplify-unknown-call call)
  202. (simplify-args call 0)
  203. (let ((proc (call-arg call 1)))
  204. (cond ((lambda-node? proc)
  205. (determine-lambda-protocol proc (list proc))
  206. (mark-changed proc))
  207. ((and (reference-node? proc)
  208. (variable-simplifier (reference-variable proc)))
  209. => (lambda (proc)
  210. (proc call))))))
  211. ;; Simplify a cell. A set-once cell is one that is set only once and does
  212. ;; not escape. If such a cell is set to a value that can be hoisted (without
  213. ;; moving variables out of scope) to the point the cell is created the cell
  214. ;; is replace with the value.
  215. ;; This should make use of the type of the cell.
  216. (define (simplify-allocation call)
  217. (set-node-simplified?! call #t)
  218. (simplify-args call 0) ;; simplify all arguments, including continuation
  219. (let ((var (car (lambda-variables (call-arg call 0)))))
  220. (if (every? cell-use? (variable-refs var))
  221. (receive (uses sets)
  222. (partition-list (lambda (n)
  223. (eq? 'contents
  224. (primop-id (call-primop (node-parent n)))))
  225. (variable-refs var))
  226. (simplify-cell-part call uses sets)))))
  227. (define (cell-use? ref)
  228. (let ((call (node-parent ref)))
  229. (case (primop-id (call-primop call))
  230. ((contents)
  231. #t)
  232. ((set-contents)
  233. (= (node-index ref) set/owner))
  234. (else
  235. #f))))
  236. (define (simplify-cell-part call my-uses my-sets)
  237. (cond ((null? my-uses)
  238. (for-each (lambda (n) (remove-body (node-parent n)))
  239. my-sets))
  240. ((null? my-sets)
  241. (for-each (lambda (n)
  242. (replace-call-with-value
  243. (node-parent n)
  244. (make-undefined-literal)))
  245. my-uses))
  246. ;; ((null? (cdr my-sets))
  247. ;; (set-literal-value! (call-arg call 1) 'single-set)
  248. ;; (really-simplify-single-set call (car my-sets) my-uses))
  249. (else
  250. (if (neq? 'small (literal-value (call-arg call 1)))
  251. (set-literal-value! (call-arg call 1) 'small)))))