licm.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013, 2014, 2015, 2017, 2018 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. ;;; Loop invariant code motion (LICM) hoists terms that don't affect a
  19. ;;; loop out of the loop, so that the loop goes faster.
  20. ;;;
  21. ;;; Code:
  22. (define-module (language cps licm)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-11)
  25. #:use-module (language cps)
  26. #:use-module (language cps utils)
  27. #:use-module (language cps intmap)
  28. #:use-module (language cps intset)
  29. #:use-module (language cps effects-analysis)
  30. #:use-module (language cps type-checks)
  31. #:export (hoist-loop-invariant-code))
  32. (define (find-exits scc succs)
  33. (intset-fold (lambda (label exits)
  34. (if (eq? empty-intset
  35. (intset-subtract (intmap-ref succs label) scc))
  36. exits
  37. (intset-add exits label)))
  38. scc
  39. empty-intset))
  40. (define (find-entry scc preds)
  41. (trivial-intset (find-exits scc preds)))
  42. (define (list->intset l)
  43. (persistent-intset
  44. (fold1 (lambda (i set) (intset-add! set i)) l empty-intset)))
  45. (define (loop-invariant? label exp loop-vars loop-effects always-reached?)
  46. (let ((fx (intmap-ref loop-effects label)))
  47. (and
  48. (not (causes-effect? fx &allocation))
  49. (or always-reached?
  50. (not (causes-effect? fx (logior &type-check &read &write))))
  51. (or (not (causes-effect? fx &write))
  52. (intmap-fold (lambda (label fx* invariant?)
  53. (and invariant?
  54. (not (effect-clobbers? fx fx*))))
  55. loop-effects #t))
  56. (or (not (causes-effect? fx &read))
  57. (intmap-fold (lambda (label fx* invariant?)
  58. (and invariant?
  59. (not (effect-clobbers? fx* fx))))
  60. loop-effects #t))
  61. (match exp
  62. ((or ($ $const) ($ $prim) ($ $const-fun) ($ $code)) #t)
  63. (($ $primcall name param args)
  64. (and-map (lambda (arg) (not (intset-ref loop-vars arg)))
  65. args))
  66. (($ $values args)
  67. (and-map (lambda (arg) (not (intset-ref loop-vars arg)))
  68. args))))))
  69. (define (hoist-one cps label cont preds
  70. loop-vars loop-effects pre-header-label always-reached?)
  71. (define (filter-loop-vars names vars)
  72. (match (vector names vars)
  73. (#((name . names) (var . vars))
  74. (if (intset-ref loop-vars var)
  75. (let-values (((names vars) (filter-loop-vars names vars)))
  76. (values (cons name names) (cons var vars)))
  77. (filter-loop-vars names vars)))
  78. (_ (values '() '()))))
  79. (define (adjoin-loop-vars loop-vars vars)
  80. (fold1 (lambda (var loop-vars) (intset-add loop-vars var))
  81. vars loop-vars))
  82. (define (hoist-exp src exp def-names def-vars pre-header-label)
  83. (let* ((hoisted-label pre-header-label)
  84. (pre-header-label (fresh-label))
  85. (hoisted-cont
  86. (rewrite-cont (intmap-ref cps hoisted-label)
  87. (($ $kargs names vars)
  88. ($kargs names vars
  89. ($continue pre-header-label src ,exp)))))
  90. (pre-header-cont
  91. (rewrite-cont (intmap-ref cps hoisted-label)
  92. (($ $kargs _ _ term)
  93. ($kargs def-names def-vars ,term)))))
  94. (values (intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
  95. pre-header-label pre-header-cont)
  96. pre-header-label)))
  97. (define (hoist-call src exp req rest def-names def-vars pre-header-label)
  98. (let* ((hoisted-label pre-header-label)
  99. (receive-label (fresh-label))
  100. (pre-header-label (fresh-label))
  101. (hoisted-cont
  102. (rewrite-cont (intmap-ref cps hoisted-label)
  103. (($ $kargs names vars)
  104. ($kargs names vars
  105. ($continue receive-label src ,exp)))))
  106. (receive-cont
  107. (build-cont
  108. ($kreceive req rest pre-header-label)))
  109. (pre-header-cont
  110. (rewrite-cont (intmap-ref cps hoisted-label)
  111. (($ $kargs _ _ term)
  112. ($kargs def-names def-vars ,term)))))
  113. (values (intmap-add!
  114. (intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
  115. receive-label receive-cont)
  116. pre-header-label pre-header-cont)
  117. pre-header-label)))
  118. (match cont
  119. (($ $kargs names vars term)
  120. (let-values (((names vars) (filter-loop-vars names vars)))
  121. (match term
  122. (($ $continue k src exp)
  123. ;; If k is a loop exit, it will be nullary.
  124. (match (intmap-ref cps k)
  125. (($ $kargs def-names def-vars)
  126. (cond
  127. ((not (loop-invariant? label exp loop-vars loop-effects
  128. always-reached?))
  129. (let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
  130. (cont (build-cont
  131. ($kargs names vars
  132. ($continue k src ,exp))))
  133. (always-reached?
  134. (and always-reached?
  135. (not (causes-effect? (intmap-ref loop-effects label)
  136. &type-check)))))
  137. (values cps cont loop-vars loop-effects
  138. pre-header-label always-reached?)))
  139. ((trivial-intset (intmap-ref preds k))
  140. (let-values
  141. (((cps pre-header-label)
  142. (hoist-exp src exp def-names def-vars pre-header-label))
  143. ((cont) (build-cont
  144. ($kargs names vars
  145. ($continue k src ($values ()))))))
  146. (values cps cont loop-vars (intmap-remove loop-effects label)
  147. pre-header-label always-reached?)))
  148. (else
  149. (let*-values
  150. (((def-names def-vars)
  151. (match (intmap-ref cps k)
  152. (($ $kargs names vars) (values names vars))))
  153. ((loop-vars) (adjoin-loop-vars loop-vars def-vars))
  154. ((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
  155. ((cps pre-header-label)
  156. (hoist-exp src exp def-names fresh-vars pre-header-label))
  157. ((cont) (build-cont
  158. ($kargs names vars
  159. ($continue k src ($values fresh-vars))))))
  160. (values cps cont loop-vars (intmap-remove loop-effects label)
  161. pre-header-label always-reached?)))))
  162. (($ $kreceive ($ $arity req () rest) kargs)
  163. (match (intmap-ref cps kargs)
  164. (($ $kargs def-names def-vars)
  165. (cond
  166. ((not (loop-invariant? label exp loop-vars loop-effects
  167. always-reached?))
  168. (let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
  169. (cont (build-cont
  170. ($kargs names vars
  171. ($continue k src ,exp)))))
  172. (values cps cont loop-vars loop-effects pre-header-label #f)))
  173. ((trivial-intset (intmap-ref preds k))
  174. (let ((loop-effects
  175. (intmap-remove (intmap-remove loop-effects label) k)))
  176. (let-values
  177. (((cps pre-header-label)
  178. (hoist-call src exp req rest def-names def-vars
  179. pre-header-label))
  180. ((cont) (build-cont
  181. ($kargs names vars
  182. ($continue kargs src ($values ()))))))
  183. (values cps cont loop-vars loop-effects
  184. pre-header-label always-reached?))))
  185. (else
  186. (let*-values
  187. (((loop-vars) (adjoin-loop-vars loop-vars def-vars))
  188. ((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
  189. ((cps pre-header-label)
  190. (hoist-call src exp req rest def-names fresh-vars
  191. pre-header-label))
  192. ((cont) (build-cont
  193. ($kargs names vars
  194. ($continue kargs src
  195. ($values fresh-vars))))))
  196. (values cps cont loop-vars loop-effects
  197. pre-header-label always-reached?)))))))))
  198. ((or ($ $branch) ($ $throw))
  199. (let* ((cont (build-cont ($kargs names vars ,term)))
  200. (always-reached? #f))
  201. (values cps cont loop-vars loop-effects
  202. pre-header-label always-reached?)))
  203. (($ $prompt k kh src escape? tag)
  204. (let* ((loop-vars (match (intmap-ref cps kh)
  205. (($ $kreceive arity kargs)
  206. (match (intmap-ref cps kargs)
  207. (($ $kargs names vars)
  208. (adjoin-loop-vars loop-vars vars))))))
  209. (cont (build-cont ($kargs names vars ,term)))
  210. (always-reached? #f))
  211. (values cps cont loop-vars loop-effects
  212. pre-header-label always-reached?))))))
  213. (($ $kreceive ($ $arity req () rest) kargs)
  214. (values cps cont loop-vars loop-effects pre-header-label
  215. always-reached?))))
  216. (define (hoist-in-loop cps entry body-labels succs preds effects)
  217. (let* ((interior-succs (intmap-map (lambda (label succs)
  218. (intset-intersect succs body-labels))
  219. succs))
  220. (sorted-labels (compute-reverse-post-order interior-succs entry))
  221. (header-label (fresh-label))
  222. (header-cont (intmap-ref cps entry))
  223. (loop-vars (match header-cont
  224. (($ $kargs names vars) (list->intset vars))))
  225. (loop-effects (persistent-intmap
  226. (intset-fold
  227. (lambda (label loop-effects)
  228. (let ((label*
  229. (if (eqv? label entry) header-label label))
  230. (fx (intmap-ref effects label)))
  231. (intmap-add! loop-effects label* fx)))
  232. body-labels empty-intmap)))
  233. (pre-header-label entry)
  234. (pre-header-cont (match header-cont
  235. (($ $kargs names vars term)
  236. (let ((vars* (map (lambda (_) (fresh-var)) vars)))
  237. (build-cont
  238. ($kargs names vars*
  239. ($continue header-label #f
  240. ($values vars*))))))))
  241. (cps (intmap-add! cps header-label header-cont))
  242. (cps (intmap-replace! cps pre-header-label pre-header-cont))
  243. (to-visit (match sorted-labels
  244. ((head . tail)
  245. (unless (eqv? head entry) (error "what?"))
  246. (cons header-label tail)))))
  247. (define (rename-back-edges cont)
  248. (define (rename label) (if (eqv? label entry) header-label label))
  249. (rewrite-cont cont
  250. (($ $kargs names vars ($ $branch kf kt src op param args))
  251. ($kargs names vars
  252. ($branch (rename kf) (rename kt) src op param args)))
  253. (($ $kargs names vars ($ $prompt k kh src escape? tag))
  254. ($kargs names vars
  255. ($prompt (rename k) (rename kh) src escape? tag)))
  256. (($ $kargs names vars ($ $continue k src exp))
  257. ($kargs names vars
  258. ($continue (rename k) src ,exp)))
  259. (($ $kreceive ($ $arity req () rest) k)
  260. ($kreceive req rest (rename k)))))
  261. (let lp ((cps cps) (to-visit to-visit)
  262. (loop-vars loop-vars) (loop-effects loop-effects)
  263. (pre-header-label pre-header-label) (always-reached? #t))
  264. (match to-visit
  265. (() cps)
  266. ((label . to-visit)
  267. (call-with-values
  268. (lambda ()
  269. (hoist-one cps label (intmap-ref cps label) preds
  270. loop-vars loop-effects
  271. pre-header-label always-reached?))
  272. (lambda (cps cont
  273. loop-vars loop-effects pre-header-label always-reached?)
  274. (lp (intmap-replace! cps label (rename-back-edges cont)) to-visit
  275. loop-vars loop-effects pre-header-label always-reached?))))))))
  276. (define (hoist-in-function kfun body cps)
  277. (let* ((succs (compute-successors cps kfun))
  278. (preds (invert-graph succs))
  279. (loops (intmap-fold
  280. (lambda (id scc loops)
  281. (cond
  282. ((trivial-intset scc) loops)
  283. ((find-entry scc preds)
  284. => (lambda (entry) (intmap-add! loops entry scc)))
  285. (else loops)))
  286. (compute-strongly-connected-components succs kfun)
  287. empty-intmap)))
  288. (if (eq? empty-intset loops)
  289. cps
  290. (let ((effects (compute-effects/elide-type-checks
  291. (intset-fold (lambda (label body-conts)
  292. (intmap-add! body-conts label
  293. (intmap-ref cps label)))
  294. body empty-intmap))))
  295. (persistent-intmap
  296. (intmap-fold (lambda (entry scc cps)
  297. (hoist-in-loop cps entry scc succs preds effects))
  298. loops cps))))))
  299. (define (hoist-loop-invariant-code cps)
  300. (with-fresh-name-state cps
  301. (intmap-fold hoist-in-function
  302. (compute-reachable-functions cps)
  303. cps)))