contification.scm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. ;;; Contification is a pass that turns $fun instances into $cont
  19. ;;; instances if all calls to the $fun return to the same continuation.
  20. ;;; This is a more rigorous variant of our old "fixpoint labels
  21. ;;; allocation" optimization.
  22. ;;;
  23. ;;; See Kennedy's "Compiling with Continuations, Continued", and Fluet
  24. ;;; and Weeks's "Contification using Dominators".
  25. ;;;
  26. ;;; Code:
  27. (define-module (language cps contification)
  28. #:use-module (ice-9 match)
  29. #:use-module (srfi srfi-11)
  30. #:use-module ((srfi srfi-1) #:select (fold))
  31. #:use-module (language cps)
  32. #:use-module (language cps renumber)
  33. #:use-module (language cps utils)
  34. #:use-module (language cps intmap)
  35. #:use-module (language cps intset)
  36. #:export (contify))
  37. (define (compute-singly-referenced-labels conts)
  38. "Compute the set of labels in CONTS that have exactly one
  39. predecessor."
  40. (define (add-ref label cont single multiple)
  41. (define (ref k single multiple)
  42. (if (intset-ref single k)
  43. (values single (intset-add! multiple k))
  44. (values (intset-add! single k) multiple)))
  45. (define (ref0) (values single multiple))
  46. (define (ref1 k) (ref k single multiple))
  47. (define (ref2 k k*)
  48. (if k*
  49. (let-values (((single multiple) (ref k single multiple)))
  50. (ref k* single multiple))
  51. (ref1 k)))
  52. (match cont
  53. (($ $kreceive arity k) (ref1 k))
  54. (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
  55. (($ $ktail) (ref0))
  56. (($ $kclause arity kbody kalt) (ref2 kbody kalt))
  57. (($ $kargs names syms ($ $continue k src exp))
  58. (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
  59. (let*-values (((single multiple) (values empty-intset empty-intset))
  60. ((single multiple) (intmap-fold add-ref conts single multiple)))
  61. (intset-subtract (persistent-intset single)
  62. (persistent-intset multiple))))
  63. (define (compute-functions conts)
  64. "Compute a map from $kfun label to bound variable names for all
  65. functions in CONTS. Functions have two bound variable names: their self
  66. binding, and the name they are given in their continuation. If their
  67. continuation has more than one predecessor, then the bound variable name
  68. doesn't uniquely identify the function, so we exclude that function from
  69. the set."
  70. (define (function-self label)
  71. (match (intmap-ref conts label)
  72. (($ $kfun src meta self) self)))
  73. (let ((single (compute-singly-referenced-labels conts)))
  74. (intmap-fold (lambda (label cont functions)
  75. (match cont
  76. (($ $kargs _ _ ($ $continue k src ($ $fun kfun)))
  77. (if (intset-ref single k)
  78. (match (intmap-ref conts k)
  79. (($ $kargs (name) (var))
  80. (intmap-add functions kfun
  81. (intset var (function-self kfun)))))
  82. functions))
  83. (($ $kargs _ _ ($ $continue k src
  84. ($ $rec _ vars (($ $fun kfuns) ...))))
  85. (if (intset-ref single k)
  86. (fold (lambda (var kfun functions)
  87. (intmap-add functions kfun
  88. (intset var (function-self kfun))))
  89. functions vars kfuns)
  90. functions))
  91. (_ functions)))
  92. conts
  93. empty-intmap)))
  94. (define (compute-multi-clause conts)
  95. "Compute an set containing all labels that are part of a multi-clause
  96. case-lambda. See the note in compute-contification-candidates."
  97. (define (multi-clause? clause)
  98. (and clause
  99. (match (intmap-ref conts clause)
  100. (($ $kclause arity body alt)
  101. alt))))
  102. (intmap-fold (lambda (label cont multi)
  103. (match cont
  104. (($ $kfun src meta self tail clause)
  105. (if (multi-clause? clause)
  106. (intset-union multi (compute-function-body conts label))
  107. multi))
  108. (_ multi)))
  109. conts
  110. empty-intset))
  111. (define (compute-arities conts functions)
  112. "Given the map FUNCTIONS whose keys are $kfun labels, return a map
  113. from label to arities."
  114. (define (clause-arities clause)
  115. (if clause
  116. (match (intmap-ref conts clause)
  117. (($ $kclause arity body alt)
  118. (cons arity (clause-arities alt))))
  119. '()))
  120. (intmap-map (lambda (label vars)
  121. (match (intmap-ref conts label)
  122. (($ $kfun src meta self tail clause)
  123. (clause-arities clause))))
  124. functions))
  125. ;; For now, we don't contify functions with optional, keyword, or rest
  126. ;; arguments.
  127. (define (contifiable-arity? arity)
  128. (match arity
  129. (($ $arity req () #f () aok?)
  130. #t)
  131. (_
  132. #f)))
  133. (define (arity-matches? arity nargs)
  134. (match arity
  135. (($ $arity req () #f () aok?)
  136. (= nargs (length req)))
  137. (_
  138. #f)))
  139. (define (compute-contification-candidates conts)
  140. "Compute and return a label -> (variable ...) map describing all
  141. functions with known uses that are only ever used as the operator of a
  142. $call, and are always called with a compatible arity."
  143. (let* ((functions (compute-functions conts))
  144. (multi-clause (compute-multi-clause conts))
  145. (vars (intmap-fold (lambda (label vars out)
  146. (intset-fold (lambda (var out)
  147. (intmap-add out var label))
  148. vars out))
  149. functions
  150. empty-intmap))
  151. (arities (compute-arities conts functions)))
  152. (define (restrict-arity functions proc nargs)
  153. (match (intmap-ref vars proc (lambda (_) #f))
  154. (#f functions)
  155. (label
  156. (let lp ((arities (intmap-ref arities label)))
  157. (match arities
  158. (() (intmap-remove functions label))
  159. ((arity . arities)
  160. (cond
  161. ((not (contifiable-arity? arity)) (lp '()))
  162. ((arity-matches? arity nargs) functions)
  163. (else (lp arities)))))))))
  164. (define (visit-cont label cont functions)
  165. (define (exclude-var functions var)
  166. (match (intmap-ref vars var (lambda (_) #f))
  167. (#f functions)
  168. (label (intmap-remove functions label))))
  169. (define (exclude-vars functions vars)
  170. (match vars
  171. (() functions)
  172. ((var . vars)
  173. (exclude-vars (exclude-var functions var) vars))))
  174. (match cont
  175. (($ $kargs _ _ ($ $continue _ _ exp))
  176. (match exp
  177. ((or ($ $const) ($ $prim) ($ $closure) ($ $fun) ($ $rec))
  178. functions)
  179. (($ $values args)
  180. (exclude-vars functions args))
  181. (($ $call proc args)
  182. (let ((functions (exclude-vars functions args)))
  183. ;; This contification algorithm is happy to contify the
  184. ;; `lp' in this example into a shared tail between clauses:
  185. ;;
  186. ;; (letrec ((lp (lambda () (lp))))
  187. ;; (case-lambda
  188. ;; ((a) (lp))
  189. ;; ((a b) (lp))))
  190. ;;
  191. ;; However because the current compilation pipeline has to
  192. ;; re-nest continuations into old CPS, there would be no
  193. ;; scope in which the tail would be valid. So, until the
  194. ;; old compilation pipeline is completely replaced,
  195. ;; conservatively exclude contifiable fucntions called
  196. ;; from multi-clause procedures.
  197. (if (intset-ref multi-clause label)
  198. (exclude-var functions proc)
  199. (restrict-arity functions proc (length args)))))
  200. (($ $callk k proc args)
  201. (exclude-vars functions (cons proc args)))
  202. (($ $branch kt ($ $primcall name args))
  203. (exclude-vars functions args))
  204. (($ $branch kt ($ $values (arg)))
  205. (exclude-var functions arg))
  206. (($ $primcall name args)
  207. (exclude-vars functions args))
  208. (($ $prompt escape? tag handler)
  209. (exclude-var functions tag))))
  210. (_ functions)))
  211. (intmap-fold visit-cont conts functions)))
  212. (define (compute-call-graph conts labels vars)
  213. "Given the set of contifiable functions LABELS and associated bound
  214. variables VARS, compute and return two values: a map
  215. LABEL->LABEL... indicating the contifiable functions called by a
  216. function, and a map LABEL->LABEL... indicating the return continuations
  217. for a function. The first return value also has an entry
  218. 0->LABEL... indicating all contifiable functions called by
  219. non-contifiable functions. We assume that 0 is not in the contifiable
  220. function set."
  221. (let ((bodies
  222. ;; label -> fun-label for all labels in bodies of contifiable
  223. ;; functions
  224. (intset-fold (lambda (fun-label bodies)
  225. (intset-fold (lambda (label bodies)
  226. (intmap-add bodies label fun-label))
  227. (compute-function-body conts fun-label)
  228. bodies))
  229. labels
  230. empty-intmap)))
  231. (when (intset-ref labels 0)
  232. (error "internal error: label 0 should not be contifiable"))
  233. (intmap-fold
  234. (lambda (label cont calls returns)
  235. (match cont
  236. (($ $kargs _ _ ($ $continue k src ($ $call proc)))
  237. (match (intmap-ref vars proc (lambda (_) #f))
  238. (#f (values calls returns))
  239. (callee
  240. (let ((caller (intmap-ref bodies label (lambda (_) 0))))
  241. (values (intmap-add calls caller callee intset-add)
  242. (intmap-add returns callee k intset-add))))))
  243. (_ (values calls returns))))
  244. conts
  245. (intset->intmap (lambda (label) empty-intset) (intset-add labels 0))
  246. (intset->intmap (lambda (label) empty-intset) labels))))
  247. (define (tail-label conts label)
  248. (match (intmap-ref conts label)
  249. (($ $kfun src meta self tail body)
  250. tail)))
  251. (define (compute-return-labels labels tails returns return-substs)
  252. (define (subst k)
  253. (match (intmap-ref return-substs k (lambda (_) #f))
  254. (#f k)
  255. (k (subst k))))
  256. ;; Compute all return labels, then subtract tail labels of the
  257. ;; functions in question.
  258. (intset-subtract
  259. ;; Return labels for all calls to these labels.
  260. (intset-fold (lambda (label out)
  261. (intset-fold (lambda (k out)
  262. (intset-add out (subst k)))
  263. (intmap-ref returns label)
  264. out))
  265. labels
  266. empty-intset)
  267. (intset-fold (lambda (label out)
  268. (intset-add out (intmap-ref tails label)))
  269. labels
  270. empty-intset)))
  271. (define (intmap->intset map)
  272. (define (add-key label cont labels)
  273. (intset-add labels label))
  274. (intmap-fold add-key map empty-intset))
  275. (define (filter-contifiable contified groups)
  276. (intmap-fold (lambda (id labels groups)
  277. (let ((labels (intset-subtract labels contified)))
  278. (if (eq? empty-intset labels)
  279. groups
  280. (intmap-add groups id labels))))
  281. groups
  282. empty-intmap))
  283. (define (trivial-set set)
  284. (let ((first (intset-next set)))
  285. (and first
  286. (not (intset-next set (1+ first)))
  287. first)))
  288. (define (compute-contification conts)
  289. (let*-values
  290. (;; label -> (var ...)
  291. ((candidates) (compute-contification-candidates conts))
  292. ((labels) (intmap->intset candidates))
  293. ;; var -> label
  294. ((vars) (intmap-fold (lambda (label vars out)
  295. (intset-fold (lambda (var out)
  296. (intmap-add out var label))
  297. vars out))
  298. candidates
  299. empty-intmap))
  300. ;; caller-label -> callee-label..., callee-label -> return-label...
  301. ((calls returns) (compute-call-graph conts labels vars))
  302. ;; callee-label -> tail-label
  303. ((tails) (intset-fold
  304. (lambda (label tails)
  305. (intmap-add tails label (tail-label conts label)))
  306. labels
  307. empty-intmap))
  308. ;; Strongly connected components, allowing us to contify mutually
  309. ;; tail-recursive functions. Since `compute-call-graph' added on
  310. ;; a synthetic 0->LABEL... entry for contifiable functions called
  311. ;; by non-contifiable functions, we need to remove that entry
  312. ;; from the partition. It will be in its own component, as it
  313. ;; has no predecessors.
  314. ;;
  315. ;; id -> label...
  316. ((groups) (intmap-remove
  317. (compute-strongly-connected-components calls 0)
  318. 0)))
  319. ;; todo: thread groups through contification
  320. (define (attempt-contification labels contified return-substs)
  321. (let ((returns (compute-return-labels labels tails returns
  322. return-substs)))
  323. (cond
  324. ((trivial-set returns)
  325. => (lambda (k)
  326. ;; Success!
  327. (values (intset-union contified labels)
  328. (intset-fold (lambda (label return-substs)
  329. (let ((tail (intmap-ref tails label)))
  330. (intmap-add return-substs tail k)))
  331. labels return-substs))))
  332. ((trivial-set labels)
  333. ;; Single-label SCC failed to contify.
  334. (values contified return-substs))
  335. (else
  336. ;; Multi-label SCC failed to contify. Try instead to contify
  337. ;; each one.
  338. (intset-fold
  339. (lambda (label contified return-substs)
  340. (let ((labels (intset-add empty-intset label)))
  341. (attempt-contification labels contified return-substs)))
  342. labels contified return-substs)))))
  343. (call-with-values
  344. (lambda ()
  345. (fixpoint
  346. (lambda (contified return-substs)
  347. (intmap-fold
  348. (lambda (id group contified return-substs)
  349. (attempt-contification group contified return-substs))
  350. (filter-contifiable contified groups)
  351. contified
  352. return-substs))
  353. empty-intset
  354. empty-intmap))
  355. (lambda (contified return-substs)
  356. (values (intset-fold (lambda (label call-substs)
  357. (intset-fold
  358. (lambda (var call-substs)
  359. (intmap-add call-substs var label))
  360. (intmap-ref candidates label)
  361. call-substs))
  362. contified
  363. empty-intmap)
  364. return-substs)))))
  365. (define (apply-contification conts call-substs return-substs)
  366. (define (call-subst proc)
  367. (intmap-ref call-substs proc (lambda (_) #f)))
  368. (define (return-subst k)
  369. (intmap-ref return-substs k (lambda (_) #f)))
  370. (define (find-body kfun nargs)
  371. (match (intmap-ref conts kfun)
  372. (($ $kfun src meta self tail clause)
  373. (let lp ((clause clause))
  374. (match (intmap-ref conts clause)
  375. (($ $kclause arity body alt)
  376. (if (arity-matches? arity nargs)
  377. body
  378. (lp alt))))))))
  379. (define (continue k src exp)
  380. (define (lookup-return-cont k)
  381. (match (return-subst k)
  382. (#f k)
  383. (k (lookup-return-cont k))))
  384. (let ((k* (lookup-return-cont k)))
  385. (if (eq? k k*)
  386. (build-term ($continue k src ,exp))
  387. ;; We are contifying this return. It must be a call, a
  388. ;; $values expression, or a return primcall. k* will be
  389. ;; either a $ktail or a $kreceive continuation. CPS has this
  390. ;; thing though where $kreceive can't be the target of a
  391. ;; $values expression, and "return" can only continue to a
  392. ;; tail continuation, so we might have to rewrite to a
  393. ;; "values" primcall.
  394. (build-term
  395. ($continue k* src
  396. ,(match (intmap-ref conts k*)
  397. (($ $kreceive)
  398. (match exp
  399. (($ $call) exp)
  400. ;; A primcall that can continue to $ktail can also
  401. ;; continue to $kreceive.
  402. (($ $primcall) exp)
  403. (($ $values vals)
  404. (build-exp ($primcall 'values vals)))))
  405. (($ $ktail) exp)))))))
  406. (define (visit-exp k src exp)
  407. (match exp
  408. (($ $call proc args)
  409. ;; If proc is contifiable, replace call with jump.
  410. (match (call-subst proc)
  411. (#f (continue k src exp))
  412. (kfun
  413. (let ((body (find-body kfun (length args))))
  414. (build-term ($continue body src ($values args)))))))
  415. (($ $fun kfun)
  416. ;; If the function's tail continuation has been
  417. ;; substituted, that means it has been contified.
  418. (if (return-subst (tail-label conts kfun))
  419. (continue k src (build-exp ($values ())))
  420. (continue k src exp)))
  421. (($ $rec names vars funs)
  422. (match (filter (match-lambda ((n v f) (not (call-subst v))))
  423. (map list names vars funs))
  424. (() (continue k src (build-exp ($values ()))))
  425. (((names vars funs) ...)
  426. (continue k src (build-exp ($rec names vars funs))))))
  427. (_ (continue k src exp))))
  428. ;; Renumbering is not strictly necessary but some passes may not be
  429. ;; equipped to deal with stale $kfun nodes whose bodies have been
  430. ;; wired into other functions.
  431. (renumber
  432. (intmap-map
  433. (lambda (label cont)
  434. (match cont
  435. (($ $kargs names vars ($ $continue k src exp))
  436. ;; Remove bindings for functions that have been contified.
  437. (match (filter (match-lambda ((name var) (not (call-subst var))))
  438. (map list names vars))
  439. (((names vars) ...)
  440. (build-cont
  441. ($kargs names vars ,(visit-exp k src exp))))))
  442. (_ cont)))
  443. conts)))
  444. (define (contify conts)
  445. ;; FIXME: Renumbering isn't really needed but dead continuations may
  446. ;; cause compute-singly-referenced-labels to spuriously mark some
  447. ;; conts as irreducible. For now we punt and renumber so that there
  448. ;; are only live conts.
  449. (let ((conts (renumber conts)))
  450. (let-values (((call-substs return-substs) (compute-contification conts)))
  451. (apply-contification conts call-substs return-substs))))