to-cps.scm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey
  3. ; Convert a byte-code-compiler node into a cps node.
  4. ; Entry point.
  5. (define (x->cps node name)
  6. (receive (value first-call last-lambda)
  7. (cps node)
  8. (if first-call
  9. (bug "(X->CPS ~S) got a non-value" node))
  10. (maybe-add-name! value name)
  11. value))
  12. ;----------------------------------------------------------------
  13. ; (CPS <node>)
  14. ; -> <value> <first-call> <last-lambda>
  15. ; <value> is the CPSed value of <node>. If <node> contains no non-trivial
  16. ; calls, <first-call> and <last-lambda> are both #f. Otherwise they are
  17. ; the first of the non-trivial calls and the continuation of the last.
  18. (define (cps node)
  19. (receive (value first-call last-lambda)
  20. (real-cps node)
  21. (let ((value (cond ((not (list? value))
  22. value)
  23. ((or (null? value)
  24. (not (null? (cdr value))))
  25. (bug "value expression did not return one value ~S"
  26. (schemify node)))
  27. (else
  28. (car value)))))
  29. (values value first-call last-lambda))))
  30. ; Same as above except that <value> is a list of values.
  31. (define (values-cps node)
  32. (receive (value first-call last-lambda)
  33. (real-cps node)
  34. (values (if (list? value)
  35. value
  36. (list value))
  37. first-call
  38. last-lambda)))
  39. (define (real-cps node)
  40. ((operator-table-ref cps-converters
  41. (node-operator-id node))
  42. node))
  43. (define cps-converters
  44. (make-operator-table
  45. (lambda (node id)
  46. (error "no cps-converter for node ~S" node))))
  47. (define (define-cps-converter name proc)
  48. (operator-define! cps-converters name #f proc))
  49. ;----------------------------------------------------------------
  50. ; (TAIL-CPS <node> <continuation-variable>)
  51. ; -> <first-call>
  52. (define (tail-cps node cont-var)
  53. ((operator-table-ref tail-cps-converters (node-operator-id node))
  54. node
  55. cont-var))
  56. (define tail-cps-converters
  57. (make-operator-table
  58. (lambda (node cont-var)
  59. (error "no tail-cps-converter for node ~S" node))))
  60. (define (define-tail-cps-converter name proc)
  61. (operator-define! tail-cps-converters name #f proc))
  62. ; Use PROC in the CPS table and give it a wrapper that makes a return for use
  63. ; in the TAIL-CPS table.
  64. (define (define-value-cps-converter name proc)
  65. (operator-define! cps-converters name #f
  66. (lambda (node)
  67. (values (proc node) #f #f)))
  68. (operator-define! tail-cps-converters name #f
  69. (lambda (node cont-var)
  70. (make-return cont-var (proc node)))))
  71. ; El Hacko Grande: we use the name of the CONT-VAR to determine whether
  72. ; it is a return or a join point.
  73. (define (join? var)
  74. (case (variable-name var)
  75. ((c) #f)
  76. ((j) #t)
  77. (else
  78. (bug "funny continuation variable name ~S" var))))
  79. (define (make-return cont-var value)
  80. (really-make-return cont-var (list value)))
  81. (define (make-multiple-value-return cont-var values)
  82. (really-make-return cont-var values))
  83. (define (really-make-return cont-var values)
  84. (let ((return (make-call-node
  85. (get-primop (if (join? cont-var)
  86. (enum primop jump)
  87. (enum primop unknown-return)))
  88. (+ 1 (length values))
  89. 0)))
  90. (attach-call-args return (cons (make-reference-node cont-var) values))
  91. return))
  92. ;----------------------------------------------------------------
  93. ; Constants are easy.
  94. (define-value-cps-converter 'literal
  95. (lambda (node)
  96. (cps-literal (node-form node) node)))
  97. (define-value-cps-converter 'quote
  98. (lambda (node)
  99. (cps-literal (cadr (node-form node)) node)))
  100. (define (cps-literal value node)
  101. (make-literal-node value (node-type node)))
  102. (define-value-cps-converter 'unspecific
  103. (lambda (node)
  104. (make-unspecific)))
  105. (define (make-unspecific)
  106. (make-call-node (get-prescheme-primop 'unspecific) 0 0))
  107. ; Used for primitives in non-call position. The CDR of the form is a
  108. ; variable that will be bound to the primitive's closed-compiled value.
  109. (define-value-cps-converter 'primitive
  110. (lambda (node)
  111. (make-reference-node (cdr (node-form node)))))
  112. ;----------------------------------------------------------------
  113. (define-value-cps-converter 'lambda
  114. (lambda (node)
  115. (let ((form (node-form node))
  116. (cont-var (make-variable 'c (lambda-node-return-type node)))
  117. (vars (map (lambda (name)
  118. (let ((var (make-variable (name-node->symbol name)
  119. (node-type name))))
  120. (node-set! name 'variable var)
  121. var))
  122. (cadr (node-form node)))))
  123. (let ((lnode (make-lambda-node 'p 'proc (cons cont-var vars))))
  124. (attach-body lnode (tail-cps (caddr form) cont-var))
  125. lnode))))
  126. ;----------------------------------------------------------------
  127. ; References and SET!
  128. (define-value-cps-converter 'name
  129. (lambda (node)
  130. (cond ((node-ref node 'variable)
  131. => make-reference-node) ; eventually have to check for SET!'s
  132. ((node-ref node 'binding)
  133. => (lambda (binding)
  134. (let ((var (binding-place binding)))
  135. (cond ((not (variable? var))
  136. (bug "binding for ~S has funny place ~S" node var))
  137. ((variable-set!? var)
  138. (make-global-ref var))
  139. (else
  140. (make-reference-node var))))))
  141. (else
  142. (bug "name node ~S has neither variable nor binding" node)))))
  143. (define (make-global-ref var)
  144. ((structure-ref node let-nodes)
  145. ((call (global-ref 0 (* var))))
  146. call))
  147. ; Stolen from form.scm as an expedient. This needs to be moved to somewhere
  148. ; that both FORMS and TO-CPS can see it.
  149. (define (variable-set!? var)
  150. (memq 'set! (variable-flags var)))
  151. (define-cps-converter 'set!
  152. (lambda (node)
  153. (receive (first-call last-lambda)
  154. (make-global-set! (node-form node))
  155. (values (make-unspecific) first-call last-lambda))))
  156. (define-tail-cps-converter 'set!
  157. (lambda (node cont-var)
  158. (receive (first-call last-lambda)
  159. (make-global-set! (node-form node))
  160. (attach-body last-lambda (make-return cont-var (make-unspecific)))
  161. first-call)))
  162. (define (make-global-set! form)
  163. (let ((name (cadr form))
  164. (value (caddr form)))
  165. (receive (value first-call last-lambda)
  166. (cps value)
  167. (maybe-add-name! value name)
  168. (let ((cont (make-lambda-node 'c 'cont '()))
  169. (var (name-node->variable name)))
  170. ((structure-ref node let-nodes)
  171. ((call (global-set! 1 cont (* var) value)))
  172. (values (splice!->first first-call last-lambda call)
  173. cont))))))
  174. (define (name-node->variable name-node)
  175. (let ((binding (node-ref name-node 'binding)))
  176. (if (and binding
  177. (variable? (binding-place binding)))
  178. (binding-place binding)
  179. (bug "name node ~S has no variable" name-node))))
  180. ;----------------------------------------------------------------
  181. ; CALL & GOTO
  182. (define-cps-converter 'call
  183. (lambda (node)
  184. (let ((exp (node-form node)))
  185. (convert-call (car exp) (cdr exp) node))))
  186. ; Treat non-tail-recursive GOTO's as normal calls.
  187. (define-cps-converter 'goto
  188. (lambda (node)
  189. (let ((exp (node-form node)))
  190. (user-warning "Ignoring non-tail-recursive GOTO: ~S" (schemify node))
  191. (convert-call (cadr exp) (cddr exp) node))))
  192. ; Dispatch on the procedure. Do something special with lambdas, primitives,
  193. ; primops (in literal nodes). Everything else is turned into an unknown call.
  194. ; Calls to primitives are expanded and then CPS'ed.
  195. (define (convert-call proc args node)
  196. (cond ((lambda-node? proc)
  197. (convert-let (node-form proc) args node))
  198. ((primitive-node? proc)
  199. (values-cps (expand-primitive-call proc args node)))
  200. ((and (literal-node? proc)
  201. (primop? (node-form proc)))
  202. (convert-primop-call (node-form proc) args (node-type node)))
  203. (else
  204. (convert-primop-call (get-primop (enum primop unknown-call))
  205. (cons proc ; add protocol argument
  206. (cons (make-literal normal-protocol)
  207. args))
  208. (node-type node)))))
  209. ; Same again, except that for unknown tail-recursive calls we use different
  210. ; protocols for CALL and GOTO.
  211. (define-tail-cps-converter 'call
  212. (lambda (node cont-var)
  213. (if (join? cont-var)
  214. (convert-and-add-jump node cont-var)
  215. (let ((exp (node-form node)))
  216. (tail-convert-call (car exp) (cdr exp) node cont-var normal-protocol)))))
  217. (define-tail-cps-converter 'goto
  218. (lambda (node cont-var)
  219. (if (join? cont-var)
  220. (convert-and-add-jump node cont-var)
  221. (let ((exp (node-form node)))
  222. (tail-convert-call (cadr exp) (cddr exp) node cont-var goto-protocol)))))
  223. (define (convert-and-add-jump node join-var)
  224. (receive (values first-call last-lambda)
  225. (values-cps node)
  226. (let ((jump (make-multiple-value-return join-var values)))
  227. (cond (first-call
  228. (attach-body last-lambda jump)
  229. first-call)
  230. (else
  231. jump)))))
  232. (define (tail-convert-call proc args node cont-var protocol)
  233. (cond ((lambda-node? proc)
  234. (convert-tail-let (node-form proc) args node cont-var))
  235. ((primitive-node? proc)
  236. (tail-cps (expand-primitive-call proc args node)
  237. cont-var))
  238. ((and (literal-node? proc)
  239. (primop? (node-form proc)))
  240. (convert-primop-tail-call (node-form proc) args cont-var))
  241. (else
  242. (convert-unknown-tail-call (cons proc args) cont-var protocol))))
  243. ; Every primitive has its own expander.
  244. (define (expand-primitive-call proc args node)
  245. ((primitive-expander (node-form proc)) args (node-type node)))
  246. (define lambda-node? (node-predicate 'lambda))
  247. (define primitive-node? (node-predicate 'primitive))
  248. (define literal-node? (node-predicate 'literal))
  249. (define literal-op (get-operator 'literal))
  250. (define (make-literal value)
  251. (make-node literal-op value))
  252. ;----------------------------------------------------------------
  253. ; LET (= a call whose procedure is a LAMBDA)
  254. ; REALLY-CONVERT-LET does all the work. These convert the body of the LET
  255. ; using either CPS or TAIL-CPS and connect everything up.
  256. (define (convert-let proc args node)
  257. (receive (lnode first-call)
  258. (really-convert-let proc args node)
  259. (receive (vals body-first-call body-last-lambda)
  260. (values-cps (caddr proc))
  261. (values vals
  262. first-call
  263. (splice!->last lnode body-first-call body-last-lambda)))))
  264. (define (convert-tail-let proc args node cont-var)
  265. (receive (lnode first-call)
  266. (really-convert-let proc args node)
  267. (attach-body lnode (tail-cps (caddr proc) cont-var))
  268. first-call))
  269. ; Make the call to the LET primop and build the lambda node for the procedure.
  270. (define (really-convert-let proc args node)
  271. (receive (call first-call last-lambda)
  272. (cps-call (get-primop (enum primop let)) 1 1 args cps)
  273. (let ((vars (map (lambda (name)
  274. (let ((var (make-variable (name-node->symbol name)
  275. (node-type name))))
  276. (node-set! name 'variable var)
  277. var))
  278. (cadr proc))))
  279. (do ((names (cadr proc) (cdr names))
  280. (index 1 (+ index 1)))
  281. ((null? names))
  282. (maybe-add-argument-name! call index (node-form (car names))))
  283. (let ((lnode (make-lambda-node #f 'cont vars)))
  284. (attach call 0 lnode)
  285. (values lnode (splice!->first first-call last-lambda call))))))
  286. ; Primitive calls
  287. ; Use CPS-CALL to do the work and then make a continuation if the primop is
  288. ; not trivial.
  289. (define (convert-primop-call primop args type)
  290. (let ((trivial? (primop-trivial? primop)))
  291. (receive (call first-call last-lambda)
  292. (cps-call primop (if trivial? 0 1) (if trivial? 0 1) args cps)
  293. (if (not trivial?)
  294. (add-continuation call first-call last-lambda type)
  295. (values call first-call last-lambda)))))
  296. (define (add-continuation call first-call last-lambda type)
  297. (let* ((vars (map (lambda (type)
  298. (make-variable 'v type))
  299. (if (tuple-type? type)
  300. (tuple-type-types type)
  301. (list type))))
  302. (cont (make-lambda-node 'c 'cont vars)))
  303. (attach call 0 cont)
  304. (values (if (tuple-type? type)
  305. (map make-reference-node vars)
  306. (make-reference-node (car vars)))
  307. (splice!->first first-call last-lambda call)
  308. cont)))
  309. ; Call CONVERT-PRIMOP-CALL and then make a return.
  310. (define (convert-primop-tail-call primop args cont-var)
  311. (receive (value first-call last-lambda)
  312. (convert-primop-call primop args (variable-type cont-var))
  313. (splice!->first first-call
  314. last-lambda
  315. (if (list? value)
  316. (make-multiple-value-return cont-var value)
  317. (make-return cont-var value)))))
  318. ; Another front for CPS-CALL, passing it the UNKNOWN-TAIL-CALL primop and
  319. ; its arguments, which are the procedure being called, the protocol, and
  320. ; the actual arguments.
  321. (define (convert-unknown-tail-call args cont-var protocol)
  322. (receive (call first-call last-lambda)
  323. (cps-call (get-primop (enum primop unknown-tail-call)) 0 1
  324. (cons (car args)
  325. (cons (make-literal protocol) (cdr args)))
  326. cps)
  327. (attach call 0 (make-reference-node cont-var))
  328. (splice!->first first-call last-lambda call)))
  329. ;----------------------------------------------------------------
  330. ; BEGIN
  331. ; These are fronts for CPS-SEQUENCE.
  332. (define-cps-converter 'begin
  333. (lambda (node)
  334. (receive (last-node real-first-call last-lambda)
  335. (cps-sequence (cdr (node-form node)) values-cps)
  336. (if (not real-first-call)
  337. (cps last-node)
  338. (receive (vals first-call real-last-lambda)
  339. (values-cps last-node)
  340. (values vals
  341. real-first-call
  342. (splice!->last last-lambda first-call real-last-lambda)))))))
  343. (define-tail-cps-converter 'begin
  344. (lambda (node cont-var)
  345. (receive (last-node first-call last-lambda)
  346. (cps-sequence (cdr (node-form node)) values-cps)
  347. (splice!->first first-call last-lambda (tail-cps last-node cont-var)))))
  348. ;----------------------------------------------------------------
  349. ;
  350. ; (IF <a> <b> <c>)
  351. ; =>
  352. ; (LET ((J (LAMBDA (V) [rest-goes-here])))
  353. ; (TEST (LAMBDA () [tail-cps <b> J])
  354. ; (LAMBDA () [tail-cps <c> J])
  355. ; <a>))
  356. (define-cps-converter 'if
  357. (lambda (node)
  358. (let ((exp (node-form node))
  359. (join-var (make-variable 'j type/unknown))
  360. (res-vars (make-variables (node-type node))))
  361. (receive (call first-call last-lambda)
  362. (convert-if exp join-var)
  363. (let ((let-lambda (make-lambda-node 'c 'cont (list join-var)))
  364. (let-call (make-call-node (get-primop (enum primop let)) 2 1))
  365. (join-lambda (make-lambda-node 'j 'jump res-vars)))
  366. (attach let-call 0 let-lambda)
  367. (attach let-call 1 join-lambda)
  368. (attach-body let-lambda call)
  369. (values (map make-reference-node res-vars)
  370. (splice!->first first-call last-lambda let-call )
  371. join-lambda))))))
  372. (define (make-variables type)
  373. (map (lambda (type)
  374. (make-variable 'v type))
  375. (if (tuple-type? type)
  376. (tuple-type-types type)
  377. (list type))))
  378. ; Tail-recursive IFs do not require a join point.
  379. (define-tail-cps-converter 'if
  380. (lambda (node cont-var)
  381. (let ((exp (node-form node)))
  382. (receive (call first-call last-lambda)
  383. (convert-if exp cont-var)
  384. (splice!->first first-call last-lambda call)))))
  385. ; Actually build the two-continuation call to the TEST primop.
  386. (define (convert-if exp cont-var)
  387. (receive (call first-call last-lambda)
  388. (cps-call (get-prescheme-primop 'test) 2 2 (list (cadr exp)) cps)
  389. (let ((true-cont (make-lambda-node 'c 'cont '()))
  390. (true-call (tail-cps (caddr exp) cont-var))
  391. (false-cont (make-lambda-node 'c 'cont '()))
  392. (false-call (tail-cps (cadddr exp) cont-var)))
  393. (attach-body true-cont true-call)
  394. (attach-body false-cont false-call)
  395. (attach call 0 true-cont)
  396. (attach call 1 false-cont)
  397. (values call first-call last-lambda))))
  398. ;----------------------------------------------------------------
  399. (define-cps-converter 'values
  400. (lambda (node)
  401. (let ((args (cdr (node-form node))))
  402. (receive (call first-call last-lambda)
  403. (cps-call (get-prescheme-primop 'unspecific) 0 0 args cps)
  404. (let ((vals (vector->list (call-args call))))
  405. (map detach vals)
  406. (values vals first-call last-lambda))))))
  407. (define-tail-cps-converter 'values
  408. (lambda (node cont-var)
  409. (let ((args (cdr (node-form node))))
  410. (receive (call first-call last-lambda)
  411. (cps-call (get-primop (enum primop unknown-return)) 0 1 args cps)
  412. (attach call 0 (make-reference-node cont-var))
  413. (splice!->first first-call last-lambda call)))))
  414. (define-cps-converter 'call-with-values
  415. (lambda (node)
  416. (convert-call-with-values node #f)))
  417. (define-tail-cps-converter 'call-with-values
  418. (lambda (node cont-var)
  419. (convert-call-with-values node cont-var)))
  420. ; Consumer is known to be a lambda node.
  421. (define (convert-call-with-values node maybe-cont-var)
  422. (receive (vals first-call last-lambda)
  423. (values-cps (cadr (node-form node)))
  424. (let ((consumer (x->cps (caddr (node-form node)) #f))
  425. (call (make-call-node (get-primop (if maybe-cont-var
  426. (enum primop tail-call)
  427. (enum primop call)))
  428. (+ 2 (length vals))
  429. (if maybe-cont-var 0 1))))
  430. (attach-call-args call `(#f ,consumer . ,vals))
  431. (cond (maybe-cont-var
  432. (attach call 0 (make-reference-node maybe-cont-var))
  433. (splice!->first first-call last-lambda call))
  434. (else
  435. (add-continuation call first-call last-lambda (node-type node)))))))
  436. ;----------------------------------------------------------------
  437. ; LETRECs have been analyzed and restructured by FLATTEN, so we know that
  438. ; the values are all lambdas.
  439. (define-cps-converter 'letrec
  440. (lambda (node)
  441. (let ((form (node-form node)))
  442. (receive (first-call last-lambda)
  443. (convert-letrec form)
  444. (receive (vals body-first-call body-last-lambda)
  445. (values-cps (caddr form))
  446. (values vals
  447. first-call
  448. (splice!->last last-lambda
  449. body-first-call
  450. body-last-lambda)))))))
  451. (define-tail-cps-converter 'letrec
  452. (lambda (node cont-var)
  453. (let ((form (node-form node)))
  454. (receive (first-call last-lambda)
  455. (convert-letrec form)
  456. (attach-body last-lambda (tail-cps (caddr form) cont-var))
  457. first-call))))
  458. (define (convert-letrec form)
  459. (let ((vars (map (lambda (l)
  460. (let ((var (make-variable (name-node->symbol (car l))
  461. (node-type (car l)))))
  462. (node-set! (car l) 'variable var)
  463. var))
  464. (cadr form)))
  465. (vals (map (lambda (l)
  466. (receive (value first-call last-lambda)
  467. (cps (cadr l))
  468. value))
  469. (cadr form)))
  470. (cont (make-lambda-node 'c 'cont '())))
  471. ((structure-ref node let-nodes)
  472. ((top (letrec1 1 l1))
  473. (l1 ((x #f) . vars) call2)
  474. (call2 (letrec2 1 cont (* x) . vals)))
  475. (do ((names (cadr form) (cdr names))
  476. (index 2 (+ index 1)))
  477. ((null? names))
  478. (maybe-add-argument-name! call2 index (node-form (caar names))))
  479. (values top cont))))
  480. ;----------------------------------------------------------------
  481. ; Utilities.
  482. ; Stuff is a list of alternating call and lambda nodes, with possible #Fs.
  483. ; This joins the nodes together by making the calls be the bodies of the
  484. ; lambdas (the call->lambda links are already done). The last node is
  485. ; returned.
  486. (define (splice! stuff)
  487. (let loop ((stuff stuff) (first #f) (last #f))
  488. (if (null? stuff)
  489. (values first last)
  490. (receive (first last)
  491. (let ((next (car stuff)))
  492. (cond ((not next)
  493. (values first last))
  494. ((not first)
  495. (values next next))
  496. (else
  497. (if (and ((structure-ref node lambda-node?) last)
  498. ((structure-ref node call-node?) next))
  499. (attach-body last next))
  500. (values first next))))
  501. (loop (cdr stuff) first last)))))
  502. (define (splice!->first . stuff)
  503. (receive (first last)
  504. (splice! stuff)
  505. first))
  506. (define (splice!->last . stuff)
  507. (receive (first last)
  508. (splice! stuff)
  509. last))
  510. ; Stuff for making CPS nodes
  511. (define make-reference-node (structure-ref node make-reference-node))
  512. (define make-lambda-node (structure-ref node make-lambda-node))
  513. (define make-literal-node (structure-ref node make-literal-node))
  514. (define make-call-node (structure-ref node make-call-node))
  515. (define attach (structure-ref node attach))
  516. (define detach (structure-ref node detach))
  517. (define attach-body (structure-ref node attach-body))
  518. (define attach-call-args (structure-ref node attach-call-args))
  519. (define call-args (structure-ref node call-args))
  520. ; Adding names to lambda nodes for debugging help.
  521. (define (maybe-add-argument-name! call index name)
  522. (maybe-add-name! ((structure-ref node call-arg) call index) name))
  523. (define (maybe-add-name! value name)
  524. (if ((structure-ref node lambda-node?) value)
  525. ((structure-ref node set-lambda-name!) value (schemify name))))
  526. ; Getting symbols for use as variable names.
  527. (define (name-node->symbol node)
  528. (let loop ((name (node-form node)))
  529. (if (generated? name)
  530. (loop (generated-name name))
  531. name)))