merge.scm 13 KB

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