fix-letrec.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. ;;; transformation of letrec into simpler forms
  2. ;; Copyright (C) 2009-2013,2016,2019 Free Software Foundation, Inc.
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. (define-module (language tree-il fix-letrec)
  17. #:use-module (system base syntax)
  18. #:use-module (srfi srfi-1)
  19. #:use-module (srfi srfi-11)
  20. #:use-module (ice-9 match)
  21. #:use-module (language tree-il)
  22. #:use-module (language tree-il effects)
  23. #:use-module (language cps graphs)
  24. #:use-module (language cps intmap)
  25. #:use-module (language cps intset)
  26. #:export (fix-letrec))
  27. ;; For a detailed discussion, see "Fixing Letrec: A Faithful Yet
  28. ;; Efficient Implementation of Scheme's Recursive Binding Construct", by
  29. ;; Oscar Waddell, Dipanwita Sarkar, and R. Kent Dybvig, as well as
  30. ;; "Fixing Letrec (reloaded)", by Abdulaziz Ghuloum and R. Kent Dybvig.
  31. (define fix-fold (make-tree-il-folder))
  32. (define (analyze-lexicals x)
  33. (define referenced (make-hash-table))
  34. (define assigned (make-hash-table))
  35. ;; Functional hash sets would be nice.
  36. (fix-fold x
  37. (lambda (x)
  38. (record-case x
  39. ((<lexical-ref> gensym)
  40. (hashq-set! referenced gensym #t)
  41. (values))
  42. ((<lexical-set> gensym)
  43. (hashq-set! assigned gensym #t)
  44. (values))
  45. (else
  46. (values))))
  47. (lambda (x)
  48. (values)))
  49. (values referenced assigned))
  50. (define (make-seq* src head tail)
  51. (record-case head
  52. ((<lambda>) tail)
  53. ((<const>) tail)
  54. ((<lexical-ref>) tail)
  55. ((<void>) tail)
  56. (else (make-seq src head tail))))
  57. (define (free-variables expr cache)
  58. (define (adjoin elt set)
  59. (lset-adjoin eq? set elt))
  60. (define (union set1 set2)
  61. (lset-union eq? set1 set2))
  62. (define (difference set1 set2)
  63. (lset-difference eq? set1 set2))
  64. (define fix-fold (make-tree-il-folder))
  65. (define (recurse expr)
  66. (free-variables expr cache))
  67. (define (recurse* exprs)
  68. (fold (lambda (expr free)
  69. (union (recurse expr) free))
  70. '()
  71. exprs))
  72. (define (visit expr)
  73. (match expr
  74. ((or ($ <void>) ($ <const>) ($ <primitive-ref>)
  75. ($ <module-ref>) ($ <toplevel-ref>))
  76. '())
  77. (($ <lexical-ref> src name gensym)
  78. (list gensym))
  79. (($ <lexical-set> src name gensym exp)
  80. (adjoin gensym (recurse exp)))
  81. (($ <module-set> src mod name public? exp)
  82. (recurse exp))
  83. (($ <toplevel-set> src mod name exp)
  84. (recurse exp))
  85. (($ <toplevel-define> src mod name exp)
  86. (recurse exp))
  87. (($ <conditional> src test consequent alternate)
  88. (union (recurse test)
  89. (union (recurse consequent)
  90. (recurse alternate))))
  91. (($ <call> src proc args)
  92. (recurse* (cons proc args)))
  93. (($ <primcall> src name args)
  94. (recurse* args))
  95. (($ <seq> src head tail)
  96. (union (recurse head)
  97. (recurse tail)))
  98. (($ <lambda> src meta body)
  99. (recurse body))
  100. (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
  101. (union (difference (union (recurse* inits)
  102. (recurse body))
  103. gensyms)
  104. (if alternate
  105. (recurse alternate)
  106. '())))
  107. (($ <let> src names gensyms vals body)
  108. (union (recurse* vals)
  109. (difference (recurse body)
  110. gensyms)))
  111. (($ <letrec> src in-order? names gensyms vals body)
  112. (difference (union (recurse* vals)
  113. (recurse body))
  114. gensyms))
  115. (($ <fix> src names gensyms vals body)
  116. (difference (union (recurse* vals)
  117. (recurse body))
  118. gensyms))
  119. (($ <let-values> src exp body)
  120. (union (recurse exp)
  121. (recurse body)))
  122. (($ <prompt> src escape-only? tag body handler)
  123. (union (recurse tag)
  124. (union (recurse body)
  125. (recurse handler))))
  126. (($ <abort> src tag args tail)
  127. (union (recurse tag)
  128. (union (recurse* args)
  129. (recurse tail))))))
  130. (or (hashq-ref cache expr)
  131. (let ((res (visit expr)))
  132. (hashq-set! cache expr res)
  133. res)))
  134. (define (enumerate elts)
  135. (fold2 (lambda (x out id)
  136. (values (intmap-add out id x) (1+ id)))
  137. elts empty-intmap 0))
  138. (define (compute-complex id->sym id->init assigned)
  139. (define compute-effects
  140. (make-effects-analyzer (lambda (x) (hashq-ref assigned x))))
  141. (intmap-fold
  142. (lambda (id sym complex)
  143. (if (or (hashq-ref assigned sym)
  144. (let ((effects (compute-effects (intmap-ref id->init id))))
  145. (not (constant? (exclude-effects effects &allocation)))))
  146. (intset-add complex id)
  147. complex))
  148. id->sym empty-intset))
  149. (define (compute-sccs names syms inits in-order? fv-cache assigned)
  150. (define id->name (enumerate names))
  151. (define id->sym (enumerate syms))
  152. (define id->init (enumerate inits))
  153. (define sym->id (intmap-fold (lambda (id sym out) (acons sym id out))
  154. id->sym '()))
  155. (define (var-list->intset vars)
  156. (fold1 (lambda (sym out)
  157. (intset-add out (assq-ref sym->id sym)))
  158. vars empty-intset))
  159. (define (free-in-init init)
  160. (var-list->intset
  161. (lset-intersection eq? syms (free-variables init fv-cache))))
  162. (define fv-edges
  163. (fold2 (lambda (init fv i)
  164. (values
  165. (intmap-add fv i (free-in-init init))
  166. (1+ i)))
  167. inits empty-intmap 0))
  168. (define order-edges
  169. (if in-order?
  170. (let ((complex (compute-complex id->sym id->init assigned)))
  171. (intmap-fold (lambda (id sym out prev)
  172. (values
  173. (intmap-add out id (intset-intersect complex prev))
  174. (intset-add prev id)))
  175. id->sym empty-intmap empty-intset))
  176. empty-intmap))
  177. (define sccs
  178. (reverse
  179. (compute-sorted-strongly-connected-components
  180. (invert-graph (intmap-union fv-edges order-edges intset-union)))))
  181. (map (lambda (ids)
  182. (intset-fold-right (lambda (id out)
  183. (cons (list (intmap-ref id->name id)
  184. (intmap-ref id->sym id)
  185. (intmap-ref id->init id))
  186. out))
  187. ids '()))
  188. sccs))
  189. (define (fix-scc src binds body fv-cache referenced assigned)
  190. (match binds
  191. (((name sym init))
  192. ;; Case of an SCC containing just a single binding.
  193. (cond
  194. ((not (hashq-ref referenced sym))
  195. (make-seq* src init body))
  196. ((and (lambda? init) (not (hashq-ref assigned sym)))
  197. (make-fix src (list name) (list sym) (list init) body))
  198. ((memq sym (free-variables init fv-cache))
  199. (make-let src (list name) (list sym) (list (make-void src))
  200. (make-seq src
  201. (make-lexical-set src name sym init)
  202. body)))
  203. (else
  204. (make-let src (list name) (list sym) (list init)
  205. body))))
  206. (_
  207. (call-with-values (lambda ()
  208. (partition
  209. (lambda (bind)
  210. (match bind
  211. ((name sym init)
  212. (and (lambda? init)
  213. (not (hashq-ref assigned sym))))))
  214. binds))
  215. (lambda (l c)
  216. (define (bind-complex-vars body)
  217. (if (null? c)
  218. body
  219. (let ((inits (map (lambda (x) (make-void #f)) c)))
  220. (make-let src (map car c) (map cadr c) inits body))))
  221. (define (bind-lambdas body)
  222. (if (null? l)
  223. body
  224. (make-fix src (map car l) (map cadr l) (map caddr l) body)))
  225. (define (initialize-complex body)
  226. (fold-right (lambda (bind body)
  227. (match bind
  228. ((name sym init)
  229. (make-seq src
  230. (make-lexical-set src name sym init)
  231. body))))
  232. body c))
  233. (bind-complex-vars
  234. (bind-lambdas
  235. (initialize-complex body))))))))
  236. (define (fix-term src in-order? names gensyms vals body
  237. fv-cache referenced assigned)
  238. (fold-right (lambda (binds body)
  239. (fix-scc src binds body fv-cache referenced assigned))
  240. body
  241. (compute-sccs names gensyms vals in-order? fv-cache
  242. assigned)))
  243. (define (fix-letrec x)
  244. (let-values (((referenced assigned) (analyze-lexicals x)))
  245. (define fv-cache (make-hash-table))
  246. (post-order
  247. (lambda (x)
  248. (record-case x
  249. ;; Sets to unreferenced variables may be replaced by their
  250. ;; expression, called for effect.
  251. ((<lexical-set> gensym exp)
  252. (if (hashq-ref referenced gensym)
  253. x
  254. (make-seq* #f exp (make-void #f))))
  255. ((<letrec> src in-order? names gensyms vals body)
  256. (fix-term src in-order? names gensyms vals body
  257. fv-cache referenced assigned))
  258. ((<let> src names gensyms vals body)
  259. ;; Apply the same algorithm to <let> that binds <lambda>
  260. (if (or-map lambda? vals)
  261. (fix-term src #f names gensyms vals body
  262. fv-cache referenced assigned)
  263. x))
  264. (else x)))
  265. x)))
  266. ;;; Local Variables:
  267. ;;; eval: (put 'record-case 'scheme-indent-function 1)
  268. ;;; End: