tailify.scm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2021, 2023 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. ;;; Tailification converts a program so that all calls are tail calls.
  19. ;;; It is a minimal form of global CPS conversion that stack-allocates
  20. ;;; "return continuations" -- minimal in the sense that the only
  21. ;;; additionally residualized continuations are the ones necessary to
  22. ;;; preserve the all-tail-calls property. Notably, loops, conditionals,
  23. ;;; and similar features in the source program are left as is unless
  24. ;;; it's necessary to split them.
  25. ;;;
  26. ;;; The first step of tailification computes the set of "tails" in a
  27. ;;; function. The function entry starts a tail, as does each return
  28. ;;; point from non-tail calls. Join points between different tails
  29. ;;; also start tails.
  30. ;;;
  31. ;;; In the residual program, there are four ways that a continuation
  32. ;;; exits:
  33. ;;;
  34. ;;; - Tail calls in the source program are tail calls in the residual
  35. ;;; program; no change.
  36. ;;;
  37. ;;; - For non-tail calls in the source program, the caller saves the
  38. ;;; state of the continuation (the live variables flowing into the
  39. ;;; continuation) on an explicit stack, and saves the label of the
  40. ;;; continuation. The return continuation will be converted into a
  41. ;;; arity-checking function entry, to handle multi-value returns;
  42. ;;; when it is invoked, it will pop its incoming live variables from
  43. ;;; the continuation stack.
  44. ;;;
  45. ;;; - Terms that continue to a join continuation are converted to
  46. ;;; label calls in tail position, passing the state of the
  47. ;;; continuation as arguments.
  48. ;;;
  49. ;;; - Returning values from a continuation pops the return label from
  50. ;;; the stack and does an indirect tail label call on that label,
  51. ;;; with the given return values.
  52. ;;;
  53. ;;; Additionally, the abort-to-prompt run-time routine may unwind the
  54. ;;; explicit stack and tail-call a handler continuation. If the
  55. ;;; continuation is not escape-only, then the slice of the continuation
  56. ;;; that would be popped off is captured before unwinding. Resuming a
  57. ;;; continuation splats the saved continuation back on the stack and
  58. ;;; returns to the top continuation, just as in the tail return case
  59. ;;; above.
  60. ;;;
  61. ;;; We expect that a tailified program will probably be slower than a
  62. ;;; non-tailified program. However a tailified program has a few
  63. ;;; interesting properties: the stack is packed and only contains live
  64. ;;; data; the stack can be traversed in a portable way, allowing for
  65. ;;; implementation of prompts on systems that don't support them
  66. ;;; natively; and as all calls are tail calls, the whole system can be
  67. ;;; implemented naturally with a driver trampoline on targets that don't
  68. ;;; support tail calls (e.g. JavaScript and WebAssembly).
  69. ;;;
  70. ;;; Code:
  71. (define-module (language cps hoot tailify)
  72. #:use-module (srfi srfi-1)
  73. #:use-module (srfi srfi-11)
  74. #:use-module (ice-9 match)
  75. #:use-module (language cps)
  76. #:use-module (language cps intmap)
  77. #:use-module (language cps intset)
  78. #:use-module (language cps graphs)
  79. #:use-module (language cps utils)
  80. #:use-module (language cps renumber)
  81. #:use-module (language cps with-cps)
  82. #:export (tailify))
  83. (define (trivial-intmap x)
  84. (let ((next (intmap-next x)))
  85. (and (eqv? next (intmap-prev x))
  86. next)))
  87. (define (live-constants live-in constants head)
  88. (intmap-select constants
  89. (intset-intersect (intmap-ref live-in head)
  90. (intmap-keys constants))))
  91. (define (live-vars live-in constants head)
  92. (intset-subtract (intmap-ref live-in head)
  93. (intmap-keys constants)))
  94. (define (rename-var* fresh-names var)
  95. (intmap-ref fresh-names var (lambda (var) var)))
  96. (define (rename-vars* fresh-names vars)
  97. (match vars
  98. (() '())
  99. ((var . vars)
  100. (cons (rename-var* fresh-names var)
  101. (rename-vars* fresh-names vars)))))
  102. (define (compute-saved-vars* fresh-names live-in constants reprs k)
  103. (intset-fold-right
  104. (lambda (var reprs* vars)
  105. (values (cons (intmap-ref reprs var) reprs*)
  106. (cons (rename-var* fresh-names var) vars)))
  107. (live-vars live-in constants k) '() '()))
  108. (define (tailify-tail cps head body fresh-names winds live-in constants
  109. reprs entries original-ktail)
  110. "Rewrite the conts with labels in the intset BODY, forming the body of
  111. the tail which begins at HEAD in the source program. The entry to the
  112. tail was already rewritten, with ENTRIES containing an intmap of tail
  113. heads to $kfun labels. WINDS associates 'unwind primcalls with the
  114. corresponding conts that pushes on the dynamic stack. LIVE-IN indicates
  115. the variables that are live at tail heads, and CONSTANTS is an intmap
  116. associating vars known to be constant with their values. REPRS holds
  117. the representation of each var. ORIGINAL-KTAIL is the tail cont of the
  118. source function; terms in the tail that continue to ORIGINAL-KTAIL will
  119. be rewritten to continue to the tail's ktail."
  120. ;; HEAD will have been given a corresponding entry $kfun by
  121. ;; tailify-tails. Here we find the tail-label for the current tail.
  122. (define local-ktail
  123. (match (intmap-ref cps head)
  124. (($ $kfun src meta self ktail kentry)
  125. ktail)))
  126. ;; (pk 'tailify-tail head body fresh-names original-ktail local-ktail)
  127. (define (rename-var var) (rename-var* fresh-names var))
  128. (define (rename-vars vars) (rename-vars* fresh-names vars))
  129. (define (rename-exp exp)
  130. (rewrite-exp exp
  131. ((or ($ $const) ($ $prim) ($ $const-fun) ($ $code)) ,exp)
  132. (($ $call proc args)
  133. ($call (rename-var proc) ,(rename-vars args)))
  134. (($ $callk k proc args)
  135. ($callk k (and proc (rename-var proc)) ,(rename-vars args)))
  136. (($ $primcall name param args)
  137. ($primcall name param ,(rename-vars args)))
  138. (($ $values args)
  139. ($values ,(rename-vars args)))))
  140. (define (compute-saved-vars fresh-names k)
  141. (compute-saved-vars* fresh-names live-in constants reprs k))
  142. ;; Return a $callk to the join tail with head K. We first pass the
  143. ;; arguments for the K in the source program, and then we pass any
  144. ;; live-in variables at the head, which are renamed according to
  145. ;; FRESH-NAMES.
  146. (define (compute-join-call join-vars k)
  147. (call-with-values (lambda () (compute-saved-vars fresh-names k))
  148. (lambda (reprs vars)
  149. (build-exp
  150. ($callk (intmap-ref entries k) #f ,(append join-vars vars))))))
  151. ;; A branch target can either be in the current tail, or it starts a
  152. ;; join continuation. It can't be $ktail, it can't be $kreceive, and
  153. ;; it takes no values, hence we pass () to compute-join-call.
  154. (define (rewrite-branch-target cps src k)
  155. (cond
  156. ((intset-ref body k)
  157. (with-cps cps k))
  158. (else
  159. (when (eqv? k original-ktail) (error "what!!"))
  160. (with-cps cps
  161. (letk kcall
  162. ($kargs () ()
  163. ($continue local-ktail src ,(compute-join-call '() k))))
  164. kcall))))
  165. (define (rewrite-branch-targets cps src k*)
  166. (match k*
  167. (()
  168. (with-cps cps '()))
  169. ((k . k*)
  170. (with-cps cps
  171. (let$ k* (rewrite-branch-targets src k*))
  172. (let$ k (rewrite-branch-target src k))
  173. (cons k k*)))))
  174. ;; Rewrite TERM. Generally speaking we just rename variable uses.
  175. ;; However if TERM continues to another tail, we have to generate the
  176. ;; appropriate call for the continuation tail kind.
  177. (define (rewrite-term cps term)
  178. (match term
  179. (($ $continue k src exp)
  180. (let ((exp (rename-exp exp)))
  181. (cond
  182. ((eqv? k original-ktail)
  183. ;; (pk 'original-tail-call k exp)
  184. (match exp
  185. (($ $values args)
  186. ;; The original term is a $values in tail position.
  187. ;; Transform to pop the continuation stack and tail call
  188. ;; it.
  189. (with-cps cps
  190. (letv ret)
  191. (letk kcall ($kargs ('ret) (ret)
  192. ($continue local-ktail src
  193. ($calli args ret))))
  194. (build-term ($continue kcall src
  195. ($primcall 'restore '(code) ())))))
  196. ((or ($ $call) ($ $callk) ($ $calli))
  197. ;; Otherwise the original term was a tail call.
  198. (with-cps cps
  199. (build-term ($continue local-ktail src ,exp))))))
  200. ((intset-ref body k)
  201. ;; Continuation within current tail.
  202. (with-cps cps
  203. (build-term ($continue k src ,exp))))
  204. (else
  205. (match exp
  206. ((or ($ $call) ($ $callk) ($ $calli))
  207. ;; A non-tail-call: push the pending continuation and tail
  208. ;; call instead.
  209. ;; (pk 'non-tail-call head k exp)
  210. (call-with-values (lambda ()
  211. (compute-saved-vars fresh-names k))
  212. (lambda (reprs vars)
  213. ;; (pk 'saved-vars reprs vars)
  214. (with-cps cps
  215. (letk kexp ($kargs () ()
  216. ($continue local-ktail src ,exp)))
  217. (letv cont)
  218. (letk kcont ($kargs ('cont) (cont)
  219. ($continue kexp src
  220. ($primcall 'save
  221. (append reprs (list 'code))
  222. ,(append vars (list cont))))))
  223. (build-term ($continue kcont src
  224. ($code (intmap-ref entries k))))))))
  225. (_
  226. ;; Calling a join continuation. This is one of those
  227. ;; cases where it might be nice in CPS to have names for
  228. ;; phi predecessor values. Ah well.
  229. (match (intmap-ref cps k)
  230. (($ $kargs names vars)
  231. (let ((vars' (map (lambda (_) (fresh-var)) vars)))
  232. (with-cps cps
  233. (letk kvals
  234. ($kargs names vars'
  235. ($continue local-ktail src
  236. ,(compute-join-call vars' k))))
  237. (build-term
  238. ($continue kvals src ,exp))))))))))))
  239. (($ $branch kf kt src op param args)
  240. (with-cps cps
  241. (let$ kf (rewrite-branch-target src kf))
  242. (let$ kt (rewrite-branch-target src kt))
  243. (build-term
  244. ($branch kf kt src op param ,(rename-vars args)))))
  245. (($ $switch kf kt* src arg)
  246. (with-cps cps
  247. (let$ kf (rewrite-branch-target src kf))
  248. (let$ kt* (rewrite-branch-targets src kt*))
  249. (build-term ($switch kf kt* src (rename-var arg)))))
  250. (($ $prompt k kh src escape? tag)
  251. (call-with-values (lambda () (compute-saved-vars fresh-names kh))
  252. (lambda (reprs vars)
  253. (with-cps cps
  254. (letv handler)
  255. (let$ k (rewrite-branch-target src k))
  256. (letk kpush ($kargs ('handler) (handler)
  257. ($continue k src
  258. ($primcall 'push-prompt escape?
  259. ((rename-var tag) handler)))))
  260. (letk kcode ($kargs () ()
  261. ($continue kpush src ($code (intmap-ref entries kh)))))
  262. (build-term ($continue kcode src
  263. ($primcall 'save reprs vars)))))))
  264. (($ $throw src op param args)
  265. (with-cps cps
  266. (build-term ($throw src op param ,(rename-vars args)))))))
  267. ;; A prompt body begins with a $prompt, may contain nested prompt
  268. ;; bodies, and continues until a corresponding 'unwind primcall.
  269. ;; Leaving a prompt body may or may not correspond to leaving the
  270. ;; current tail. Leaving the prompt body must remove the handler from
  271. ;; the stack. Removing the handler must happen before leaving the
  272. ;; tail, and notably must happen before pushing saved state for a
  273. ;; non-tail-call continuation.
  274. (define (maybe-unwind-prompt cps label term)
  275. (define (not-a-prompt-unwind) (with-cps cps term))
  276. (define (pop-prompt kh)
  277. (call-with-values (lambda () (compute-saved-vars fresh-names kh))
  278. (lambda (reprs vars)
  279. (with-cps cps
  280. (letk kterm ($kargs () () ,term))
  281. (build-term ($continue kterm #f
  282. ($primcall 'drop reprs ())))))))
  283. (cond
  284. ((intmap-ref winds label (lambda (_) #f))
  285. => (lambda (wind)
  286. (match (intmap-ref cps wind)
  287. (($ $prompt k kh) (pop-prompt kh))
  288. (_ (not-a-prompt-unwind)))))
  289. (else (not-a-prompt-unwind))))
  290. ;; The entry for the current tail has already been rewritten, so here
  291. ;; we just rewrite all the body conts.
  292. (intset-fold
  293. (lambda (label cps)
  294. (match (intmap-ref cps label)
  295. ((or ($ $kfun) ($ $kclause) ($ $ktail)) cps) ;; Unchanged.
  296. (($ $kargs names vals term)
  297. ;; (pk 'tailify-tail1 head label names vals term)
  298. (with-cps cps
  299. (let$ term (rewrite-term term))
  300. (let$ term (maybe-unwind-prompt label term))
  301. (setk label ($kargs names vals ,term))))))
  302. body cps))
  303. (define (tailify-tails cps winds live-in constants reprs tails joins)
  304. "Given that the conts in a function were partitioned into tails in the
  305. intmap TAILS, mapping tail entries to tail bodies, of which the intset
  306. JOINS indicates join continuations, return a new CPS program in which
  307. the tails have been split to separate functions in which all calls are
  308. tail calls.
  309. WINDS associates 'unwind primcalls with the corresponding conts that
  310. pushes on the dynamic stack.
  311. LIVE-IN indicates the variables that are live at tail heads.
  312. CONSTANTS is an intmap associating vars known to be constant with their
  313. values.
  314. REPRS holds the representation of each var."
  315. (define (cont-source label)
  316. (match (intmap-ref cps label)
  317. (($ $kargs _ _ term)
  318. (match term
  319. (($ $continue k src) src)
  320. (($ $branch k kt src) src)
  321. (($ $switch k kt* src) src)
  322. (($ $prompt k kh src) src)
  323. (($ $throw src) src)))))
  324. ;; Compute the set of vars that we need to save for each head, which
  325. ;; excludes the vars bound by the head cont itself.
  326. (define heads-live-in
  327. (intmap-map
  328. (lambda (head body)
  329. (let ((live (intmap-ref live-in head)))
  330. (match (intmap-ref cps head)
  331. (($ $kargs names vars)
  332. (fold1 (lambda (var live) (intset-remove live var))
  333. vars live))
  334. (_ live))))
  335. tails))
  336. ;; For live values that flow into a tail, each tail will need to give
  337. ;; them unique names.
  338. (define fresh-names-per-tail
  339. (intmap-map (lambda (head body)
  340. (intset-fold (lambda (var fresh)
  341. (intmap-add fresh var (fresh-var)))
  342. (intmap-ref heads-live-in head)
  343. empty-intmap))
  344. tails))
  345. (define (compute-saved-vars head)
  346. (compute-saved-vars* (intmap-ref fresh-names-per-tail head)
  347. heads-live-in constants reprs head))
  348. ;; For a tail whose head in the source program is HEAD, rewrite to be
  349. ;; a $kfun. For the "main" tail, no change needed. For join tails,
  350. ;; we make an unchecked $kfun-to-$kargs function to which live
  351. ;; variables are received directly as arguments. For return tails,
  352. ;; the live vars are restored from the stack. In all cases, adjoin a
  353. ;; HEAD->ENTRY mapping to ENTRIES, where ENTRY is the $kfun label for
  354. ;; the tail.
  355. (define (add-entry head body cps entries tails)
  356. (define fresh-names (intmap-ref fresh-names-per-tail head))
  357. ;; Constants don't need to be passed from tail to tail; rather they
  358. ;; are rebound locally.
  359. (define (restore-constants cps body term)
  360. (intmap-fold (lambda (var exp cps body term)
  361. (define var' (intmap-ref fresh-names var))
  362. (with-cps cps
  363. (letk k ($kargs ('const) (var') ,term))
  364. ($ (values (intset-add body k)
  365. (build-term ($continue k #f ,exp))))))
  366. (live-constants heads-live-in constants head)
  367. cps body term))
  368. (define (restore-saved cps body term)
  369. (call-with-values (lambda () (compute-saved-vars head))
  370. (lambda (reprs vars)
  371. ;; (pk 'restoring head reprs vars)
  372. (define names (map (lambda (_) 'restored) vars))
  373. (if (null? names)
  374. (with-cps cps ($ (values body term)))
  375. (with-cps cps
  376. (letk krestore ($kargs names vars ,term))
  377. ($ (values (intset-add body krestore)
  378. (build-term ($continue krestore #f
  379. ($primcall 'restore reprs ()))))))))))
  380. (cond
  381. ((intset-ref joins head)
  382. ;; A join point.
  383. (match (intmap-ref cps head)
  384. (($ $kargs names vars term)
  385. (call-with-values (lambda () (compute-saved-vars head))
  386. (lambda (reprs' vars')
  387. ;; Join calling convention: first the original args, then
  388. ;; the saved vars.
  389. (define join-names
  390. (append names (map (lambda (_) #f) vars')))
  391. (define join-vars
  392. (append vars vars'))
  393. (define join-reprs
  394. (append (map (lambda (var) (intmap-ref reprs var)) vars)
  395. reprs'))
  396. (define meta `((arg-representations . ,join-reprs)))
  397. (let*-values (((cps body term)
  398. (restore-constants cps body term)))
  399. (with-cps cps
  400. (letk ktail ($ktail))
  401. (letk kargs ($kargs join-names join-vars ,term))
  402. (letk kfun ($kfun (cont-source head) meta #f ktail kargs))
  403. ($ (values
  404. (intmap-add entries head kfun)
  405. (let ((added (intset kfun kargs ktail))
  406. (removed (intset head)))
  407. (intmap-add (intmap-remove tails head)
  408. kfun
  409. (intset-subtract (intset-union body added)
  410. removed))))))))))))
  411. (else
  412. (match (intmap-ref cps head)
  413. (($ $kfun)
  414. ;; The main entry.
  415. (values cps (intmap-add entries head head) tails))
  416. (($ $kreceive ($ $arity req () rest () #f) kargs)
  417. ;; The continuation of a non-tail call, or a prompt handler.
  418. ;; In either case we don't know the return arity of the caller
  419. ;; so we have to parse the return values count.
  420. (match (intmap-ref cps kargs)
  421. (($ $kargs names vars)
  422. (let ((vars' (map (lambda (_) (fresh-var)) vars))
  423. (src (cont-source kargs)))
  424. (let*-values (((cps body term)
  425. (restore-constants
  426. cps
  427. body
  428. (build-term
  429. ($continue kargs src ($values vars')))))
  430. ((cps body term) (restore-saved cps body term)))
  431. (with-cps cps
  432. (letk ktail ($ktail))
  433. (letk krestore ($kargs names vars' ,term))
  434. (letk kclause ($kclause (req '() rest '() #f) krestore #f))
  435. (letk kfun ($kfun src '() #f ktail kclause))
  436. ($ (values
  437. (intmap-add entries head kfun)
  438. (let ((added (intset kfun kclause krestore ktail))
  439. (removed (intset head)))
  440. (intmap-add (intmap-remove tails head)
  441. kfun
  442. (intset-subtract (intset-union body added)
  443. removed)))))))))))
  444. (($ $kargs names vars term)
  445. ;; The continuation of a known-return-arity call, from the
  446. ;; return-types optimization.
  447. (let ((vars' (map (lambda (_) (fresh-var)) vars))
  448. (src (cont-source head)))
  449. (let*-values (((cps body restore-term)
  450. (restore-constants
  451. cps
  452. body
  453. (build-term
  454. ($continue head src ($values vars')))))
  455. ((cps body restore-term)
  456. (restore-saved cps body restore-term)))
  457. (with-cps cps
  458. (letk ktail ($ktail))
  459. (letk kentry ($kargs names vars' ,restore-term))
  460. (letk kfun ($kfun src '() #f ktail kentry))
  461. ($ (values
  462. (intmap-add entries head kfun)
  463. (let ((added (intset kfun kentry ktail)))
  464. (intmap-add (intmap-remove tails head)
  465. kfun
  466. (intset-union body added)))))))))))))
  467. (define original-ktail
  468. (match (intmap-ref cps (intmap-next tails))
  469. (($ $kfun src meta self ktail kentry)
  470. ktail)))
  471. (call-with-values (lambda ()
  472. (intmap-fold (lambda (head body cps entries tails)
  473. (add-entry head body cps entries tails))
  474. tails cps empty-intmap tails))
  475. (lambda (cps entries tails)
  476. (intmap-fold
  477. (lambda (old-head head cps)
  478. (define fresh-names (intmap-ref fresh-names-per-tail old-head))
  479. (define body (intmap-ref tails head))
  480. (tailify-tail cps head body fresh-names winds heads-live-in constants
  481. reprs entries original-ktail))
  482. entries cps))))
  483. (define (compute-tails kfun body preds cps)
  484. "Compute the set of tails in the function with entry KFUN and body
  485. BODY. Return as an intset mapping the head label for each tail to its
  486. body, as an intset."
  487. ;; Initially, we start with the requirement that kfun and
  488. ;; continuations of non-tail calls are split heads.
  489. (define (initial-split label splits)
  490. (match (intmap-ref cps label)
  491. (($ $kfun)
  492. (intmap-add splits label label))
  493. (($ $kargs names vars
  494. ($ $continue k src (or ($ $call) ($ $callk) ($ $calli))))
  495. (match (intmap-ref cps k)
  496. (($ $ktail) splits)
  497. ((or ($ $kargs) ($ $kreceive)) (intmap-add splits k k))))
  498. (($ $kargs names vars ($ $prompt k kh src escape? tag))
  499. (intmap-add splits kh kh))
  500. (_
  501. splits)))
  502. ;; Then we build tails by propagating splits forward in the CFG,
  503. ;; possibly creating new split heads at the dominance frontier.
  504. (define (compute-split label splits)
  505. (define (split-head? label)
  506. (eqv? label (intmap-ref splits label (lambda (_) #f))))
  507. (define (ktail? label)
  508. (match (intmap-ref cps label)
  509. (($ $ktail) #t)
  510. (_ #f)))
  511. (cond
  512. ((split-head? label)
  513. ;; Once a label is a split head, it stays a split head.
  514. splits)
  515. ((ktail? label)
  516. ;; ktail always part of root tail.
  517. (intmap-add splits label kfun))
  518. (else
  519. (match (intset-fold
  520. (lambda (pred pred-splits)
  521. (define split
  522. (intmap-ref splits pred (lambda (_) #f)))
  523. (if (and split (not (memv split pred-splits)))
  524. (cons split pred-splits)
  525. pred-splits))
  526. (intmap-ref preds label) '())
  527. ((split)
  528. ;; If all predecessors in same split, label is too.
  529. (intmap-add splits label split (lambda (old new) new)))
  530. ((_ _ . _)
  531. ;; Otherwise this is a new split.
  532. ;; (pk 'join-split label)
  533. (intmap-add splits label label (lambda (old new) new)))))))
  534. ;; label -> split head
  535. (define initial-splits
  536. (intset-fold initial-split body empty-intmap))
  537. ;; (pk initial-splits)
  538. (cond
  539. ((trivial-intmap initial-splits)
  540. ;; There's only one split head, so only one tail, and no joins.
  541. (values (intmap-add empty-intmap kfun body)
  542. empty-intset))
  543. (else
  544. ;; Otherwise, assign each label to a tail, identified by the split
  545. ;; head, then collect the tails by split head.
  546. (let ((splits (fixpoint
  547. (lambda (splits)
  548. ;; (pk 'fixpoint splits)
  549. (intset-fold compute-split body splits))
  550. initial-splits)))
  551. (values
  552. (intmap-fold
  553. (lambda (label head split-bodies)
  554. (intmap-add split-bodies head (intset label) intset-union))
  555. splits
  556. empty-intmap)
  557. (intset-subtract (intmap-fold (lambda (label head heads)
  558. (intset-add heads head))
  559. splits empty-intset)
  560. (intmap-keys initial-splits)))))))
  561. (define (intset-pop set)
  562. "Return two values: all values in intset SET except the first one, and
  563. first value in SET, or #f if SET was empty."
  564. (match (intset-next set)
  565. (#f (values set #f))
  566. (i (values (intset-remove set i) i))))
  567. (define (identify-winds cps kfun body succs)
  568. "For each unwind primcall in BODY, adjoin an entry mapping it to the
  569. corresponding wind expression."
  570. (define (visit-label label exits bodies)
  571. (define wind (intmap-ref bodies label))
  572. (match (intmap-ref cps label)
  573. (($ $kargs _ _ ($ $prompt k kh))
  574. (let* ((bodies (intmap-add bodies k label))
  575. (bodies (intmap-add bodies kh wind)))
  576. (values exits bodies)))
  577. (($ $kargs _ _ ($ $continue k _ ($ $primcall 'wind)))
  578. (let ((bodies (intmap-add bodies k label)))
  579. (values exits bodies)))
  580. (($ $kargs _ _ ($ $continue k _ ($ $primcall 'unwind)))
  581. (let* ((exits (intmap-add exits label wind))
  582. (bodies (intmap-add bodies k (intmap-ref bodies wind))))
  583. (values exits bodies)))
  584. (else
  585. (let ((bodies (intset-fold (lambda (succ bodies)
  586. (intmap-add bodies succ wind))
  587. (intmap-ref succs label)
  588. bodies)))
  589. (values exits bodies)))))
  590. (values
  591. (worklist-fold
  592. (lambda (to-visit exits bodies)
  593. (call-with-values (lambda () (intset-pop to-visit))
  594. (lambda (to-visit label)
  595. (call-with-values (lambda () (visit-label label exits bodies))
  596. (lambda (exits* bodies*)
  597. (if (and (eq? exits exits*) (eq? bodies bodies*))
  598. (values to-visit exits bodies)
  599. (values (intset-union to-visit (intmap-ref succs label))
  600. exits* bodies*)))))))
  601. (intset kfun)
  602. empty-intmap
  603. (intmap-add empty-intmap kfun #f))))
  604. (define (compute-live-in cps body preds)
  605. "Return an intmap associating each label in BODY with an intset of
  606. live variables flowing into the label."
  607. (let ((function (intmap-select cps body)))
  608. (call-with-values
  609. (lambda ()
  610. (call-with-values (lambda () (compute-defs-and-uses function))
  611. (lambda (defs uses)
  612. ;; Unlike the use of compute-live-variables in
  613. ;; slot-allocation.scm, we don't need to add prompt
  614. ;; control-flow edges, as the prompt handler is in its own
  615. ;; tail and therefore $prompt will push the handler
  616. ;; continuation (including its needed live vars) before
  617. ;; entering the prompt body.
  618. (compute-live-variables preds defs uses))))
  619. (lambda (live-in live-out)
  620. live-in))))
  621. (define (compute-constants cps preds)
  622. "Return an intmap associating each variables BODY to their defining
  623. expression, for all variables binding constant expressions."
  624. (define (constant? exp)
  625. (match exp
  626. ((or ($ $const) ($ $prim) ($ $const-fun) ($ $code)) #t)
  627. (_ #f)))
  628. (intmap-fold
  629. (lambda (label preds constants)
  630. (cond
  631. ((trivial-intset preds)
  632. => (lambda (pred)
  633. (match (intmap-ref cps pred)
  634. (($ $continue _ _ (? constant? exp))
  635. (match (intmap-ref cps label)
  636. (($ $kargs (_) (var) _)
  637. (intmap-add constants var exp))))
  638. (_
  639. constants))))
  640. (else constants)))
  641. preds empty-intmap))
  642. (define (tailify-trivial-tail body cps)
  643. "For the function with body BODY and only one tail, rewrite any return
  644. to tail-call the saved continuation."
  645. (define (ktail? k)
  646. (match (intmap-ref cps k)
  647. (($ $ktail) #t)
  648. (_ #f)))
  649. (define (rewrite-return-to-pop-and-calli label cps)
  650. (match (intmap-ref cps label)
  651. (($ $kargs names vars
  652. ($ $continue (? ktail? k) src ($ $values args)))
  653. ;; The original term is a $values in tail position.
  654. ;; Transform to pop the continuation stack and tail
  655. ;; call it.
  656. (with-cps cps
  657. (letv ret)
  658. (letk kcall ($kargs ('ret) (ret)
  659. ($continue k src ($calli args ret))))
  660. (setk label ($kargs names vars
  661. ($continue kcall src
  662. ($primcall 'restore '(code) ()))))))
  663. (_ cps)))
  664. (intset-fold rewrite-return-to-pop-and-calli body cps))
  665. (define (tailify-function kfun body cps primcall-raw-representations)
  666. "Partition the function with entry of KFUN into tails. Rewrite all
  667. tails in such a way that they enter via a $kfun and leave only via tail
  668. calls."
  669. (define succs (compute-successors cps kfun))
  670. (define preds (invert-graph succs))
  671. (define-values (tails joins) (compute-tails kfun body preds cps))
  672. ;; (pk 'tails tails)
  673. (cond
  674. ((trivial-intmap tails)
  675. (tailify-trivial-tail body cps))
  676. (else
  677. ;; Otherwise we apply tailification.
  678. (let ((winds (identify-winds cps kfun body succs))
  679. (live-in (compute-live-in cps body preds))
  680. (constants (compute-constants cps preds))
  681. (reprs (compute-var-representations (intmap-select cps body)
  682. #:primcall-raw-representations
  683. primcall-raw-representations)))
  684. (tailify-tails cps winds live-in constants reprs tails joins)))))
  685. (define* (tailify cps #:key (primcall-raw-representations
  686. primcall-raw-representations))
  687. ;; Renumber so that label order is topological order.
  688. (let ((cps (renumber cps)))
  689. (with-fresh-name-state cps
  690. (intmap-fold
  691. (lambda (kfun body cps)
  692. (tailify-function kfun body cps primcall-raw-representations))
  693. (compute-reachable-functions cps)
  694. cps))))