merge.scm 9.9 KB

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