graphs.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013-2015, 2017-2021 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 control)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (language cps intset)
  26. #:use-module (language cps intmap)
  27. #:export (;; Various utilities.
  28. fold1 fold2
  29. trivial-intset
  30. intmap-map
  31. intmap-keys
  32. invert-bijection invert-partition
  33. rename-keys rename-intset rename-graph
  34. intset->intmap
  35. intmap-select
  36. worklist-fold
  37. fixpoint
  38. ;; Flow analysis.
  39. invert-graph
  40. compute-reverse-post-order
  41. compute-strongly-connected-components
  42. compute-sorted-strongly-connected-components
  43. compute-reverse-control-flow-order
  44. solve-flow-equations
  45. compute-live-variables))
  46. (define-inlinable (fold1 f l s0)
  47. (let lp ((l l) (s0 s0))
  48. (match l
  49. (() s0)
  50. ((elt . l) (lp l (f elt s0))))))
  51. (define-inlinable (fold2 f l s0 s1)
  52. (let lp ((l l) (s0 s0) (s1 s1))
  53. (match l
  54. (() (values s0 s1))
  55. ((elt . l)
  56. (call-with-values (lambda () (f elt s0 s1))
  57. (lambda (s0 s1)
  58. (lp l s0 s1)))))))
  59. (define (trivial-intset set)
  60. "Returns the sole member of @var{set}, if @var{set} has exactly one
  61. member, or @code{#f} otherwise."
  62. (let ((first (intset-next set)))
  63. (and first
  64. (not (intset-next set (1+ first)))
  65. first)))
  66. (define (intmap-map proc map)
  67. (persistent-intmap
  68. (intmap-fold (lambda (k v out) (intmap-add! out k (proc k v)))
  69. map
  70. empty-intmap)))
  71. (define (intmap-keys map)
  72. "Return an intset of the keys in @var{map}."
  73. (persistent-intset
  74. (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
  75. (define (invert-bijection map)
  76. "Assuming the values of @var{map} are integers and are unique, compute
  77. a map in which each value maps to its key. If the values are not
  78. unique, an error will be signaled."
  79. (persistent-intmap
  80. (intmap-fold (lambda (k v out) (intmap-add! out v k)) map empty-intmap)))
  81. (define (invert-partition map)
  82. "Assuming the values of @var{map} are disjoint intsets, compute a map
  83. in which each member of each set maps to its key. If the values are not
  84. disjoint, an error will be signaled."
  85. (intmap-fold (lambda (k v* out)
  86. (intset-fold (lambda (v out) (intmap-add out v k)) v* out))
  87. map empty-intmap))
  88. (define (intset->intmap f set)
  89. (persistent-intmap
  90. (intset-fold (lambda (label preds)
  91. (intmap-add! preds label (f label)))
  92. set empty-intmap)))
  93. (define (intmap-select map set)
  94. (persistent-intmap
  95. (intset-fold (lambda (label out)
  96. (intmap-add! out label (intmap-ref map label)))
  97. set empty-intmap)))
  98. (define worklist-fold
  99. (case-lambda
  100. ((f in out)
  101. (let lp ((in in) (out out))
  102. (if (eq? in empty-intset)
  103. out
  104. (call-with-values (lambda () (f in out)) lp))))
  105. ((f in out0 out1)
  106. (let lp ((in in) (out0 out0) (out1 out1))
  107. (if (eq? in empty-intset)
  108. (values out0 out1)
  109. (call-with-values (lambda () (f in out0 out1)) lp))))))
  110. (define fixpoint
  111. (case-lambda
  112. ((f x)
  113. (let lp ((x x))
  114. (let ((x* (f x)))
  115. (if (eq? x x*) x* (lp x*)))))
  116. ((f x0 x1)
  117. (let lp ((x0 x0) (x1 x1))
  118. (call-with-values (lambda () (f x0 x1))
  119. (lambda (x0* x1*)
  120. (if (and (eq? x0 x0*) (eq? x1 x1*))
  121. (values x0* x1*)
  122. (lp x0* x1*))))))))
  123. (define (compute-reverse-post-order succs start)
  124. "Compute a reverse post-order numbering for a depth-first walk over
  125. nodes reachable from the start node."
  126. (let visit ((label start) (order '()) (visited empty-intset))
  127. (call-with-values
  128. (lambda ()
  129. (intset-fold (lambda (succ order visited)
  130. (if (intset-ref visited succ)
  131. (values order visited)
  132. (visit succ order visited)))
  133. (intmap-ref succs label)
  134. order
  135. (intset-add! visited label)))
  136. (lambda (order visited)
  137. ;; After visiting successors, add label to the reverse post-order.
  138. (values (cons label order) visited)))))
  139. (define (invert-graph succs)
  140. "Given a graph PRED->SUCC..., where PRED is a label and SUCC... is an
  141. intset of successors, return a graph SUCC->PRED...."
  142. (intmap-fold (lambda (pred succs preds)
  143. (intset-fold
  144. (lambda (succ preds)
  145. (intmap-add preds succ pred intset-add))
  146. succs
  147. preds))
  148. succs
  149. (intmap-map (lambda (label _) empty-intset) succs)))
  150. (define (rename-keys map old->new)
  151. "Return a fresh intmap containing F(K) -> V for K and V in MAP, where
  152. F is looking up K in the intmap OLD->NEW."
  153. (persistent-intmap
  154. (intmap-fold (lambda (k v out)
  155. (intmap-add! out (intmap-ref old->new k) v))
  156. map
  157. empty-intmap)))
  158. (define (rename-intset set old->new)
  159. "Return a fresh intset of F(K) for K in SET, where F is looking up K
  160. in the intmap OLD->NEW."
  161. (intset-fold (lambda (old set) (intset-add set (intmap-ref old->new old)))
  162. set empty-intset))
  163. (define (rename-graph graph old->new)
  164. "Return a fresh intmap containing F(K) -> intset(F(V)...) for K and
  165. intset(V...) in GRAPH, where F is looking up K in the intmap OLD->NEW."
  166. (persistent-intmap
  167. (intmap-fold (lambda (pred succs out)
  168. (intmap-add! out
  169. (intmap-ref old->new pred)
  170. (rename-intset succs old->new)))
  171. graph
  172. empty-intmap)))
  173. (define (compute-strongly-connected-components succs start)
  174. "Given a LABEL->SUCCESSOR... graph, compute a SCC->LABEL... map
  175. partitioning the labels into strongly connected components (SCCs)."
  176. (let ((preds (invert-graph succs)))
  177. (define (visit-scc scc sccs-by-label)
  178. (let visit ((label scc) (sccs-by-label sccs-by-label))
  179. (if (intmap-ref sccs-by-label label (lambda (_) #f))
  180. sccs-by-label
  181. (intset-fold visit
  182. (intmap-ref preds label)
  183. (intmap-add sccs-by-label label scc)))))
  184. (intmap-fold
  185. (lambda (label scc sccs)
  186. (let ((labels (intset-add empty-intset label)))
  187. (intmap-add sccs scc labels intset-union)))
  188. (fold visit-scc empty-intmap (compute-reverse-post-order succs start))
  189. empty-intmap)))
  190. (define (compute-sorted-strongly-connected-components edges)
  191. "Given a LABEL->SUCCESSOR... graph, return a list of strongly
  192. connected components in sorted order."
  193. (define nodes
  194. (intmap-keys edges))
  195. ;; Add a "start" node that links to all nodes in the graph, and then
  196. ;; remove it from the result.
  197. (define start
  198. (if (eq? nodes empty-intset)
  199. 0
  200. (1+ (intset-prev nodes))))
  201. (define components
  202. (intmap-remove
  203. (compute-strongly-connected-components (intmap-add edges start nodes)
  204. start)
  205. start))
  206. (define node-components
  207. (intmap-fold (lambda (id nodes out)
  208. (intset-fold (lambda (node out) (intmap-add out node id))
  209. nodes out))
  210. components
  211. empty-intmap))
  212. (define (node-component node)
  213. (intmap-ref node-components node))
  214. (define (component-successors id nodes)
  215. (intset-remove
  216. (intset-fold (lambda (node out)
  217. (intset-fold
  218. (lambda (successor out)
  219. (intset-add out (node-component successor)))
  220. (intmap-ref edges node)
  221. out))
  222. nodes
  223. empty-intset)
  224. id))
  225. (define component-edges
  226. (intmap-map component-successors components))
  227. (define preds
  228. (invert-graph component-edges))
  229. (define roots
  230. (intmap-fold (lambda (id succs out)
  231. (if (eq? empty-intset succs)
  232. (intset-add out id)
  233. out))
  234. component-edges
  235. empty-intset))
  236. ;; As above, add a "start" node that links to the roots, and remove it
  237. ;; from the result.
  238. (match (compute-reverse-post-order (intmap-add preds start roots) start)
  239. (((? (lambda (id) (eqv? id start))) . ids)
  240. (map (lambda (id) (intmap-ref components id)) ids))))
  241. (define (compute-reverse-control-flow-order preds)
  242. "Return a LABEL->ORDER bijection where ORDER is a contiguous set of
  243. integers starting from 0 and incrementing in sort order. There is a
  244. precondition that labels in PREDS are already renumbered in reverse post
  245. order."
  246. (define (has-back-edge? preds)
  247. (let/ec return
  248. (intmap-fold (lambda (label labels)
  249. (intset-fold (lambda (pred)
  250. (if (<= label pred)
  251. (return #t)
  252. (values)))
  253. labels)
  254. (values))
  255. preds)
  256. #f))
  257. (if (has-back-edge? preds)
  258. ;; This is more involved than forward control flow because not all
  259. ;; live labels are reachable from the tail.
  260. (persistent-intmap
  261. (fold2 (lambda (component order n)
  262. (intset-fold (lambda (label order n)
  263. (values (intmap-add! order label n)
  264. (1+ n)))
  265. component order n))
  266. (reverse (compute-sorted-strongly-connected-components preds))
  267. empty-intmap 0))
  268. ;; Just reverse forward control flow.
  269. (let ((max (intmap-prev preds)))
  270. (intmap-map (lambda (label labels) (- max label)) preds))))
  271. (define (intset-pop set)
  272. (match (intset-next set)
  273. (#f (values set #f))
  274. (i (values (intset-remove set i) i))))
  275. (define* (solve-flow-equations succs in out kill gen subtract add meet
  276. #:optional (worklist (intmap-keys succs)))
  277. "Find a fixed point for flow equations for SUCCS, where INIT is the
  278. initial state at each node in SUCCS. KILL and GEN are intmaps
  279. indicating the state that is killed or defined at every node, and
  280. SUBTRACT, ADD, and MEET operates on that state."
  281. (define (visit label in out)
  282. (let* ((in-1 (intmap-ref in label))
  283. (kill-1 (intmap-ref kill label))
  284. (gen-1 (intmap-ref gen label))
  285. (out-1 (intmap-ref out label))
  286. (out-1* (add (subtract in-1 kill-1) gen-1)))
  287. (if (eq? out-1 out-1*)
  288. (values empty-intset in out)
  289. (let ((out (intmap-replace! out label out-1*)))
  290. (call-with-values
  291. (lambda ()
  292. (intset-fold (lambda (succ in changed)
  293. (let* ((in-1 (intmap-ref in succ))
  294. (in-1* (meet in-1 out-1*)))
  295. (if (eq? in-1 in-1*)
  296. (values in changed)
  297. (values (intmap-replace! in succ in-1*)
  298. (intset-add changed succ)))))
  299. (intmap-ref succs label) in empty-intset))
  300. (lambda (in changed)
  301. (values changed in out)))))))
  302. (let run ((worklist worklist) (in in) (out out))
  303. (call-with-values (lambda () (intset-pop worklist))
  304. (lambda (worklist popped)
  305. (if popped
  306. (call-with-values (lambda () (visit popped in out))
  307. (lambda (changed in out)
  308. (run (intset-union worklist changed) in out)))
  309. (values (persistent-intmap in)
  310. (persistent-intmap out)))))))
  311. (define (compute-live-variables preds defs uses)
  312. "Compute and return two values mapping LABEL->VAR..., where VAR... are
  313. the definitions that are live before and after LABEL, as intsets."
  314. (let* ((old->new (compute-reverse-control-flow-order preds))
  315. (init (persistent-intmap (intmap-fold
  316. (lambda (old new init)
  317. (intmap-add! init new empty-intset))
  318. old->new empty-intmap))))
  319. (call-with-values
  320. (lambda ()
  321. (solve-flow-equations (rename-graph preds old->new)
  322. init init
  323. (rename-keys defs old->new)
  324. (rename-keys uses old->new)
  325. intset-subtract intset-union intset-union))
  326. (lambda (in out)
  327. ;; As a reverse control-flow problem, the values flowing into a
  328. ;; node are actually the live values after the node executes.
  329. ;; Funny, innit? So we return them in the reverse order.
  330. (let ((new->old (invert-bijection old->new)))
  331. (values (rename-keys out new->old)
  332. (rename-keys in new->old)))))))