contification.scm 20 KB

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