graphs.scm 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013-2015, 2017-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 graphs over intsets and intmaps.
  19. ;;;
  20. ;;; Code:
  21. (define-module (language cps graphs)
  22. #:use-module (ice-9 match)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (language cps intset)
  25. #:use-module (language cps intmap)
  26. #:export (;; Various utilities.
  27. fold1 fold2
  28. trivial-intset
  29. intmap-map
  30. intmap-keys
  31. invert-bijection invert-partition
  32. intset->intmap
  33. worklist-fold
  34. fixpoint
  35. ;; Flow analysis.
  36. invert-graph
  37. compute-reverse-post-order
  38. compute-strongly-connected-components
  39. compute-sorted-strongly-connected-components
  40. solve-flow-equations))
  41. (define-inlinable (fold1 f l s0)
  42. (let lp ((l l) (s0 s0))
  43. (match l
  44. (() s0)
  45. ((elt . l) (lp l (f elt s0))))))
  46. (define-inlinable (fold2 f l s0 s1)
  47. (let lp ((l l) (s0 s0) (s1 s1))
  48. (match l
  49. (() (values s0 s1))
  50. ((elt . l)
  51. (call-with-values (lambda () (f elt s0 s1))
  52. (lambda (s0 s1)
  53. (lp l s0 s1)))))))
  54. (define (trivial-intset set)
  55. "Returns the sole member of @var{set}, if @var{set} has exactly one
  56. member, or @code{#f} otherwise."
  57. (let ((first (intset-next set)))
  58. (and first
  59. (not (intset-next set (1+ first)))
  60. first)))
  61. (define (intmap-map proc map)
  62. (persistent-intmap
  63. (intmap-fold (lambda (k v out) (intmap-add! out k (proc k v)))
  64. map
  65. empty-intmap)))
  66. (define (intmap-keys map)
  67. "Return an intset of the keys in @var{map}."
  68. (persistent-intset
  69. (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
  70. (define (invert-bijection map)
  71. "Assuming the values of @var{map} are integers and are unique, compute
  72. a map in which each value maps to its key. If the values are not
  73. unique, an error will be signalled."
  74. (intmap-fold (lambda (k v out) (intmap-add out v k)) map empty-intmap))
  75. (define (invert-partition map)
  76. "Assuming the values of @var{map} are disjoint intsets, compute a map
  77. in which each member of each set maps to its key. If the values are not
  78. disjoint, an error will be signalled."
  79. (intmap-fold (lambda (k v* out)
  80. (intset-fold (lambda (v out) (intmap-add out v k)) v* out))
  81. map empty-intmap))
  82. (define (intset->intmap f set)
  83. (persistent-intmap
  84. (intset-fold (lambda (label preds)
  85. (intmap-add! preds label (f label)))
  86. set empty-intmap)))
  87. (define worklist-fold
  88. (case-lambda
  89. ((f in out)
  90. (let lp ((in in) (out out))
  91. (if (eq? in empty-intset)
  92. out
  93. (call-with-values (lambda () (f in out)) lp))))
  94. ((f in out0 out1)
  95. (let lp ((in in) (out0 out0) (out1 out1))
  96. (if (eq? in empty-intset)
  97. (values out0 out1)
  98. (call-with-values (lambda () (f in out0 out1)) lp))))))
  99. (define fixpoint
  100. (case-lambda
  101. ((f x)
  102. (let lp ((x x))
  103. (let ((x* (f x)))
  104. (if (eq? x x*) x* (lp x*)))))
  105. ((f x0 x1)
  106. (let lp ((x0 x0) (x1 x1))
  107. (call-with-values (lambda () (f x0 x1))
  108. (lambda (x0* x1*)
  109. (if (and (eq? x0 x0*) (eq? x1 x1*))
  110. (values x0* x1*)
  111. (lp x0* x1*))))))))
  112. (define (compute-reverse-post-order succs start)
  113. "Compute a reverse post-order numbering for a depth-first walk over
  114. nodes reachable from the start node."
  115. (let visit ((label start) (order '()) (visited empty-intset))
  116. (call-with-values
  117. (lambda ()
  118. (intset-fold (lambda (succ order visited)
  119. (if (intset-ref visited succ)
  120. (values order visited)
  121. (visit succ order visited)))
  122. (intmap-ref succs label)
  123. order
  124. (intset-add! visited label)))
  125. (lambda (order visited)
  126. ;; After visiting successors, add label to the reverse post-order.
  127. (values (cons label order) visited)))))
  128. (define (invert-graph succs)
  129. "Given a graph PRED->SUCC..., where PRED is a label and SUCC... is an
  130. intset of successors, return a graph SUCC->PRED...."
  131. (intmap-fold (lambda (pred succs preds)
  132. (intset-fold
  133. (lambda (succ preds)
  134. (intmap-add preds succ pred intset-add))
  135. succs
  136. preds))
  137. succs
  138. (intmap-map (lambda (label _) empty-intset) succs)))
  139. (define (compute-strongly-connected-components succs start)
  140. "Given a LABEL->SUCCESSOR... graph, compute a SCC->LABEL... map
  141. partitioning the labels into strongly connected components (SCCs)."
  142. (let ((preds (invert-graph succs)))
  143. (define (visit-scc scc sccs-by-label)
  144. (let visit ((label scc) (sccs-by-label sccs-by-label))
  145. (if (intmap-ref sccs-by-label label (lambda (_) #f))
  146. sccs-by-label
  147. (intset-fold visit
  148. (intmap-ref preds label)
  149. (intmap-add sccs-by-label label scc)))))
  150. (intmap-fold
  151. (lambda (label scc sccs)
  152. (let ((labels (intset-add empty-intset label)))
  153. (intmap-add sccs scc labels intset-union)))
  154. (fold visit-scc empty-intmap (compute-reverse-post-order succs start))
  155. empty-intmap)))
  156. (define (compute-sorted-strongly-connected-components edges)
  157. "Given a LABEL->SUCCESSOR... graph, return a list of strongly
  158. connected components in sorted order."
  159. (define nodes
  160. (intmap-keys edges))
  161. ;; Add a "start" node that links to all nodes in the graph, and then
  162. ;; remove it from the result.
  163. (define start
  164. (if (eq? nodes empty-intset)
  165. 0
  166. (1+ (intset-prev nodes))))
  167. (define components
  168. (intmap-remove
  169. (compute-strongly-connected-components (intmap-add edges start nodes)
  170. start)
  171. start))
  172. (define node-components
  173. (intmap-fold (lambda (id nodes out)
  174. (intset-fold (lambda (node out) (intmap-add out node id))
  175. nodes out))
  176. components
  177. empty-intmap))
  178. (define (node-component node)
  179. (intmap-ref node-components node))
  180. (define (component-successors id nodes)
  181. (intset-remove
  182. (intset-fold (lambda (node out)
  183. (intset-fold
  184. (lambda (successor out)
  185. (intset-add out (node-component successor)))
  186. (intmap-ref edges node)
  187. out))
  188. nodes
  189. empty-intset)
  190. id))
  191. (define component-edges
  192. (intmap-map component-successors components))
  193. (define preds
  194. (invert-graph component-edges))
  195. (define roots
  196. (intmap-fold (lambda (id succs out)
  197. (if (eq? empty-intset succs)
  198. (intset-add out id)
  199. out))
  200. component-edges
  201. empty-intset))
  202. ;; As above, add a "start" node that links to the roots, and remove it
  203. ;; from the result.
  204. (match (compute-reverse-post-order (intmap-add preds start roots) start)
  205. (((? (lambda (id) (eqv? id start))) . ids)
  206. (map (lambda (id) (intmap-ref components id)) ids))))
  207. (define (intset-pop set)
  208. (match (intset-next set)
  209. (#f (values set #f))
  210. (i (values (intset-remove set i) i))))
  211. (define* (solve-flow-equations succs in out kill gen subtract add meet
  212. #:optional (worklist (intmap-keys succs)))
  213. "Find a fixed point for flow equations for SUCCS, where INIT is the
  214. initial state at each node in SUCCS. KILL and GEN are intmaps
  215. indicating the state that is killed or defined at every node, and
  216. SUBTRACT, ADD, and MEET operates on that state."
  217. (define (visit label in out)
  218. (let* ((in-1 (intmap-ref in label))
  219. (kill-1 (intmap-ref kill label))
  220. (gen-1 (intmap-ref gen label))
  221. (out-1 (intmap-ref out label))
  222. (out-1* (add (subtract in-1 kill-1) gen-1)))
  223. (if (eq? out-1 out-1*)
  224. (values empty-intset in out)
  225. (let ((out (intmap-replace! out label out-1*)))
  226. (call-with-values
  227. (lambda ()
  228. (intset-fold (lambda (succ in changed)
  229. (let* ((in-1 (intmap-ref in succ))
  230. (in-1* (meet in-1 out-1*)))
  231. (if (eq? in-1 in-1*)
  232. (values in changed)
  233. (values (intmap-replace! in succ in-1*)
  234. (intset-add changed succ)))))
  235. (intmap-ref succs label) in empty-intset))
  236. (lambda (in changed)
  237. (values changed in out)))))))
  238. (let run ((worklist worklist) (in in) (out out))
  239. (call-with-values (lambda () (intset-pop worklist))
  240. (lambda (worklist popped)
  241. (if popped
  242. (call-with-values (lambda () (visit popped in out))
  243. (lambda (changed in out)
  244. (run (intset-union worklist changed) in out)))
  245. (values (persistent-intmap in)
  246. (persistent-intmap out)))))))