call.scm 10 KB

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