merge.scm 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey
  3. ; This code determines which procedures are called from one other form, and
  4. ; thus can be compiled as part of that form and called with a `goto' instead
  5. ; of a normal procedure call. This saves much of the overhead of a normal
  6. ; procedure call.
  7. ;
  8. ; The procedures to be merged are annotated; no code is changed.
  9. (define-subrecord form form-merge form-merge
  10. ((head) ; self or the form into which this one will be merged
  11. )
  12. (
  13. (status #f) ; one of #F, DO-NOT-MERGE, MERGED
  14. tail-clients ; forms that call this one tail-recursively, this is an
  15. ; a-list of forms and reference nodes
  16. (tail-providers '()) ; forms that are used by this one, this is a simple list
  17. (merged '()) ; forms merged with this one
  18. (return-count 0) ; how many returns have been generated so far
  19. temp ; handy utility field
  20. ))
  21. ; Two procedures for letting the user know what is going on.
  22. (define (show-merges form)
  23. (let ((merges (form-merged form)))
  24. (if (not (null? merges))
  25. (format #t " ~S: ~S~%" (form-name form) (map form-name merges)))))
  26. (define (show-providers form)
  27. (cond ((eq? (form-type form) 'lambda)
  28. (format #t "~S ~A~%"
  29. (form-name form)
  30. (if (form-exported? form) " (exported)" ""))
  31. (cond ((or (not (null? (form-providers form)))
  32. (not (null? (form-tail-providers form))))
  33. (format #t " ~S~% ~S~%"
  34. (map form-name (form-providers form))
  35. (map form-name (form-tail-providers form))))))))
  36. ; Note that OTHERS should be merged with FORM.
  37. (define (do-merge form others)
  38. (let ((form (form-head form))
  39. (secondary (apply append (map form-merged others))))
  40. (set-form-merged! form (append others
  41. secondary
  42. (form-merged form)))
  43. (for-each (lambda (f)
  44. (set-form-head! f form))
  45. secondary)
  46. (for-each (lambda (f)
  47. (set-form-head! f form)
  48. (set-form-status! f 'merged)
  49. (set-form-type! f 'merged)
  50. (set-form-merged! f '()))
  51. others)))
  52. ; Returns the merged form, if any, to which NODE is a reference.
  53. ;(define (merged-procedure-reference node)
  54. ; (let ((res (real-merged-procedure-reference node)))
  55. ; (if (and (reference-node? node)
  56. ; (eq? 'trace-value (variable-name (reference-variable node))))
  57. ; (format " [m-p-r ~S -> ~S]~%" node res))
  58. ; res))
  59. ;
  60. (define (merged-procedure-reference node)
  61. (cond ((and (reference-node? node)
  62. (maybe-variable->form (reference-variable node)))
  63. => (lambda (form)
  64. (if (eq? 'merged (form-type form))
  65. form
  66. #f)))
  67. (else
  68. #f)))
  69. ; Is FORM ever tail called?
  70. (define (form-tail-called? form)
  71. (and (or (eq? 'lambda (form-type form))
  72. (eq? 'merged (form-type form)))
  73. (memq? 'tail-called (variable-flags (form-var form)))))
  74. ; Annotate FORM if it is in fact called tail-recursively anywhere.
  75. (define (note-tail-called-procedure form)
  76. (if (and (eq? 'lambda (form-type form))
  77. (or (any (lambda (r)
  78. (used-as-label? r))
  79. (variable-refs (form-var form)))
  80. (eq? 'tail-called (lambda-protocol (form-value form)))))
  81. (set-variable-flags! (form-var form)
  82. (cons 'tail-called
  83. (variable-flags (form-var form))))))
  84. (define (used-as-label? node)
  85. (and (node? (node-parent node))
  86. (goto-call? (node-parent node))
  87. (= 1 (node-index node))))
  88. ;------------------------------------------------------------
  89. ; Entry point.
  90. ;
  91. ; First marks the tail-called procedures and adds the MERGE slots to the
  92. ; forms. The C code generator expects FORM-MERGED to work, even if no
  93. ; actual merging was done.
  94. ;
  95. ; Three steps:
  96. ; Find the call graph.
  97. ; Merge the tail-called forms.
  98. ; Merge the non-tail-called forms.
  99. (define *merge-forms?* #t)
  100. (define (merge-forms forms)
  101. (for-each (lambda (f)
  102. (note-tail-called-procedure f)
  103. (set-form-merge! f (form-merge-maker f))
  104. (set-form-providers! f '()))
  105. forms)
  106. (if *merge-forms?*
  107. (let ((mergable-forms (filter determine-merger-graph forms)))
  108. (format #t "Call Graph:~%<procedure name>~%")
  109. (format #t " <called non-tail-recursively>~%")
  110. (format #t " <called tail-recursively>~%")
  111. (for-each show-providers forms)
  112. (format #t "Merging forms~%")
  113. (receive (tail other)
  114. (partition-list (lambda (f) (null? (form-clients f)))
  115. mergable-forms)
  116. (merge-tail-forms tail)
  117. (for-each merge-non-tail-forms forms)
  118. (for-each show-merges forms)
  119. (values)))))
  120. ; The only forms that can be merged are those that:
  121. ; are lambdas,
  122. ; all uses are calls,
  123. ; are not exported, and
  124. ; every loop containing a non-tail-recursive call must contain a call to
  125. ; at least one non-merged procedure.
  126. ;
  127. ; This code doesn't use the last criterion. Instead it makes sure that each
  128. ; procedure is called exclusively tail-recursively or non-tail-recursively
  129. ; and doesn't allow non-tail-recursion in loops at all.
  130. (define (determine-merger-graph form)
  131. (cond ((and (eq? 'lambda (form-type form))
  132. (really-determine-merger-graph form)
  133. (not (form-exported? form))
  134. (or (null? (form-clients form))
  135. (null? (form-tail-clients form))))
  136. #t)
  137. (else
  138. (set-form-status! form 'do-not-merge)
  139. #f)))
  140. ; Loop down the references to FORM's variable adding FORM to the providers
  141. ; lists of the forms that reference the variable, and adding those forms
  142. ; to FORM's clients lists. OKAY? is #T if all references are calls.
  143. ; The full usage graph is needed, even if there are uses of the form's value
  144. ; that are not calls.
  145. (define (really-determine-merger-graph form)
  146. (let loop ((refs (variable-refs (form-var form)))
  147. (clients '()) (tail-clients '()) (okay? #t))
  148. (cond ((null? refs)
  149. (set-form-clients! form clients)
  150. (set-form-tail-clients! form tail-clients)
  151. okay?)
  152. (else
  153. (let* ((r (car refs))
  154. (f (node-form (car refs))))
  155. (if (and (called-node? r)
  156. (or (calls-this-primop? (node-parent r) 'tail-call)
  157. (calls-this-primop? (node-parent r) 'unknown-tail-call)))
  158. (loop (cdr refs)
  159. clients
  160. (add-to-client-list tail-clients r form f
  161. form-tail-providers
  162. set-form-tail-providers!)
  163. okay?)
  164. (loop (cdr refs)
  165. (add-to-client-list clients r form f
  166. form-providers
  167. set-form-providers!)
  168. tail-clients
  169. (and okay? (called-node? r)))))))))
  170. (define (add-to-client-list client-list ref form f getter setter)
  171. (cond ((assq f client-list)
  172. => (lambda (p)
  173. (set-cdr! p (cons ref (cdr p)))
  174. client-list))
  175. (else
  176. (setter f (cons form (getter f)))
  177. (cons (list f ref) client-list))))
  178. ; These forms are non-exported procedures that are always tail-called.
  179. ; Strongly connected components of the call graph that have a single
  180. ; entry point, whether in the component or not, are merged.
  181. ; This depends on STRONGLY-CONNECTED-COMPONENTS returning the components
  182. ; in a reverse topologically sorted order (which it does).
  183. (define (merge-tail-forms forms)
  184. (for-each merge-tail-loop
  185. (reverse (strongly-connected-components
  186. forms
  187. (lambda (f)
  188. (filter (lambda (f) (memq? f forms))
  189. (map car (form-tail-clients f))))
  190. form-temp
  191. set-form-temp!))))
  192. ; ENTRIES are the forms in the loop that are called from outside.
  193. ; FORMS is used as a unique identifier here.
  194. (define (merge-tail-loop forms)
  195. (for-each (lambda (f) (set-form-temp! f forms)) forms)
  196. (receive (entries other)
  197. (partition-list (lambda (f)
  198. (any? (lambda (p)
  199. (not (eq? forms
  200. (form-temp (car p)))))
  201. (form-tail-clients f)))
  202. forms)
  203. (cond ((single-outside-client (if (null? entries)
  204. other
  205. entries)
  206. forms)
  207. => (lambda (f) (do-merge f forms)))
  208. ((and (not (null? entries))
  209. (null? (cdr entries))
  210. (not (null? other)))
  211. (do-merge (car entries) other)))
  212. (for-each (lambda (f) (set-form-temp! f #f)) forms)))
  213. ; This checks to see if all non-FLAGged clients of ENTRIES are in
  214. ; fact a single form, and then returns that form.
  215. ; Forms that have already been merged into another form are treated as that
  216. ; other form (by using FORM-HEAD).
  217. (define (single-outside-client entries flag)
  218. (let loop ((entries entries) (form #f))
  219. (if (null? entries)
  220. form
  221. (let loop2 ((clients (form-tail-clients (car entries))) (form form))
  222. (cond ((null? clients)
  223. (loop (cdr entries) form))
  224. ((eq? (form-temp (caar clients)) flag)
  225. (loop2 (cdr clients) form))
  226. ((not form)
  227. (loop2 (cdr clients) (form-head (caar clients))))
  228. ((eq? (form-head (caar clients)) form)
  229. (loop2 (cdr clients) form))
  230. (else
  231. #f))))))
  232. ; Merge the forms used by FORM into it if possible.
  233. (define (merge-non-tail-forms form)
  234. (for-each (lambda (f)
  235. (maybe-merge-non-tail-form f (form-head form)))
  236. (form-providers form)))
  237. ; If FORM is not INTO, has not been merged before, and is only used by
  238. ; INTO, then merge FORM into INTO and recursively check the forms used
  239. ; by FORM.
  240. (define (maybe-merge-non-tail-form form into)
  241. (cond ((and (not (eq? form into))
  242. (not (form-status form))
  243. (every? (lambda (p)
  244. (eq? (form-head (car p)) into))
  245. (form-clients form)))
  246. (do-merge into (list form))
  247. (for-each tail-call->call
  248. (variable-refs (form-var form)))
  249. (for-each tail-call->call
  250. (variable-refs (car (lambda-variables (form-node form)))))
  251. (for-each (lambda (f)
  252. (maybe-merge-non-tail-form f into))
  253. (form-providers form)))))
  254. ; Replace tail calls with calls to make the code generator's job easier.
  255. ; The user didn't say that these calls had to be tail-recursive.
  256. (define (tail-call->call ref)
  257. (let ((call (node-parent ref)))
  258. (if (or (calls-this-primop? call 'tail-call)
  259. (calls-this-primop? call 'unknown-tail-call))
  260. (let ((type (arrow-type-result
  261. (maybe-follow-uvar (node-type (call-arg call 1))))))
  262. (move (call-arg call 0)
  263. (lambda (cont)
  264. (let-nodes ((new-cont ((v type)) (return 0 cont (* v))))
  265. new-cont)))
  266. (set-call-exits! call 1)
  267. (set-call-primop! call
  268. (get-primop (if (calls-this-primop? call 'tail-call)
  269. (enum primop call)
  270. (enum primop unknown-call))))))))