closure-conversion.scm 35 KB

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