closure-conversion.scm 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  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. ;;; This pass converts a CPS term in such a way that no function has any
  19. ;;; free variables. Instead, closures are built explicitly as heap
  20. ;;; objects, and free variables are referenced through the closure.
  21. ;;;
  22. ;;; Closure conversion also removes any $rec expressions that
  23. ;;; contification did not handle. See (language cps) for a further
  24. ;;; discussion of $rec.
  25. ;;;
  26. ;;; Before closure conversion, function self variables are always bound.
  27. ;;; After closure conversion, well-known functions with no free
  28. ;;; variables may have no self reference.
  29. ;;;
  30. ;;; Code:
  31. (define-module (language cps closure-conversion)
  32. #:use-module (ice-9 match)
  33. #:use-module ((srfi srfi-1) #:select (fold
  34. filter-map
  35. ))
  36. #:use-module (srfi srfi-11)
  37. #:use-module (system base types internal)
  38. #:use-module (language cps)
  39. #:use-module (language cps utils)
  40. #:use-module (language cps with-cps)
  41. #:use-module (language cps intmap)
  42. #:use-module (language cps intset)
  43. #:export (convert-closures))
  44. (define (compute-function-bodies conts kfun)
  45. "Compute a map from FUN-LABEL->BODY-LABEL... for all $fun instances in
  46. conts."
  47. (let visit-fun ((kfun kfun) (out empty-intmap))
  48. (let ((body (compute-function-body conts kfun)))
  49. (intset-fold
  50. (lambda (label out)
  51. (match (intmap-ref conts label)
  52. (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
  53. (visit-fun kfun out))
  54. (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
  55. (fold visit-fun out kfun))
  56. (_ out)))
  57. body
  58. (intmap-add out kfun body)))))
  59. (define (compute-program-body functions)
  60. (intmap-fold (lambda (label body out) (intset-union body out))
  61. functions
  62. empty-intset))
  63. (define (filter-reachable conts functions)
  64. (let ((reachable (compute-program-body functions)))
  65. (intmap-fold
  66. (lambda (label cont out)
  67. (if (intset-ref reachable label)
  68. out
  69. (intmap-remove out label)))
  70. conts conts)))
  71. (define (compute-non-operator-uses conts)
  72. (persistent-intset
  73. (intmap-fold
  74. (lambda (label cont uses)
  75. (define (add-use var uses) (intset-add! uses var))
  76. (define (add-uses vars uses)
  77. (match vars
  78. (() uses)
  79. ((var . vars) (add-uses vars (add-use var uses)))))
  80. (match cont
  81. (($ $kargs _ _ ($ $continue _ _ exp))
  82. (match exp
  83. ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) uses)
  84. (($ $values args)
  85. (add-uses args uses))
  86. (($ $call proc args)
  87. (add-uses args uses))
  88. (($ $primcall name param args)
  89. (add-uses args uses))))
  90. (($ $kargs _ _ ($ $branch kf kt src op param args))
  91. (add-uses args uses))
  92. (($ $kargs _ _ ($ $prompt k kh src escape? tag))
  93. (add-use tag uses))
  94. (($ $kargs _ _ ($ $throw src op param args))
  95. (add-uses args uses))
  96. (_ uses)))
  97. conts
  98. empty-intset)))
  99. (define (compute-singly-referenced-labels conts body)
  100. (define (add-ref label single multiple)
  101. (define (ref k single multiple)
  102. (if (intset-ref single k)
  103. (values single (intset-add! multiple k))
  104. (values (intset-add! single k) multiple)))
  105. (define (ref0) (values single multiple))
  106. (define (ref1 k) (ref k single multiple))
  107. (define (ref2 k k*)
  108. (if k*
  109. (let-values (((single multiple) (ref k single multiple)))
  110. (ref k* single multiple))
  111. (ref1 k)))
  112. (match (intmap-ref conts label)
  113. (($ $kreceive arity k) (ref1 k))
  114. (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
  115. (($ $ktail) (ref0))
  116. (($ $kclause arity kbody kalt) (ref2 kbody kalt))
  117. (($ $kargs _ _ ($ $continue k)) (ref1 k))
  118. (($ $kargs _ _ ($ $branch kf kt)) (ref2 kf kt))
  119. (($ $kargs _ _ ($ $prompt k kh)) (ref2 k kh))
  120. (($ $kargs _ _ ($ $throw)) (ref0))))
  121. (let*-values (((single multiple) (values empty-intset empty-intset))
  122. ((single multiple) (intset-fold add-ref body single multiple)))
  123. (intset-subtract (persistent-intset single)
  124. (persistent-intset multiple))))
  125. (define (compute-function-names conts functions)
  126. "Compute a map of FUN-LABEL->BOUND-VAR... for each labelled function
  127. whose bound vars we know."
  128. (define (add-named-fun var kfun out)
  129. (let ((self (match (intmap-ref conts kfun)
  130. (($ $kfun src meta self) self))))
  131. (intmap-add out kfun (intset var self))))
  132. (intmap-fold
  133. (lambda (label body out)
  134. (let ((single (compute-singly-referenced-labels conts body)))
  135. (intset-fold
  136. (lambda (label out)
  137. (match (intmap-ref conts label)
  138. (($ $kargs _ _ ($ $continue k _ ($ $fun kfun)))
  139. (if (intset-ref single k)
  140. (match (intmap-ref conts k)
  141. (($ $kargs (_) (var)) (add-named-fun var kfun out))
  142. (_ out))
  143. out))
  144. (($ $kargs _ _ ($ $continue k _ ($ $rec _ vars (($ $fun kfun) ...))))
  145. (unless (intset-ref single k)
  146. (error "$rec continuation has multiple predecessors??"))
  147. (fold add-named-fun out vars kfun))
  148. (_ out)))
  149. body
  150. out)))
  151. functions
  152. empty-intmap))
  153. (define (compute-well-known-functions conts bound->label)
  154. "Compute a set of labels indicating the well-known functions in
  155. @var{conts}. A well-known function is a function whose bound names we
  156. know and which is never used in a non-operator position."
  157. (intset-subtract
  158. (persistent-intset
  159. (intmap-fold (lambda (bound label candidates)
  160. (intset-add! candidates label))
  161. bound->label
  162. empty-intset))
  163. (persistent-intset
  164. (intset-fold (lambda (var not-well-known)
  165. (match (intmap-ref bound->label var (lambda (_) #f))
  166. (#f not-well-known)
  167. (label (intset-add! not-well-known label))))
  168. (compute-non-operator-uses conts)
  169. empty-intset))))
  170. (define (intset-cons i set)
  171. (intset-add set i))
  172. (define (compute-shared-closures conts well-known)
  173. "Compute a map LABEL->VAR indicating the sets of functions that will
  174. share a closure. If a functions's label is in the map, it is shared.
  175. The entries indicate the var of the shared closure, which will be one of
  176. the bound vars of the closure."
  177. (intmap-fold
  178. (lambda (label cont out)
  179. (match cont
  180. (($ $kargs _ _
  181. ($ $continue _ _ ($ $rec names vars (($ $fun kfuns) ...))))
  182. ;; The split-rec pass should have ensured that this $rec forms a
  183. ;; strongly-connected component, so the free variables from all of
  184. ;; the functions will be alive as long as one of the closures is
  185. ;; alive. For that reason we can consider storing all free
  186. ;; variables in one closure and sharing it.
  187. (let* ((kfuns-set (fold intset-cons empty-intset kfuns))
  188. (unknown-kfuns (intset-subtract kfuns-set well-known)))
  189. (cond
  190. ((or (eq? empty-intset kfuns-set) (trivial-intset kfuns-set))
  191. ;; There is only zero or one function bound here. Trivially
  192. ;; shared already.
  193. out)
  194. ((eq? empty-intset unknown-kfuns)
  195. ;; All functions are well-known; we can share a closure. Use
  196. ;; the first bound variable.
  197. (let ((closure (car vars)))
  198. (intset-fold (lambda (kfun out)
  199. (intmap-add out kfun closure))
  200. kfuns-set out)))
  201. ((trivial-intset unknown-kfuns)
  202. => (lambda (unknown-kfun)
  203. ;; Only one function is not-well-known. Use that
  204. ;; function's closure as the shared closure.
  205. (let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
  206. (intset-fold (lambda (kfun out)
  207. (intmap-add out kfun closure))
  208. kfuns-set out))))
  209. (else
  210. ;; More than one not-well-known function means we need more
  211. ;; than one proper closure, so we can't share.
  212. out))))
  213. (_ out)))
  214. conts
  215. empty-intmap))
  216. (define* (rewrite-shared-closure-calls cps functions label->bound shared kfun)
  217. "Rewrite CPS such that every call to a function with a shared closure
  218. instead is a $callk to that label, but passing the shared closure as the
  219. proc argument. For recursive calls, use the appropriate 'self'
  220. variable, if possible. Also rewrite uses of the non-well-known but
  221. shared closures to use the appropriate 'self' variable, if possible."
  222. ;; env := var -> (var . label)
  223. (define (visit-fun kfun cps env)
  224. (define (subst var)
  225. (match (intmap-ref env var (lambda (_) #f))
  226. (#f var)
  227. ((var . label) var)))
  228. (define (visit-exp exp)
  229. (rewrite-exp exp
  230. ((or ($ $const) ($ $prim)) ,exp)
  231. (($ $call proc args)
  232. ,(let ((args (map subst args)))
  233. (rewrite-exp (intmap-ref env proc (lambda (_) #f))
  234. (#f ($call proc ,args))
  235. ((closure . label) ($callk label closure ,args)))))
  236. (($ $primcall name param args)
  237. ($primcall name param ,(map subst args)))
  238. (($ $values args)
  239. ($values ,(map subst args)))))
  240. (define (visit-term term)
  241. (rewrite-term term
  242. (($ $continue k src exp)
  243. ($continue k src ,(visit-exp exp)))
  244. (($ $branch kf kt src op param args)
  245. ($branch kf kt src op param ,(map subst args)))
  246. (($ $prompt k kh src escape? tag)
  247. ($prompt k kh src escape? (subst tag)))
  248. (($ $throw src op param args)
  249. ($throw src op param ,(map subst args)))))
  250. (define (visit-rec labels vars cps)
  251. (define (compute-env label bound self rec-bound rec-labels env)
  252. (define (add-bound-var bound label env)
  253. (intmap-add env bound (cons self label) (lambda (old new) new)))
  254. (if (intmap-ref shared label (lambda (_) #f))
  255. ;; Within a function with a shared closure, rewrite
  256. ;; references to bound vars to use the "self" var.
  257. (fold add-bound-var env rec-bound rec-labels)
  258. ;; Otherwise be sure to use "self" references in any
  259. ;; closure.
  260. (add-bound-var bound label env)))
  261. (fold (lambda (label var cps)
  262. (match (intmap-ref cps label)
  263. (($ $kfun src meta self)
  264. (visit-fun label cps
  265. (compute-env label var self vars labels env)))))
  266. cps labels vars))
  267. (define (visit-cont label cps)
  268. (match (intmap-ref cps label)
  269. (($ $kargs names vars
  270. ($ $continue k src ($ $fun label)))
  271. (visit-fun label cps env))
  272. (($ $kargs _ _
  273. ($ $continue k src ($ $rec names vars (($ $fun labels) ...))))
  274. (visit-rec labels vars cps))
  275. (($ $kargs names vars term)
  276. (with-cps cps
  277. (setk label ($kargs names vars ,(visit-term term)))))
  278. (_ cps)))
  279. (intset-fold visit-cont (intmap-ref functions kfun) cps))
  280. ;; Initial environment is bound-var -> (shared-var . label) map for
  281. ;; functions with shared closures.
  282. (let ((env (intmap-fold (lambda (label shared env)
  283. (intset-fold (lambda (bound env)
  284. (intmap-add env bound
  285. (cons shared label)))
  286. (intset-remove
  287. (intmap-ref label->bound label)
  288. (match (intmap-ref cps label)
  289. (($ $kfun src meta self) self)))
  290. env))
  291. shared
  292. empty-intmap)))
  293. (persistent-intmap (visit-fun kfun cps env))))
  294. (define (compute-free-vars conts kfun shared)
  295. "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
  296. references."
  297. (define (add-def var defs) (intset-add! defs var))
  298. (define (add-defs vars defs)
  299. (match vars
  300. (() defs)
  301. ((var . vars) (add-defs vars (add-def var defs)))))
  302. (define (add-use var uses)
  303. (intset-add! uses var))
  304. (define (add-uses vars uses)
  305. (match vars
  306. (() uses)
  307. ((var . vars) (add-uses vars (add-use var uses)))))
  308. (define (visit-nested-funs body)
  309. (intset-fold
  310. (lambda (label out)
  311. (match (intmap-ref conts label)
  312. (($ $kargs _ _ ($ $continue _ _
  313. ($ $fun kfun)))
  314. (intmap-union out (visit-fun kfun)))
  315. (($ $kargs _ _ ($ $continue _ _
  316. ($ $rec _ _ (($ $fun labels) ...))))
  317. (let* ((out (fold (lambda (kfun out)
  318. (intmap-union out (visit-fun kfun)))
  319. out labels))
  320. (free (fold (lambda (kfun free)
  321. (intset-union free (intmap-ref out kfun)))
  322. empty-intset labels)))
  323. (fold (lambda (kfun out)
  324. ;; For functions that share a closure, the free
  325. ;; variables for one will be the union of the free
  326. ;; variables for all.
  327. (if (intmap-ref shared kfun (lambda (_) #f))
  328. (intmap-replace out kfun free)
  329. out))
  330. out
  331. labels)))
  332. (_ out)))
  333. body
  334. empty-intmap))
  335. (define (visit-fun kfun)
  336. (let* ((body (compute-function-body conts kfun))
  337. (free (visit-nested-funs body)))
  338. (call-with-values
  339. (lambda ()
  340. (intset-fold
  341. (lambda (label defs uses)
  342. (match (intmap-ref conts label)
  343. (($ $kargs names vars term)
  344. (values
  345. (add-defs vars defs)
  346. (match term
  347. (($ $continue k src exp)
  348. (match exp
  349. ((or ($ $const) ($ $prim)) uses)
  350. (($ $fun kfun)
  351. (intset-union (persistent-intset uses)
  352. (intmap-ref free kfun)))
  353. (($ $rec names vars (($ $fun kfun) ...))
  354. (fold (lambda (kfun uses)
  355. (intset-union (persistent-intset uses)
  356. (intmap-ref free kfun)))
  357. uses kfun))
  358. (($ $values args)
  359. (add-uses args uses))
  360. (($ $call proc args)
  361. (add-use proc (add-uses args uses)))
  362. (($ $callk label proc args)
  363. (add-use proc (add-uses args uses)))
  364. (($ $primcall name param args)
  365. (add-uses args uses))))
  366. (($ $branch kf kt src op param args)
  367. (add-uses args uses))
  368. (($ $prompt k kh src escape? tag)
  369. (add-use tag uses))
  370. (($ $throw src op param args)
  371. (add-uses args uses)))))
  372. (($ $kfun src meta self)
  373. (values (add-def self defs) uses))
  374. (_ (values defs uses))))
  375. body empty-intset empty-intset))
  376. (lambda (defs uses)
  377. (intmap-add free kfun (intset-subtract
  378. (persistent-intset uses)
  379. (persistent-intset defs)))))))
  380. (visit-fun kfun))
  381. (define (eliminate-closure? label free-vars)
  382. (eq? (intmap-ref free-vars label) empty-intset))
  383. (define (closure-label label shared bound->label)
  384. (cond
  385. ((intmap-ref shared label (lambda (_) #f))
  386. => (lambda (closure)
  387. (intmap-ref bound->label closure)))
  388. (else label)))
  389. (define (closure-alias label well-known free-vars)
  390. (and (intset-ref well-known label)
  391. (trivial-intset (intmap-ref free-vars label))))
  392. (define (prune-free-vars free-vars bound->label well-known shared)
  393. "Given the label->bound-var map @var{free-vars}, remove free variables
  394. that are known functions with zero free variables, and replace
  395. references to well-known functions with one free variable with that free
  396. variable, until we reach a fixed point on the free-vars map."
  397. (define (prune-free in-label free free-vars)
  398. (intset-fold (lambda (var free)
  399. (match (intmap-ref bound->label var (lambda (_) #f))
  400. (#f free)
  401. (label
  402. (cond
  403. ((eliminate-closure? label free-vars)
  404. (intset-remove free var))
  405. ((closure-alias (closure-label label shared bound->label)
  406. well-known free-vars)
  407. => (lambda (alias)
  408. ;; If VAR is free in LABEL, then ALIAS must
  409. ;; also be free because its definition must
  410. ;; precede VAR's definition.
  411. (intset-add (intset-remove free var) alias)))
  412. (else free)))))
  413. free free))
  414. (fixpoint (lambda (free-vars)
  415. (intmap-fold (lambda (label free free-vars)
  416. (intmap-replace free-vars label
  417. (prune-free label free free-vars)))
  418. free-vars
  419. free-vars))
  420. free-vars))
  421. (define (intset-find set i)
  422. (let lp ((idx 0) (start #f))
  423. (let ((start (intset-next set start)))
  424. (cond
  425. ((not start) (error "not found" set i))
  426. ((= start i) idx)
  427. (else (lp (1+ idx) (1+ start)))))))
  428. (define (intset-count set)
  429. (intset-fold (lambda (_ count) (1+ count)) set 0))
  430. (define (compute-elidable-closures cps well-known shared free-vars)
  431. "Compute the set of well-known callees with no free variables. Calls
  432. to these functions can avoid passing a closure parameter. Note however
  433. that we have to exclude well-known callees that are part of a shared
  434. closure that contains any not-well-known member."
  435. (define (intset-map f set)
  436. (persistent-intset
  437. (intset-fold (lambda (i out) (if (f i) (intset-add! out i) out))
  438. set
  439. empty-intset)))
  440. (let ((no-free-vars (persistent-intset
  441. (intmap-fold (lambda (label free out)
  442. (if (eq? empty-intset free)
  443. (intset-add! out label)
  444. out))
  445. free-vars empty-intset)))
  446. (shared
  447. (intmap-fold
  448. (lambda (label cont out)
  449. (match cont
  450. (($ $kargs _ _
  451. ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
  452. ;; Either all of these functions share a closure, in
  453. ;; which all or all except one of them are well-known, or
  454. ;; none of the functions share a closure.
  455. (if (intmap-ref shared (car kfuns) (lambda (_) #f))
  456. (let* ((scc (fold intset-cons empty-intset kfuns)))
  457. (intset-fold (lambda (label out)
  458. (intmap-add out label scc))
  459. scc out))
  460. out))
  461. (_ out)))
  462. cps
  463. empty-intmap)))
  464. (intmap-fold (lambda (label labels elidable)
  465. (if (eq? labels (intset-intersect labels well-known))
  466. elidable
  467. (intset-subtract elidable labels)))
  468. shared
  469. (intset-intersect well-known no-free-vars))))
  470. (define (convert-one cps label body free-vars bound->label well-known shared
  471. elidable)
  472. (define (well-known? label)
  473. (intset-ref well-known label))
  474. (let* ((free (intmap-ref free-vars label))
  475. (nfree (intset-count free))
  476. (self-known? (well-known? (closure-label label shared bound->label)))
  477. (self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
  478. (define (convert-arg cps var k)
  479. "Convert one possibly free variable reference to a bound reference.
  480. If @var{var} is free, it is replaced by a closure reference via a
  481. @code{free-ref} primcall, and @var{k} is called with the new var.
  482. Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
  483. ;; We know that var is not the name of a well-known function.
  484. (cond
  485. ((and=> (intmap-ref bound->label var (lambda (_) #f))
  486. (lambda (kfun)
  487. (and (eq? empty-intset (intmap-ref free-vars kfun))
  488. kfun)))
  489. ;; A not-well-known function with zero free vars. Copy as a
  490. ;; constant, relying on the linker to reify just one copy.
  491. => (lambda (kfun)
  492. (with-cps cps
  493. (letv var*)
  494. (let$ body (k var*))
  495. (letk k* ($kargs (#f) (var*) ,body))
  496. (build-term ($continue k* #f ($const-fun kfun))))))
  497. ((intset-ref free var)
  498. (if (and self-known? (eqv? 1 nfree))
  499. ;; A reference to the one free var of a well-known function.
  500. (with-cps cps
  501. ($ (k self)))
  502. (let* ((idx (intset-find free var))
  503. (param (cond
  504. ((not self-known?) (cons 'closure (+ idx 2)))
  505. ((= nfree 2) (cons 'pair idx))
  506. (else (cons 'vector (+ idx 1))))))
  507. (with-cps cps
  508. (letv var*)
  509. (let$ body (k var*))
  510. (letk k* ($kargs (#f) (var*) ,body))
  511. (build-term
  512. ($continue k* #f
  513. ($primcall 'scm-ref/immediate param (self))))))))
  514. (else
  515. (with-cps cps
  516. ($ (k var))))))
  517. (define (convert-args cps vars k)
  518. "Convert a number of possibly free references to bound references.
  519. @var{k} is called with the bound references, and should return the
  520. term."
  521. (match vars
  522. (()
  523. (with-cps cps
  524. ($ (k '()))))
  525. ((var . vars)
  526. (convert-arg cps var
  527. (lambda (cps var)
  528. (convert-args cps vars
  529. (lambda (cps vars)
  530. (with-cps cps
  531. ($ (k (cons var vars)))))))))))
  532. (define (allocate-closure cps k src label known? nfree)
  533. "Allocate a new closure, and pass it to $var{k}."
  534. (match (vector known? nfree)
  535. (#(#f 0)
  536. ;; The call sites cannot be enumerated, but the closure has no
  537. ;; identity; statically allocate it.
  538. (with-cps cps
  539. (build-term ($continue k src ($const-fun label)))))
  540. (#(#f nfree)
  541. ;; The call sites cannot be enumerated; allocate a closure.
  542. (with-cps cps
  543. (letv closure tag code)
  544. (letk k* ($kargs () ()
  545. ($continue k src ($values (closure)))))
  546. (letk kinit ($kargs ('code) (code)
  547. ($continue k* src
  548. ($primcall 'word-set!/immediate '(closure . 1)
  549. (closure code)))))
  550. (letk kcode ($kargs () ()
  551. ($continue kinit src ($code label))))
  552. (letk ktag1
  553. ($kargs ('tag) (tag)
  554. ($continue kcode src
  555. ($primcall 'word-set!/immediate '(closure . 0)
  556. (closure tag)))))
  557. (letk ktag0
  558. ($kargs ('closure) (closure)
  559. ($continue ktag1 src
  560. ($primcall 'load-u64 (+ %tc7-program (ash nfree 16)) ()))))
  561. (build-term
  562. ($continue ktag0 src
  563. ($primcall 'allocate-words/immediate `(closure . ,(+ nfree 2))
  564. ())))))
  565. (#(#t 2)
  566. ;; Well-known closure with two free variables; the closure is a
  567. ;; pair.
  568. (with-cps cps
  569. (build-term
  570. ($continue k src
  571. ($primcall 'allocate-words/immediate `(pair . 2) ())))))
  572. ;; Well-known callee with more than two free variables; the closure
  573. ;; is a vector.
  574. (#(#t nfree)
  575. (unless (> nfree 2)
  576. (error "unexpected well-known nullary, unary, or binary closure"))
  577. (with-cps cps
  578. (letv v w0)
  579. (letk k* ($kargs () () ($continue k src ($values (v)))))
  580. (letk ktag1
  581. ($kargs ('w0) (w0)
  582. ($continue k* src
  583. ($primcall 'word-set!/immediate '(vector . 0) (v w0)))))
  584. (letk ktag0
  585. ($kargs ('v) (v)
  586. ($continue ktag1 src
  587. ($primcall 'load-u64 (+ %tc7-vector (ash nfree 8)) ()))))
  588. (build-term
  589. ($continue ktag0 src
  590. ($primcall 'allocate-words/immediate `(vector . ,(1+ nfree))
  591. ())))))))
  592. (define (init-closure cps k src var known? free)
  593. "Initialize the free variables @var{closure-free} in a closure
  594. bound to @var{var}, and continue to @var{k}."
  595. (let ((count (intset-count free)))
  596. (cond
  597. ((and known? (<= count 1))
  598. ;; Well-known callee with zero or one free variables; no
  599. ;; initialization necessary.
  600. (with-cps cps
  601. (build-term ($continue k src ($values ())))))
  602. (else
  603. ;; Otherwise residualize a sequence of scm-set!.
  604. (let-values (((kind offset)
  605. ;; What are we initializing? A closure if the
  606. ;; procedure is not well-known; a pair if it has
  607. ;; only 2 free variables; otherwise, a vector.
  608. (cond
  609. ((not known?) (values 'closure 2))
  610. ((= count 2) (values 'pair 0))
  611. (else (values 'vector 1)))))
  612. (let lp ((cps cps) (prev #f) (idx 0))
  613. (match (intset-next free prev)
  614. (#f (with-cps cps
  615. (build-term ($continue k src ($values ())))))
  616. (v (with-cps cps
  617. (let$ body (lp (1+ v) (1+ idx)))
  618. (letk k ($kargs () () ,body))
  619. ($ (convert-arg v
  620. (lambda (cps v)
  621. (with-cps cps
  622. (build-term
  623. ($continue k src
  624. ($primcall 'scm-set!/immediate
  625. (cons kind (+ offset idx))
  626. (var v)))))))))))))))))
  627. (define (make-single-closure cps k src kfun)
  628. (let ((free (intmap-ref free-vars kfun)))
  629. (match (vector (well-known? kfun) (intset-count free))
  630. (#(#f 0)
  631. (with-cps cps
  632. (build-term ($continue k src ($const-fun kfun)))))
  633. (#(#t 0)
  634. (with-cps cps
  635. (build-term ($continue k src ($const #f)))))
  636. (#(#t 1)
  637. ;; A well-known closure of one free variable is replaced
  638. ;; at each use with the free variable itself, so we don't
  639. ;; need a binding at all; and yet, the continuation
  640. ;; expects one value, so give it something. DCE should
  641. ;; clean up later.
  642. (with-cps cps
  643. (build-term ($continue k src ($const #f)))))
  644. (#(well-known? nfree)
  645. ;; A bit of a mess, but beta conversion should remove the
  646. ;; final $values if possible.
  647. (with-cps cps
  648. (letv closure)
  649. (letk k* ($kargs () () ($continue k src ($values (closure)))))
  650. (let$ init (init-closure k* src closure well-known? free))
  651. (letk knew ($kargs (#f) (closure) ,init))
  652. ($ (allocate-closure knew src kfun well-known? nfree)))))))
  653. ;; The callee is known, but not necessarily well-known.
  654. (define (convert-known-proc-call cps k src label closure args)
  655. (define (have-closure cps closure)
  656. (convert-args cps args
  657. (lambda (cps args)
  658. (with-cps cps
  659. (build-term
  660. ($continue k src ($callk label closure args)))))))
  661. (cond
  662. ((eq? (intmap-ref free-vars label) empty-intset)
  663. ;; Known call, no free variables; no closure needed. If the
  664. ;; callee is well-known, elide the closure argument entirely.
  665. ;; Otherwise pass #f.
  666. (if (intset-ref elidable label)
  667. (have-closure cps #f)
  668. (with-cps cps
  669. ($ (with-cps-constants ((false #f))
  670. ($ (have-closure false)))))))
  671. ((and (well-known? (closure-label label shared bound->label))
  672. (trivial-intset (intmap-ref free-vars label)))
  673. ;; Well-known closures with one free variable are
  674. ;; replaced at their use sites by uses of the one free
  675. ;; variable.
  676. => (lambda (var)
  677. (convert-arg cps var have-closure)))
  678. (else
  679. ;; Otherwise just load the proc.
  680. (convert-arg cps closure have-closure))))
  681. (define (visit-term cps term)
  682. (match term
  683. (($ $continue k src (or ($ $const) ($ $prim)))
  684. (with-cps cps
  685. term))
  686. (($ $continue k src ($ $fun kfun))
  687. (with-cps cps
  688. ($ (make-single-closure k src kfun))))
  689. ;; Remove letrec.
  690. (($ $continue k src ($ $rec names vars (($ $fun kfuns) ...)))
  691. (match (vector names vars kfuns)
  692. (#(() () ())
  693. ;; Trivial empty case.
  694. (with-cps cps
  695. (build-term ($continue k src ($values ())))))
  696. (#((name) (var) (kfun))
  697. ;; Trivial single case. We have already proven that K has
  698. ;; only LABEL as its predecessor, so we have been able
  699. ;; already to rewrite free references to the bound name with
  700. ;; the self name.
  701. (with-cps cps
  702. ($ (make-single-closure k src kfun))))
  703. (#(_ _ (kfun0 . _))
  704. ;; A non-trivial strongly-connected component. Does it have
  705. ;; a shared closure?
  706. (match (intmap-ref shared kfun0 (lambda (_) #f))
  707. (#f
  708. ;; Nope. Allocate closures for each function.
  709. (let lp ((cps (match (intmap-ref cps k)
  710. ;; Steal declarations from the continuation.
  711. (($ $kargs names vals body)
  712. (intmap-replace cps k
  713. (build-cont
  714. ($kargs () () ,body))))))
  715. (in (map vector names vars kfuns))
  716. (init (lambda (cps)
  717. (with-cps cps
  718. (build-term
  719. ($continue k src ($values ())))))))
  720. (match in
  721. (() (init cps))
  722. ((#(name var kfun) . in)
  723. (let* ((known? (well-known? kfun))
  724. (free (intmap-ref free-vars kfun))
  725. (nfree (intset-count free)))
  726. (define (next-init cps)
  727. (with-cps cps
  728. (let$ body (init))
  729. (letk k ($kargs () () ,body))
  730. ($ (init-closure k src var known? free))))
  731. (with-cps cps
  732. (let$ body (lp in next-init))
  733. (letk k ($kargs (name) (var) ,body))
  734. ($ (allocate-closure k src kfun known? nfree))))))))
  735. (shared
  736. ;; If shared is in the bound->var map, that means one of
  737. ;; the functions is not well-known. Otherwise use kfun0
  738. ;; as the function label, but just so make-single-closure
  739. ;; can find the free vars, not for embedding in the
  740. ;; closure.
  741. (let* ((kfun (intmap-ref bound->label shared (lambda (_) kfun0)))
  742. (cps (match (intmap-ref cps k)
  743. ;; Make continuation declare only the shared
  744. ;; closure.
  745. (($ $kargs names vals body)
  746. (intmap-replace cps k
  747. (build-cont
  748. ($kargs (#f) (shared) ,body)))))))
  749. (with-cps cps
  750. ($ (make-single-closure k src kfun)))))))))
  751. (($ $continue k src ($ $call proc args))
  752. (match (intmap-ref bound->label proc (lambda (_) #f))
  753. (#f
  754. (convert-arg cps proc
  755. (lambda (cps proc)
  756. (convert-args cps args
  757. (lambda (cps args)
  758. (with-cps cps
  759. (build-term
  760. ($continue k src ($call proc args)))))))))
  761. (label
  762. (convert-known-proc-call cps k src label proc args))))
  763. (($ $continue k src ($ $callk label proc args))
  764. (convert-known-proc-call cps k src label proc args))
  765. (($ $continue k src ($ $primcall name param args))
  766. (convert-args cps args
  767. (lambda (cps args)
  768. (with-cps cps
  769. (build-term
  770. ($continue k src ($primcall name param args)))))))
  771. (($ $continue k src ($ $values args))
  772. (convert-args cps args
  773. (lambda (cps args)
  774. (with-cps cps
  775. (build-term
  776. ($continue k src ($values args)))))))
  777. (($ $branch kf kt src op param args)
  778. (convert-args cps args
  779. (lambda (cps args)
  780. (with-cps cps
  781. (build-term
  782. ($branch kf kt src op param args))))))
  783. (($ $prompt k kh src escape? tag)
  784. (convert-arg cps tag
  785. (lambda (cps tag)
  786. (with-cps cps
  787. (build-term
  788. ($prompt k kh src escape? tag))))))
  789. (($ $throw src op param args)
  790. (convert-args cps args
  791. (lambda (cps args)
  792. (with-cps cps
  793. (build-term
  794. ($throw src op param args))))))))
  795. (intset-fold (lambda (label cps)
  796. (match (intmap-ref cps label (lambda (_) #f))
  797. (($ $kargs names vars term)
  798. (with-cps cps
  799. (let$ term (visit-term term))
  800. (setk label ($kargs names vars ,term))))
  801. (($ $kfun src meta self ktail kclause)
  802. (if (intset-ref elidable label)
  803. (with-cps cps
  804. (setk label ($kfun src meta #f ktail kclause)))
  805. cps))
  806. (_ cps)))
  807. body
  808. cps)))
  809. (define (convert-closures cps)
  810. "Convert free reference in @var{cps} to primcalls to @code{free-ref},
  811. and allocate and initialize flat closures."
  812. (let* ((kfun 0) ;; Ass-u-me.
  813. ;; label -> body-label...
  814. (functions (compute-function-bodies cps kfun))
  815. (cps (filter-reachable cps functions))
  816. ;; label -> bound-var...
  817. (label->bound (compute-function-names cps functions))
  818. ;; bound-var -> label
  819. (bound->label (invert-partition label->bound))
  820. ;; label...
  821. (well-known (compute-well-known-functions cps bound->label))
  822. ;; label -> closure-var
  823. (shared (compute-shared-closures cps well-known))
  824. (cps (rewrite-shared-closure-calls cps functions label->bound shared
  825. kfun))
  826. ;; label -> free-var...
  827. (free-vars (compute-free-vars cps kfun shared))
  828. (free-vars (prune-free-vars free-vars bound->label well-known shared))
  829. ;; label...
  830. (elidable (compute-elidable-closures cps well-known shared free-vars)))
  831. (let ((free-in-program (intmap-ref free-vars kfun)))
  832. (unless (eq? empty-intset free-in-program)
  833. (error "Expected no free vars in program" free-in-program)))
  834. (with-fresh-name-state cps
  835. (persistent-intmap
  836. (intmap-fold
  837. (lambda (label body cps)
  838. (convert-one cps label body free-vars bound->label well-known shared
  839. elidable))
  840. functions
  841. cps)))))
  842. ;;; Local Variables:
  843. ;;; eval: (put 'convert-arg 'scheme-indent-function 2)
  844. ;;; eval: (put 'convert-args 'scheme-indent-function 2)
  845. ;;; End: