licm.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013, 2014, 2015 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 &type-check)))
  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) ($ $closure)) #t)
  63. (($ $prompt) #f) ;; ?
  64. (($ $branch) #f)
  65. (($ $primcall 'values) #f)
  66. (($ $primcall name args)
  67. (and-map (lambda (arg) (not (intset-ref loop-vars arg)))
  68. args))
  69. (($ $values args)
  70. (and-map (lambda (arg) (not (intset-ref loop-vars arg)))
  71. args))))))
  72. (define (hoist-one cps label cont preds
  73. loop-vars loop-effects pre-header-label always-reached?)
  74. (define (filter-loop-vars names vars)
  75. (match (vector names vars)
  76. (#((name . names) (var . vars))
  77. (if (intset-ref loop-vars var)
  78. (let-values (((names vars) (filter-loop-vars names vars)))
  79. (values (cons name names) (cons var vars)))
  80. (filter-loop-vars names vars)))
  81. (_ (values '() '()))))
  82. (define (adjoin-loop-vars loop-vars vars)
  83. (fold1 (lambda (var loop-vars) (intset-add loop-vars var))
  84. vars loop-vars))
  85. (define (hoist-exp src exp def-names def-vars pre-header-label)
  86. (let* ((hoisted-label pre-header-label)
  87. (pre-header-label (fresh-label))
  88. (hoisted-cont
  89. (rewrite-cont (intmap-ref cps hoisted-label)
  90. (($ $kargs names vars)
  91. ($kargs names vars
  92. ($continue pre-header-label src ,exp)))))
  93. (pre-header-cont
  94. (rewrite-cont (intmap-ref cps hoisted-label)
  95. (($ $kargs _ _ term)
  96. ($kargs def-names def-vars ,term)))))
  97. (values (intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
  98. pre-header-label pre-header-cont)
  99. pre-header-label)))
  100. (define (hoist-call src exp req rest def-names def-vars pre-header-label)
  101. (let* ((hoisted-label pre-header-label)
  102. (receive-label (fresh-label))
  103. (pre-header-label (fresh-label))
  104. (hoisted-cont
  105. (rewrite-cont (intmap-ref cps hoisted-label)
  106. (($ $kargs names vars)
  107. ($kargs names vars
  108. ($continue receive-label src ,exp)))))
  109. (receive-cont
  110. (build-cont
  111. ($kreceive req rest pre-header-label)))
  112. (pre-header-cont
  113. (rewrite-cont (intmap-ref cps hoisted-label)
  114. (($ $kargs _ _ term)
  115. ($kargs def-names def-vars ,term)))))
  116. (values (intmap-add!
  117. (intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
  118. receive-label receive-cont)
  119. pre-header-label pre-header-cont)
  120. pre-header-label)))
  121. (match cont
  122. (($ $kargs names vars ($ $continue k src exp))
  123. ;; If k is a loop exit, it will be nullary.
  124. (let-values (((names vars) (filter-loop-vars names vars)))
  125. (match (intmap-ref cps k)
  126. (($ $kargs def-names def-vars)
  127. (cond
  128. ((not (loop-invariant? label exp loop-vars loop-effects
  129. always-reached?))
  130. (let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
  131. (loop-vars (match exp
  132. (($ $prompt escape? tag handler)
  133. (match (intmap-ref cps handler)
  134. (($ $kreceive arity kargs)
  135. (match (intmap-ref cps kargs)
  136. (($ $kargs names vars)
  137. (adjoin-loop-vars loop-vars vars))))))
  138. (_ loop-vars)))
  139. (cont (build-cont
  140. ($kargs names vars
  141. ($continue k src ,exp))))
  142. (always-reached?
  143. (and always-reached?
  144. (match exp
  145. (($ $branch) #f)
  146. (_ (not (causes-effect? (intmap-ref loop-effects label)
  147. &type-check)))))))
  148. (values cps cont loop-vars loop-effects
  149. pre-header-label always-reached?)))
  150. ((trivial-intset (intmap-ref preds k))
  151. (let-values
  152. (((cps pre-header-label)
  153. (hoist-exp src exp def-names def-vars pre-header-label))
  154. ((cont) (build-cont
  155. ($kargs names vars
  156. ($continue k src ($values ()))))))
  157. (values cps cont loop-vars (intmap-remove loop-effects label)
  158. pre-header-label always-reached?)))
  159. (else
  160. (let*-values
  161. (((def-names def-vars)
  162. (match (intmap-ref cps k)
  163. (($ $kargs names vars) (values names vars))))
  164. ((loop-vars) (adjoin-loop-vars loop-vars def-vars))
  165. ((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
  166. ((cps pre-header-label)
  167. (hoist-exp src exp def-names fresh-vars pre-header-label))
  168. ((cont) (build-cont
  169. ($kargs names vars
  170. ($continue k src ($values fresh-vars))))))
  171. (values cps cont loop-vars (intmap-remove loop-effects label)
  172. pre-header-label always-reached?)))))
  173. (($ $kreceive ($ $arity req () rest) kargs)
  174. (match (intmap-ref cps kargs)
  175. (($ $kargs def-names def-vars)
  176. (cond
  177. ((not (loop-invariant? label exp loop-vars loop-effects
  178. always-reached?))
  179. (let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
  180. (cont (build-cont
  181. ($kargs names vars
  182. ($continue k src ,exp)))))
  183. (values cps cont loop-vars loop-effects pre-header-label #f)))
  184. ((trivial-intset (intmap-ref preds k))
  185. (let ((loop-effects
  186. (intmap-remove (intmap-remove loop-effects label) k)))
  187. (let-values
  188. (((cps pre-header-label)
  189. (hoist-call src exp req rest def-names def-vars
  190. pre-header-label))
  191. ((cont) (build-cont
  192. ($kargs names vars
  193. ($continue kargs src ($values ()))))))
  194. (values cps cont loop-vars loop-effects
  195. pre-header-label always-reached?))))
  196. (else
  197. (let*-values
  198. (((loop-vars) (adjoin-loop-vars loop-vars def-vars))
  199. ((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
  200. ((cps pre-header-label)
  201. (hoist-call src exp req rest def-names fresh-vars
  202. pre-header-label))
  203. ((cont) (build-cont
  204. ($kargs names vars
  205. ($continue kargs src
  206. ($values fresh-vars))))))
  207. (values cps cont loop-vars loop-effects
  208. pre-header-label always-reached?))))))))))
  209. (($ $kreceive ($ $arity req () rest) kargs)
  210. (values cps cont loop-vars loop-effects pre-header-label
  211. always-reached?))))
  212. (define (hoist-in-loop cps entry body-labels succs preds effects)
  213. (let* ((interior-succs (intmap-map (lambda (label succs)
  214. (intset-intersect succs body-labels))
  215. succs))
  216. (sorted-labels (compute-reverse-post-order interior-succs entry))
  217. (header-label (fresh-label))
  218. (header-cont (intmap-ref cps entry))
  219. (loop-vars (match header-cont
  220. (($ $kargs names vars) (list->intset vars))))
  221. (loop-effects (persistent-intmap
  222. (intset-fold
  223. (lambda (label loop-effects)
  224. (let ((label*
  225. (if (eqv? label entry) header-label label))
  226. (fx (intmap-ref effects label)))
  227. (intmap-add! loop-effects label* fx)))
  228. body-labels empty-intmap)))
  229. (pre-header-label entry)
  230. (pre-header-cont (match header-cont
  231. (($ $kargs names vars term)
  232. (let ((vars* (map (lambda (_) (fresh-var)) vars)))
  233. (build-cont
  234. ($kargs names vars*
  235. ($continue header-label #f
  236. ($values vars*))))))))
  237. (cps (intmap-add! cps header-label header-cont))
  238. (cps (intmap-replace! cps pre-header-label pre-header-cont))
  239. (to-visit (match sorted-labels
  240. ((head . tail)
  241. (unless (eqv? head entry) (error "what?"))
  242. (cons header-label tail)))))
  243. (define (rename-back-edges cont)
  244. (define (rename label) (if (eqv? label entry) header-label label))
  245. (rewrite-cont cont
  246. (($ $kargs names vars ($ $continue kf src ($ $branch kt exp)))
  247. ($kargs names vars
  248. ($continue (rename kf) src ($branch (rename kt) ,exp))))
  249. (($ $kargs names vars ($ $continue k src exp))
  250. ($kargs names vars
  251. ($continue (rename k) src ,exp)))
  252. (($ $kreceive ($ $arity req () rest) k)
  253. ($kreceive req rest (rename k)))))
  254. (let lp ((cps cps) (to-visit to-visit)
  255. (loop-vars loop-vars) (loop-effects loop-effects)
  256. (pre-header-label pre-header-label) (always-reached? #t))
  257. (match to-visit
  258. (() cps)
  259. ((label . to-visit)
  260. (call-with-values
  261. (lambda ()
  262. (hoist-one cps label (intmap-ref cps label) preds
  263. loop-vars loop-effects
  264. pre-header-label always-reached?))
  265. (lambda (cps cont
  266. loop-vars loop-effects pre-header-label always-reached?)
  267. (lp (intmap-replace! cps label (rename-back-edges cont)) to-visit
  268. loop-vars loop-effects pre-header-label always-reached?))))))))
  269. (define (hoist-in-function kfun body cps)
  270. (let* ((succs (compute-successors cps kfun))
  271. (preds (invert-graph succs))
  272. (loops (intmap-fold
  273. (lambda (id scc loops)
  274. (cond
  275. ((trivial-intset scc) loops)
  276. ((find-entry scc preds)
  277. => (lambda (entry) (intmap-add! loops entry scc)))
  278. (else loops)))
  279. (compute-strongly-connected-components succs kfun)
  280. empty-intmap)))
  281. (if (eq? empty-intset loops)
  282. cps
  283. (let ((effects (compute-effects/elide-type-checks
  284. (intset-fold (lambda (label body-conts)
  285. (intmap-add! body-conts label
  286. (intmap-ref cps label)))
  287. body empty-intmap))))
  288. (persistent-intmap
  289. (intmap-fold (lambda (entry scc cps)
  290. (hoist-in-loop cps entry scc succs preds effects))
  291. loops cps))))))
  292. (define (hoist-loop-invariant-code cps)
  293. (with-fresh-name-state cps
  294. (intmap-fold hoist-in-function
  295. (compute-reachable-functions cps)
  296. cps)))