utils.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013, 2014, 2015, 2017, 2018, 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. ;;; Commentary:
  17. ;;;
  18. ;;; Helper facilities for working with CPS.
  19. ;;;
  20. ;;; Code:
  21. (define-module (language cps utils)
  22. #:use-module (ice-9 match)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (language cps)
  25. #:use-module (language cps intset)
  26. #:use-module (language cps intmap)
  27. #:use-module (language cps graphs)
  28. #:export (;; Fresh names.
  29. label-counter var-counter
  30. fresh-label fresh-var
  31. with-fresh-name-state compute-max-label-and-var
  32. let-fresh
  33. ;; Graphs.
  34. compute-function-body
  35. compute-reachable-functions
  36. compute-successors
  37. compute-predecessors
  38. compute-idoms
  39. compute-dom-edges)
  40. #:re-export (fold1 fold2
  41. trivial-intset
  42. intmap-map
  43. intmap-keys
  44. invert-bijection invert-partition
  45. intset->intmap
  46. worklist-fold
  47. fixpoint
  48. ;; Flow analysis.
  49. invert-graph
  50. compute-reverse-post-order
  51. compute-strongly-connected-components
  52. compute-sorted-strongly-connected-components
  53. solve-flow-equations))
  54. (define label-counter (make-parameter #f))
  55. (define var-counter (make-parameter #f))
  56. (define (fresh-label)
  57. (let ((count (or (label-counter)
  58. (error "fresh-label outside with-fresh-name-state"))))
  59. (label-counter (1+ count))
  60. count))
  61. (define (fresh-var)
  62. (let ((count (or (var-counter)
  63. (error "fresh-var outside with-fresh-name-state"))))
  64. (var-counter (1+ count))
  65. count))
  66. (define-syntax-rule (let-fresh (label ...) (var ...) body ...)
  67. (let* ((label (fresh-label)) ...
  68. (var (fresh-var)) ...)
  69. body ...))
  70. (define-syntax-rule (with-fresh-name-state fun body ...)
  71. (call-with-values (lambda () (compute-max-label-and-var fun))
  72. (lambda (max-label max-var)
  73. (parameterize ((label-counter (1+ max-label))
  74. (var-counter (1+ max-var)))
  75. body ...))))
  76. (define (compute-max-label-and-var conts)
  77. (values (or (intmap-prev conts) -1)
  78. (intmap-fold (lambda (k cont max-var)
  79. (match cont
  80. (($ $kargs names syms body)
  81. (apply max max-var syms))
  82. (($ $kfun src meta (and self (not #f)))
  83. (max max-var self))
  84. (_ max-var)))
  85. conts
  86. -1)))
  87. (define (compute-function-body conts kfun)
  88. (persistent-intset
  89. (let visit-cont ((label kfun) (labels empty-intset))
  90. (cond
  91. ((intset-ref labels label) labels)
  92. (else
  93. (let ((labels (intset-add! labels label)))
  94. (match (intmap-ref conts label)
  95. (($ $kreceive arity k) (visit-cont k labels))
  96. (($ $kfun src meta self ktail kclause)
  97. (let ((labels (visit-cont ktail labels)))
  98. (if kclause
  99. (visit-cont kclause labels)
  100. labels)))
  101. (($ $ktail) labels)
  102. (($ $kclause arity kbody kalt)
  103. (if kalt
  104. (visit-cont kalt (visit-cont kbody labels))
  105. (visit-cont kbody labels)))
  106. (($ $kargs names syms term)
  107. (match term
  108. (($ $continue k)
  109. (visit-cont k labels))
  110. (($ $branch kf kt)
  111. (visit-cont kf (visit-cont kt labels)))
  112. (($ $prompt k kh)
  113. (visit-cont k (visit-cont kh labels)))
  114. (($ $throw)
  115. labels))))))))))
  116. (define* (compute-reachable-functions conts #:optional (kfun 0))
  117. "Compute a mapping LABEL->LABEL..., where each key is a reachable
  118. $kfun and each associated value is the body of the function, as an
  119. intset."
  120. (define (intset-cons i set) (intset-add set i))
  121. (define (visit-fun kfun body to-visit)
  122. (intset-fold
  123. (lambda (label to-visit)
  124. (define (return kfun*) (fold intset-cons to-visit kfun*))
  125. (define (return1 kfun) (intset-add to-visit kfun))
  126. (define (return0) to-visit)
  127. (match (intmap-ref conts label)
  128. (($ $kargs _ _ ($ $continue _ _ exp))
  129. (match exp
  130. (($ $fun label) (return1 label))
  131. (($ $rec _ _ (($ $fun labels) ...)) (return labels))
  132. (($ $const-fun label) (return1 label))
  133. (($ $code label) (return1 label))
  134. (($ $callk label) (return1 label))
  135. (_ (return0))))
  136. (_ (return0))))
  137. body
  138. to-visit))
  139. (let lp ((to-visit (intset kfun)) (visited empty-intmap))
  140. (let ((to-visit (intset-subtract to-visit (intmap-keys visited))))
  141. (if (eq? to-visit empty-intset)
  142. visited
  143. (call-with-values
  144. (lambda ()
  145. (intset-fold
  146. (lambda (kfun to-visit visited)
  147. (let ((body (compute-function-body conts kfun)))
  148. (values (visit-fun kfun body to-visit)
  149. (intmap-add visited kfun body))))
  150. to-visit
  151. empty-intset
  152. visited))
  153. lp)))))
  154. (define* (compute-successors conts #:optional (kfun (intmap-next conts)))
  155. (define (visit label succs)
  156. (let visit ((label kfun) (succs empty-intmap))
  157. (define (propagate0)
  158. (intmap-add! succs label empty-intset))
  159. (define (propagate1 succ)
  160. (visit succ (intmap-add! succs label (intset succ))))
  161. (define (propagate2 succ0 succ1)
  162. (let ((succs (intmap-add! succs label (intset succ0 succ1))))
  163. (visit succ1 (visit succ0 succs))))
  164. (if (intmap-ref succs label (lambda (_) #f))
  165. succs
  166. (match (intmap-ref conts label)
  167. (($ $kargs names vars term)
  168. (match term
  169. (($ $continue k) (propagate1 k))
  170. (($ $branch kf kt) (propagate2 kf kt))
  171. (($ $prompt k kh) (propagate2 k kh))
  172. (($ $throw) (propagate0))))
  173. (($ $kreceive arity k)
  174. (propagate1 k))
  175. (($ $kfun src meta self tail clause)
  176. (if clause
  177. (propagate2 clause tail)
  178. (propagate1 tail)))
  179. (($ $kclause arity kbody kalt)
  180. (if kalt
  181. (propagate2 kbody kalt)
  182. (propagate1 kbody)))
  183. (($ $ktail) (propagate0))))))
  184. (persistent-intmap (visit kfun empty-intmap)))
  185. (define* (compute-predecessors conts kfun #:key
  186. (labels (compute-function-body conts kfun)))
  187. (define (meet cdr car)
  188. (cons car cdr))
  189. (define (add-preds label preds)
  190. (define (add-pred k preds)
  191. (intmap-add! preds k label meet))
  192. (match (intmap-ref conts label)
  193. (($ $kreceive arity k)
  194. (add-pred k preds))
  195. (($ $kfun src meta self ktail kclause)
  196. (add-pred ktail (if kclause (add-pred kclause preds) preds)))
  197. (($ $ktail)
  198. preds)
  199. (($ $kclause arity kbody kalt)
  200. (add-pred kbody (if kalt (add-pred kalt preds) preds)))
  201. (($ $kargs names syms term)
  202. (match term
  203. (($ $continue k) (add-pred k preds))
  204. (($ $branch kf kt) (add-pred kf (add-pred kt preds)))
  205. (($ $prompt k kh) (add-pred k (add-pred kh preds)))
  206. (($ $throw) preds)))))
  207. (persistent-intmap
  208. (intset-fold add-preds labels
  209. (intset->intmap (lambda (label) '()) labels))))
  210. ;; Precondition: For each function in CONTS, the continuation names are
  211. ;; topologically sorted.
  212. (define (compute-idoms conts kfun)
  213. ;; This is the iterative O(n^2) fixpoint algorithm, originally from
  214. ;; Allen and Cocke ("Graph-theoretic constructs for program flow
  215. ;; analysis", 1972). See the discussion in Cooper, Harvey, and
  216. ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
  217. (let ((preds-map (compute-predecessors conts kfun)))
  218. (define (compute-idom idoms preds)
  219. (define (idom-ref label)
  220. (intmap-ref idoms label (lambda (_) #f)))
  221. (match preds
  222. (() -1)
  223. ((pred) pred) ; Shortcut.
  224. ((pred . preds)
  225. (define (common-idom d0 d1)
  226. ;; We exploit the fact that a reverse post-order is a
  227. ;; topological sort, and so the idom of a node is always
  228. ;; numerically less than the node itself.
  229. (let lp ((d0 d0) (d1 d1))
  230. (cond
  231. ;; d0 or d1 can be false on the first iteration.
  232. ((not d0) d1)
  233. ((not d1) d0)
  234. ((= d0 d1) d0)
  235. ((< d0 d1) (lp d0 (idom-ref d1)))
  236. (else (lp (idom-ref d0) d1)))))
  237. (fold1 common-idom preds pred))))
  238. (define (adjoin-idom label preds idoms)
  239. (let ((idom (compute-idom idoms preds)))
  240. ;; Don't use intmap-add! here.
  241. (intmap-add idoms label idom (lambda (old new) new))))
  242. (fixpoint (lambda (idoms)
  243. (intmap-fold adjoin-idom preds-map idoms))
  244. empty-intmap)))
  245. ;; Precondition: For each function in CONTS, the continuation names are
  246. ;; topologically sorted.
  247. (define (compute-idoms conts kfun)
  248. ;; This is the iterative O(n^2) fixpoint algorithm, originally from
  249. ;; Allen and Cocke ("Graph-theoretic constructs for program flow
  250. ;; analysis", 1972). See the discussion in Cooper, Harvey, and
  251. ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
  252. (let ((preds-map (compute-predecessors conts kfun)))
  253. (define (compute-idom idoms preds)
  254. (define (idom-ref label)
  255. (intmap-ref idoms label (lambda (_) #f)))
  256. (match preds
  257. (() -1)
  258. ((pred) pred) ; Shortcut.
  259. ((pred . preds)
  260. (define (common-idom d0 d1)
  261. ;; We exploit the fact that a reverse post-order is a
  262. ;; topological sort, and so the idom of a node is always
  263. ;; numerically less than the node itself.
  264. (let lp ((d0 d0) (d1 d1))
  265. (cond
  266. ;; d0 or d1 can be false on the first iteration.
  267. ((not d0) d1)
  268. ((not d1) d0)
  269. ((= d0 d1) d0)
  270. ((< d0 d1) (lp d0 (idom-ref d1)))
  271. (else (lp (idom-ref d0) d1)))))
  272. (fold1 common-idom preds pred))))
  273. (define (adjoin-idom label preds idoms)
  274. (let ((idom (compute-idom idoms preds)))
  275. ;; Don't use intmap-add! here.
  276. (intmap-add idoms label idom (lambda (old new) new))))
  277. (fixpoint (lambda (idoms)
  278. (intmap-fold adjoin-idom preds-map idoms))
  279. empty-intmap)))
  280. ;; Compute a vector containing, for each node, a list of the nodes that
  281. ;; it immediately dominates. These are the "D" edges in the DJ tree.
  282. (define (compute-dom-edges idoms)
  283. (define (snoc cdr car) (cons car cdr))
  284. (persistent-intmap
  285. (intmap-fold (lambda (label idom doms)
  286. (let ((doms (intmap-add! doms label '())))
  287. (cond
  288. ((< idom 0) doms) ;; No edge to entry.
  289. (else (intmap-add! doms idom label snoc)))))
  290. idoms
  291. empty-intmap)))