compile-bytecode.scm 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  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. ;;; Compiling CPS to bytecode. The result is in the bytecode language,
  19. ;;; which happens to be an ELF image as a bytecode.
  20. ;;;
  21. ;;; Code:
  22. (define-module (language cps compile-bytecode)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (language cps)
  26. #:use-module (language cps slot-allocation)
  27. #:use-module (language cps utils)
  28. #:use-module (language cps closure-conversion)
  29. #:use-module (language cps loop-instrumentation)
  30. #:use-module (language cps optimize)
  31. #:use-module (language cps reify-primitives)
  32. #:use-module (language cps renumber)
  33. #:use-module (language cps split-rec)
  34. #:use-module (language cps intmap)
  35. #:use-module (language cps intset)
  36. #:use-module (system vm assembler)
  37. #:use-module (system base types internal)
  38. #:export (compile-bytecode))
  39. (define (kw-arg-ref args kw default)
  40. (match (memq kw args)
  41. ((_ val . _) val)
  42. (_ default)))
  43. (define (intmap-for-each f map)
  44. (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
  45. (define (intmap-select map set)
  46. (persistent-intmap
  47. (intset-fold
  48. (lambda (k out)
  49. (intmap-add! out k (intmap-ref map k)))
  50. set
  51. empty-intmap)))
  52. ;; Any $values expression that continues to a $kargs and causes no
  53. ;; shuffles is a forwarding label.
  54. (define (compute-forwarding-labels cps allocation)
  55. (fixpoint
  56. (lambda (forwarding-map)
  57. (intmap-fold (lambda (label target forwarding-map)
  58. (let ((new-target (intmap-ref forwarding-map target
  59. (lambda (target) target))))
  60. (if (eqv? target new-target)
  61. forwarding-map
  62. (intmap-replace forwarding-map label new-target))))
  63. forwarding-map forwarding-map))
  64. (intmap-fold (lambda (label cont forwarding-labels)
  65. (match cont
  66. (($ $kargs _ _ ($ $continue k _ ($ $values)))
  67. (match (lookup-parallel-moves label allocation)
  68. (()
  69. (match (intmap-ref cps k)
  70. (($ $ktail) forwarding-labels)
  71. (_ (intmap-add forwarding-labels label k))))
  72. (_ forwarding-labels)))
  73. (_ forwarding-labels)))
  74. cps empty-intmap)))
  75. (define (compile-function cps asm opts)
  76. (let* ((allocation (allocate-slots cps #:precolor-calls?
  77. (kw-arg-ref opts #:precolor-calls? #t)))
  78. (forwarding-labels (compute-forwarding-labels cps allocation))
  79. (frame-size (lookup-nlocals allocation)))
  80. (define (forward-label k)
  81. (intmap-ref forwarding-labels k (lambda (k) k)))
  82. (define (elide-cont? label)
  83. (match (intmap-ref forwarding-labels label (lambda (_) #f))
  84. (#f #f)
  85. (target (not (eqv? label target)))))
  86. (define (maybe-slot sym)
  87. (lookup-maybe-slot sym allocation))
  88. (define (slot sym)
  89. (lookup-slot sym allocation))
  90. (define (from-sp var)
  91. (- frame-size 1 var))
  92. (define (maybe-mov dst src)
  93. (unless (= dst src)
  94. (emit-mov asm (from-sp dst) (from-sp src))))
  95. (define (compile-tail label exp)
  96. ;; There are only three kinds of expressions in tail position:
  97. ;; tail calls, multiple-value returns, and single-value returns.
  98. (define (maybe-reset-frame nlocals)
  99. (unless (= frame-size nlocals)
  100. (emit-reset-frame asm nlocals)))
  101. (match exp
  102. (($ $call proc args)
  103. (for-each (match-lambda
  104. ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
  105. (lookup-parallel-moves label allocation))
  106. (maybe-reset-frame (1+ (length args)))
  107. (emit-handle-interrupts asm)
  108. (emit-tail-call asm))
  109. (($ $callk k proc args)
  110. (for-each (match-lambda
  111. ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
  112. (lookup-parallel-moves label allocation))
  113. (let ((nclosure (if proc 1 0)))
  114. (maybe-reset-frame (+ nclosure (length args))))
  115. (emit-handle-interrupts asm)
  116. (emit-tail-call-label asm k))
  117. (($ $values args)
  118. (for-each (match-lambda
  119. ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
  120. (lookup-parallel-moves label allocation))
  121. (maybe-reset-frame (length args))
  122. (emit-handle-interrupts asm)
  123. (emit-return-values asm))))
  124. (define (compile-value label exp dst)
  125. (match exp
  126. (($ $values (arg))
  127. (maybe-mov dst (slot arg)))
  128. (($ $primcall (or 's64->u64 'u64->s64) #f (arg))
  129. (maybe-mov dst (slot arg)))
  130. (($ $const exp)
  131. (emit-load-constant asm (from-sp dst) exp))
  132. (($ $const-fun k)
  133. (emit-load-static-procedure asm (from-sp dst) k))
  134. (($ $code k)
  135. (emit-load-label asm (from-sp dst) k))
  136. (($ $primcall 'current-module)
  137. (emit-current-module asm (from-sp dst)))
  138. (($ $primcall 'current-thread)
  139. (emit-current-thread asm (from-sp dst)))
  140. (($ $primcall 'define! #f (mod sym))
  141. (emit-define! asm (from-sp dst)
  142. (from-sp (slot mod)) (from-sp (slot sym))))
  143. (($ $primcall 'resolve (bound?) (name))
  144. (emit-resolve asm (from-sp dst) bound? (from-sp (slot name))))
  145. (($ $primcall 'allocate-words annotation (nfields))
  146. (emit-allocate-words asm (from-sp dst) (from-sp (slot nfields))))
  147. (($ $primcall 'allocate-words/immediate (annotation . nfields))
  148. (emit-allocate-words/immediate asm (from-sp dst) nfields))
  149. (($ $primcall 'allocate-pointerless-words annotation (nfields))
  150. (emit-allocate-pointerless-words asm (from-sp dst)
  151. (from-sp (slot nfields))))
  152. (($ $primcall 'allocate-pointerless-words/immediate
  153. (annotation . nfields))
  154. (emit-allocate-pointerless-words/immediate asm (from-sp dst) nfields))
  155. (($ $primcall 'scm-ref annotation (obj idx))
  156. (emit-scm-ref asm (from-sp dst) (from-sp (slot obj))
  157. (from-sp (slot idx))))
  158. (($ $primcall 'scm-ref/tag annotation (obj))
  159. (let ((tag (match annotation
  160. ('pair %tc1-pair)
  161. ('struct %tc3-struct))))
  162. (emit-scm-ref/tag asm (from-sp dst) (from-sp (slot obj)) tag)))
  163. (($ $primcall 'scm-ref/immediate (annotation . idx) (obj))
  164. (emit-scm-ref/immediate asm (from-sp dst) (from-sp (slot obj)) idx))
  165. (($ $primcall 'word-ref annotation (obj idx))
  166. (emit-word-ref asm (from-sp dst) (from-sp (slot obj))
  167. (from-sp (slot idx))))
  168. (($ $primcall 'word-ref/immediate (annotation . idx) (obj))
  169. (emit-word-ref/immediate asm (from-sp dst) (from-sp (slot obj)) idx))
  170. (($ $primcall 'pointer-ref/immediate (annotation . idx) (obj))
  171. (emit-pointer-ref/immediate asm (from-sp dst) (from-sp (slot obj)) idx))
  172. (($ $primcall 'tail-pointer-ref/immediate (annotation . idx) (obj))
  173. (emit-tail-pointer-ref/immediate asm (from-sp dst) (from-sp (slot obj))
  174. idx))
  175. (($ $primcall 'cache-ref key ())
  176. (emit-cache-ref asm (from-sp dst) key))
  177. (($ $primcall 'resolve-module public? (name))
  178. (emit-resolve-module asm (from-sp dst) (from-sp (slot name)) public?))
  179. (($ $primcall 'lookup #f (mod name))
  180. (emit-lookup asm (from-sp dst) (from-sp (slot mod)) (from-sp (slot name))))
  181. (($ $primcall 'add/immediate y (x))
  182. (emit-add/immediate asm (from-sp dst) (from-sp (slot x)) y))
  183. (($ $primcall 'sub/immediate y (x))
  184. (emit-sub/immediate asm (from-sp dst) (from-sp (slot x)) y))
  185. (($ $primcall 'uadd/immediate y (x))
  186. (emit-uadd/immediate asm (from-sp dst) (from-sp (slot x)) y))
  187. (($ $primcall 'usub/immediate y (x))
  188. (emit-usub/immediate asm (from-sp dst) (from-sp (slot x)) y))
  189. (($ $primcall 'umul/immediate y (x))
  190. (emit-umul/immediate asm (from-sp dst) (from-sp (slot x)) y))
  191. (($ $primcall 'rsh (x y))
  192. (emit-rsh asm (from-sp dst) (from-sp (slot x)) (from-sp (slot y))))
  193. (($ $primcall 'lsh (x y))
  194. (emit-lsh asm (from-sp dst) (from-sp (slot x)) (from-sp (slot y))))
  195. (($ $primcall 'rsh/immediate y (x))
  196. (emit-rsh/immediate asm (from-sp dst) (from-sp (slot x)) y))
  197. (($ $primcall 'lsh/immediate y (x))
  198. (emit-lsh/immediate asm (from-sp dst) (from-sp (slot x)) y))
  199. (($ $primcall 'ursh/immediate y (x))
  200. (emit-ursh/immediate asm (from-sp dst) (from-sp (slot x)) y))
  201. (($ $primcall 'srsh/immediate y (x))
  202. (emit-srsh/immediate asm (from-sp dst) (from-sp (slot x)) y))
  203. (($ $primcall 'ulsh/immediate y (x))
  204. (emit-ulsh/immediate asm (from-sp dst) (from-sp (slot x)) y))
  205. (($ $primcall 'builtin-ref idx ())
  206. (emit-builtin-ref asm (from-sp dst) idx))
  207. (($ $primcall 'scm->f64 #f (src))
  208. (emit-scm->f64 asm (from-sp dst) (from-sp (slot src))))
  209. (($ $primcall 'load-f64 val ())
  210. (emit-load-f64 asm (from-sp dst) val))
  211. (($ $primcall 'scm->u64 #f (src))
  212. (emit-scm->u64 asm (from-sp dst) (from-sp (slot src))))
  213. (($ $primcall 'scm->u64/truncate #f (src))
  214. (emit-scm->u64/truncate asm (from-sp dst) (from-sp (slot src))))
  215. (($ $primcall 'load-u64 val ())
  216. (emit-load-u64 asm (from-sp dst) val))
  217. (($ $primcall 'u64->scm #f (src))
  218. (emit-u64->scm asm (from-sp dst) (from-sp (slot src))))
  219. (($ $primcall 'scm->s64 #f (src))
  220. (emit-scm->s64 asm (from-sp dst) (from-sp (slot src))))
  221. (($ $primcall 'load-s64 val ())
  222. (emit-load-s64 asm (from-sp dst) val))
  223. (($ $primcall 's64->scm #f (src))
  224. (emit-s64->scm asm (from-sp dst) (from-sp (slot src))))
  225. (($ $primcall 'u8-ref ann (obj ptr idx))
  226. (emit-u8-ref asm (from-sp dst) (from-sp (slot ptr))
  227. (from-sp (slot idx))))
  228. (($ $primcall 's8-ref ann (obj ptr idx))
  229. (emit-s8-ref asm (from-sp dst) (from-sp (slot ptr))
  230. (from-sp (slot idx))))
  231. (($ $primcall 'u16-ref ann (obj ptr idx))
  232. (emit-u16-ref asm (from-sp dst) (from-sp (slot ptr))
  233. (from-sp (slot idx))))
  234. (($ $primcall 's16-ref ann (obj ptr idx))
  235. (emit-s16-ref asm (from-sp dst) (from-sp (slot ptr))
  236. (from-sp (slot idx))))
  237. (($ $primcall 'u32-ref ann (obj ptr idx))
  238. (emit-u32-ref asm (from-sp dst) (from-sp (slot ptr))
  239. (from-sp (slot idx))))
  240. (($ $primcall 's32-ref ann (obj ptr idx))
  241. (emit-s32-ref asm (from-sp dst) (from-sp (slot ptr))
  242. (from-sp (slot idx))))
  243. (($ $primcall 'u64-ref ann (obj ptr idx))
  244. (emit-u64-ref asm (from-sp dst) (from-sp (slot ptr))
  245. (from-sp (slot idx))))
  246. (($ $primcall 's64-ref ann (obj ptr idx))
  247. (emit-s64-ref asm (from-sp dst) (from-sp (slot ptr))
  248. (from-sp (slot idx))))
  249. (($ $primcall 'f32-ref ann (obj ptr idx))
  250. (emit-f32-ref asm (from-sp dst) (from-sp (slot ptr))
  251. (from-sp (slot idx))))
  252. (($ $primcall 'f64-ref ann (obj ptr idx))
  253. (emit-f64-ref asm (from-sp dst) (from-sp (slot ptr))
  254. (from-sp (slot idx))))
  255. (($ $primcall 'atomic-scm-ref/immediate (annotation . idx) (obj))
  256. (emit-atomic-scm-ref/immediate asm (from-sp dst) (from-sp (slot obj))
  257. idx))
  258. (($ $primcall 'atomic-scm-swap!/immediate (annotation . idx) (obj val))
  259. (emit-atomic-scm-swap!/immediate asm (from-sp dst) (from-sp (slot obj))
  260. idx (from-sp (slot val))))
  261. (($ $primcall 'atomic-scm-compare-and-swap!/immediate (annotation . idx)
  262. (obj expected desired))
  263. (emit-atomic-scm-compare-and-swap!/immediate
  264. asm (from-sp dst) (from-sp (slot obj)) idx (from-sp (slot expected))
  265. (from-sp (slot desired))))
  266. (($ $primcall 'untag-fixnum #f (src))
  267. (emit-untag-fixnum asm (from-sp dst) (from-sp (slot src))))
  268. (($ $primcall 'tag-fixnum #f (src))
  269. (emit-tag-fixnum asm (from-sp dst) (from-sp (slot src))))
  270. (($ $primcall 'untag-char #f (src))
  271. (emit-untag-char asm (from-sp dst) (from-sp (slot src))))
  272. (($ $primcall 'tag-char #f (src))
  273. (emit-tag-char asm (from-sp dst) (from-sp (slot src))))
  274. (($ $primcall name #f args)
  275. ;; FIXME: Inline all the cases.
  276. (emit-text asm `((,name ,(from-sp dst)
  277. ,@(map (compose from-sp slot) args)))))))
  278. (define (compile-effect label exp k)
  279. (match exp
  280. (($ $values ()) #f)
  281. (($ $primcall 'cache-set! key (val))
  282. (emit-cache-set! asm key (from-sp (slot val))))
  283. (($ $primcall 'scm-set! annotation (obj idx val))
  284. (emit-scm-set! asm (from-sp (slot obj)) (from-sp (slot idx))
  285. (from-sp (slot val))))
  286. (($ $primcall 'scm-set!/tag annotation (obj val))
  287. (let ((tag (match annotation
  288. ('pair %tc1-pair)
  289. ('struct %tc3-struct))))
  290. (emit-scm-set!/tag asm (from-sp (slot obj)) tag
  291. (from-sp (slot val)))))
  292. (($ $primcall 'scm-set!/immediate (annotation . idx) (obj val))
  293. (emit-scm-set!/immediate asm (from-sp (slot obj)) idx
  294. (from-sp (slot val))))
  295. (($ $primcall 'word-set! annotation (obj idx val))
  296. (emit-word-set! asm (from-sp (slot obj)) (from-sp (slot idx))
  297. (from-sp (slot val))))
  298. (($ $primcall 'word-set!/immediate (annotation . idx) (obj val))
  299. (emit-word-set!/immediate asm (from-sp (slot obj)) idx
  300. (from-sp (slot val))))
  301. (($ $primcall 'pointer-set!/immediate (annotation . idx) (obj val))
  302. (emit-pointer-set!/immediate asm (from-sp (slot obj)) idx
  303. (from-sp (slot val))))
  304. (($ $primcall 'string-set! #f (string index char))
  305. (emit-string-set! asm (from-sp (slot string)) (from-sp (slot index))
  306. (from-sp (slot char))))
  307. (($ $primcall 'push-fluid #f (fluid val))
  308. (emit-push-fluid asm (from-sp (slot fluid)) (from-sp (slot val))))
  309. (($ $primcall 'pop-fluid #f ())
  310. (emit-pop-fluid asm))
  311. (($ $primcall 'push-dynamic-state #f (state))
  312. (emit-push-dynamic-state asm (from-sp (slot state))))
  313. (($ $primcall 'pop-dynamic-state #f ())
  314. (emit-pop-dynamic-state asm))
  315. (($ $primcall 'wind #f (winder unwinder))
  316. (emit-wind asm (from-sp (slot winder)) (from-sp (slot unwinder))))
  317. (($ $primcall 'u8-set! ann (obj ptr idx val))
  318. (emit-u8-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  319. (from-sp (slot val))))
  320. (($ $primcall 's8-set! ann (obj ptr idx val))
  321. (emit-s8-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  322. (from-sp (slot val))))
  323. (($ $primcall 'u16-set! ann (obj ptr idx val))
  324. (emit-u16-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  325. (from-sp (slot val))))
  326. (($ $primcall 's16-set! ann (obj ptr idx val))
  327. (emit-s16-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  328. (from-sp (slot val))))
  329. (($ $primcall 'u32-set! ann (obj ptr idx val))
  330. (emit-u32-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  331. (from-sp (slot val))))
  332. (($ $primcall 's32-set! ann (obj ptr idx val))
  333. (emit-s32-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  334. (from-sp (slot val))))
  335. (($ $primcall 'u64-set! ann (obj ptr idx val))
  336. (emit-u64-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  337. (from-sp (slot val))))
  338. (($ $primcall 's64-set! ann (obj ptr idx val))
  339. (emit-s64-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  340. (from-sp (slot val))))
  341. (($ $primcall 'f32-set! ann (obj ptr idx val))
  342. (emit-f32-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  343. (from-sp (slot val))))
  344. (($ $primcall 'f64-set! ann (obj ptr idx val))
  345. (emit-f64-set! asm (from-sp (slot ptr)) (from-sp (slot idx))
  346. (from-sp (slot val))))
  347. (($ $primcall 'unwind #f ())
  348. (emit-unwind asm))
  349. (($ $primcall 'fluid-set! #f (fluid value))
  350. (emit-fluid-set! asm (from-sp (slot fluid)) (from-sp (slot value))))
  351. (($ $primcall 'atomic-scm-set!/immediate (annotation . idx) (obj val))
  352. (emit-atomic-scm-set!/immediate asm (from-sp (slot obj)) idx
  353. (from-sp (slot val))))
  354. (($ $primcall 'instrument-loop #f ())
  355. (emit-instrument-loop asm)
  356. (emit-handle-interrupts asm))))
  357. (define (compile-throw op param args)
  358. (match (vector op param args)
  359. (#('throw #f (key args))
  360. (emit-throw asm (from-sp (slot key)) (from-sp (slot args))))
  361. (#('throw/value param (val))
  362. (emit-throw/value asm (from-sp (slot val)) param))
  363. (#('throw/value+data param (val))
  364. (emit-throw/value+data asm (from-sp (slot val)) param))))
  365. (define (emit-parallel-moves-after-return-and-reset-frame label nlocals)
  366. (let lp ((moves (lookup-parallel-moves label allocation))
  367. (reset-frame? #f))
  368. (cond
  369. ((and (not reset-frame?)
  370. (and-map (match-lambda
  371. ((src . dst)
  372. (and (< src nlocals) (< dst nlocals))))
  373. moves))
  374. (emit-reset-frame asm nlocals)
  375. (lp moves #t))
  376. (else
  377. (match moves
  378. (() #t)
  379. (((src . dst) . moves)
  380. (emit-fmov asm dst src)
  381. (lp moves reset-frame?)))))))
  382. (define (compile-prompt label k kh escape? tag)
  383. (match (intmap-ref cps kh)
  384. (($ $kreceive ($ $arity req () rest () #f) khandler-body)
  385. (let ((receive-args (gensym "handler"))
  386. (nreq (length req))
  387. (proc-slot (lookup-call-proc-slot label allocation)))
  388. (emit-prompt asm (from-sp (slot tag)) escape? proc-slot
  389. receive-args)
  390. (emit-j asm k)
  391. (emit-label asm receive-args)
  392. (unless (and rest (zero? nreq))
  393. (emit-receive-values asm proc-slot (->bool rest) nreq))
  394. (when (and rest
  395. (match (intmap-ref cps khandler-body)
  396. (($ $kargs names (_ ... rest))
  397. (maybe-slot rest))))
  398. (emit-bind-rest asm (+ proc-slot nreq)))
  399. (emit-parallel-moves-after-return-and-reset-frame kh frame-size)
  400. (emit-j asm (forward-label khandler-body))))))
  401. (define (compile-values label exp syms)
  402. (match exp
  403. (($ $values args)
  404. (for-each (match-lambda
  405. ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
  406. (lookup-parallel-moves label allocation)))))
  407. (define (compile-test label next-label kf kt op param args)
  408. (define (prefer-true?)
  409. (if (< (max kt kf) label)
  410. ;; Two backwards branches. Prefer
  411. ;; the nearest.
  412. (> kt kf)
  413. ;; Otherwise prefer a backwards
  414. ;; branch or a near jump.
  415. (< kt kf)))
  416. (define (emit-branch emit-jt emit-jf)
  417. (cond
  418. ((eq? kt next-label)
  419. (emit-jf asm kf))
  420. ((eq? kf next-label)
  421. (emit-jt asm kt))
  422. ((prefer-true?)
  423. (emit-jt asm kt)
  424. (emit-j asm kf))
  425. (else
  426. (emit-jf asm kf)
  427. (emit-j asm kt))))
  428. (define (unary op a)
  429. (op asm (from-sp (slot a)))
  430. (emit-branch emit-je emit-jne))
  431. (define (binary op emit-jt emit-jf a b)
  432. (op asm (from-sp (slot a)) (from-sp (slot b)))
  433. (emit-branch emit-jt emit-jf))
  434. (define (binary-test op a b)
  435. (binary op emit-je emit-jne a b))
  436. (define (binary-< emit-<? a b)
  437. (binary emit-<? emit-jl emit-jnl a b))
  438. (define (binary-<= emit-<? a b)
  439. (binary emit-<? emit-jge emit-jnge b a))
  440. (define (binary-test/imm op a b)
  441. (op asm (from-sp (slot a)) b)
  442. (emit-branch emit-je emit-jne))
  443. (define (binary-</imm op a b)
  444. (op asm (from-sp (slot a)) b)
  445. (emit-branch emit-jl emit-jnl))
  446. (match (vector op param args)
  447. ;; Immediate type tag predicates.
  448. (#('fixnum? #f (a)) (unary emit-fixnum? a))
  449. (#('heap-object? #f (a)) (unary emit-heap-object? a))
  450. (#('char? #f (a)) (unary emit-char? a))
  451. (#('eq-false? #f (a)) (unary emit-eq-false? a))
  452. (#('eq-nil? #f (a)) (unary emit-eq-nil? a))
  453. (#('eq-null? #f (a)) (unary emit-eq-null? a))
  454. (#('eq-true? #f (a)) (unary emit-eq-true? a))
  455. (#('unspecified? #f (a)) (unary emit-unspecified? a))
  456. (#('undefined? #f (a)) (unary emit-undefined? a))
  457. (#('eof-object? #f (a)) (unary emit-eof-object? a))
  458. (#('null? #f (a)) (unary emit-null? a))
  459. (#('false? #f (a)) (unary emit-false? a))
  460. (#('nil? #f (a)) (unary emit-nil? a))
  461. ;; Heap type tag predicates.
  462. (#('pair? #f (a)) (unary emit-pair? a))
  463. (#('struct? #f (a)) (unary emit-struct? a))
  464. (#('symbol? #f (a)) (unary emit-symbol? a))
  465. (#('variable? #f (a)) (unary emit-variable? a))
  466. (#('vector? #f (a)) (unary emit-vector? a))
  467. (#('mutable-vector? #f (a)) (unary emit-mutable-vector? a))
  468. (#('immutable-vector? #f (a)) (unary emit-immutable-vector? a))
  469. (#('string? #f (a)) (unary emit-string? a))
  470. (#('heap-number? #f (a)) (unary emit-heap-number? a))
  471. (#('hash-table? #f (a)) (unary emit-hash-table? a))
  472. (#('pointer? #f (a)) (unary emit-pointer? a))
  473. (#('fluid? #f (a)) (unary emit-fluid? a))
  474. (#('stringbuf? #f (a)) (unary emit-stringbuf? a))
  475. (#('dynamic-state? #f (a)) (unary emit-dynamic-state? a))
  476. (#('frame? #f (a)) (unary emit-frame? a))
  477. (#('keyword? #f (a)) (unary emit-keyword? a))
  478. (#('atomic-box? #f (a)) (unary emit-atomic-box? a))
  479. (#('syntax? #f (a)) (unary emit-syntax? a))
  480. (#('program? #f (a)) (unary emit-program? a))
  481. (#('vm-continuation? #f (a)) (unary emit-vm-continuation? a))
  482. (#('bytevector? #f (a)) (unary emit-bytevector? a))
  483. (#('weak-set? #f (a)) (unary emit-weak-set? a))
  484. (#('weak-table? #f (a)) (unary emit-weak-table? a))
  485. (#('array? #f (a)) (unary emit-array? a))
  486. (#('bitvector? #f (a)) (unary emit-bitvector? a))
  487. (#('smob? #f (a)) (unary emit-smob? a))
  488. (#('port? #f (a)) (unary emit-port? a))
  489. (#('bignum? #f (a)) (unary emit-bignum? a))
  490. (#('flonum? #f (a)) (unary emit-flonum? a))
  491. (#('compnum? #f (a)) (unary emit-compnum? a))
  492. (#('fracnum? #f (a)) (unary emit-fracnum? a))
  493. ;; Binary predicates.
  494. (#('eq? #f (a b)) (binary-test emit-eq? a b))
  495. (#('heap-numbers-equal? #f (a b))
  496. (binary-test emit-heap-numbers-equal? a b))
  497. (#('< #f (a b)) (binary-< emit-<? a b))
  498. (#('<= #f (a b)) (binary-<= emit-<? a b))
  499. (#('= #f (a b)) (binary-test emit-=? a b))
  500. (#('u64-< #f (a b)) (binary-< emit-u64<? a b))
  501. (#('u64-imm-< b (a)) (binary-</imm emit-u64-imm<? a b))
  502. (#('imm-u64-< b (a)) (binary-</imm emit-imm-u64<? a b))
  503. (#('u64-= #f (a b)) (binary-test emit-u64=? a b))
  504. (#('u64-imm-= b (a)) (binary-test/imm emit-s64-imm=? a b))
  505. (#('s64-= #f (a b)) (binary-test emit-u64=? a b))
  506. (#('s64-imm-= b (a)) (binary-test/imm emit-s64-imm=? a b))
  507. (#('s64-< #f (a b)) (binary-< emit-s64<? a b))
  508. (#('s64-imm-< b (a)) (binary-</imm emit-s64-imm<? a b))
  509. (#('imm-s64-< b (a)) (binary-</imm emit-imm-s64<? a b))
  510. (#('f64-< #f (a b)) (binary-< emit-f64<? a b))
  511. (#('f64-<= #f (a b)) (binary-<= emit-f64<? a b))
  512. (#('f64-= #f (a b)) (binary-test emit-f64=? a b))))
  513. (define (compile-trunc label k exp nreq rest-var)
  514. (define (do-call proc args emit-call)
  515. (let* ((proc-slot (lookup-call-proc-slot label allocation))
  516. (nclosure (if proc 1 0))
  517. (nargs (+ nclosure (length args)))
  518. (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
  519. (for-each (match-lambda
  520. ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
  521. (lookup-parallel-moves label allocation))
  522. (emit-handle-interrupts asm)
  523. (emit-call asm proc-slot nargs)
  524. (emit-slot-map asm proc-slot (lookup-slot-map label allocation))
  525. (cond
  526. ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
  527. (match (lookup-parallel-moves k allocation)
  528. ((((? (lambda (src) (= src proc-slot)) src)
  529. . dst)) dst)
  530. (_ #f)))
  531. ;; The usual case: one required live return value, ignoring
  532. ;; any additional values.
  533. => (lambda (dst)
  534. (emit-receive asm dst proc-slot frame-size)))
  535. (else
  536. (unless (and (zero? nreq) rest-var)
  537. (emit-receive-values asm proc-slot (->bool rest-var) nreq))
  538. (when (and rest-var (maybe-slot rest-var))
  539. (emit-bind-rest asm (+ proc-slot nreq)))
  540. (emit-parallel-moves-after-return-and-reset-frame k frame-size)))))
  541. (match exp
  542. (($ $call proc args)
  543. (do-call proc args
  544. (lambda (asm proc-slot nargs)
  545. (emit-call asm proc-slot nargs))))
  546. (($ $callk k proc args)
  547. (do-call proc args
  548. (lambda (asm proc-slot nargs)
  549. (emit-call-label asm proc-slot nargs k))))))
  550. (define (skip-elided-conts label)
  551. (if (elide-cont? label)
  552. (skip-elided-conts (1+ label))
  553. label))
  554. (define (compile-expression label k exp)
  555. (let* ((forwarded-k (forward-label k))
  556. (fallthrough? (= forwarded-k (skip-elided-conts (1+ label)))))
  557. (define (maybe-emit-jump)
  558. (unless fallthrough?
  559. (emit-j asm forwarded-k)))
  560. (match (intmap-ref cps k)
  561. (($ $ktail)
  562. (compile-tail label exp))
  563. (($ $kargs (name) (sym))
  564. (let ((dst (maybe-slot sym)))
  565. (when dst
  566. (compile-value label exp dst)))
  567. (maybe-emit-jump))
  568. (($ $kargs () ())
  569. (compile-effect label exp k)
  570. (maybe-emit-jump))
  571. (($ $kargs names syms)
  572. (compile-values label exp syms)
  573. (maybe-emit-jump))
  574. (($ $kreceive ($ $arity req () rest () #f) kargs)
  575. (compile-trunc label k exp (length req)
  576. (and rest
  577. (match (intmap-ref cps kargs)
  578. (($ $kargs names (_ ... rest)) rest))))
  579. (let* ((kargs (forward-label kargs))
  580. (fallthrough? (and fallthrough?
  581. (= kargs (skip-elided-conts (1+ k))))))
  582. (unless fallthrough?
  583. (emit-j asm kargs)))))))
  584. (define (compile-term label term)
  585. (match term
  586. (($ $continue k src exp)
  587. (when src
  588. (emit-source asm src))
  589. (unless (elide-cont? label)
  590. (compile-expression label k exp)))
  591. (($ $branch kf kt src op param args)
  592. (when src
  593. (emit-source asm src))
  594. (compile-test label (skip-elided-conts (1+ label))
  595. (forward-label kf) (forward-label kt)
  596. op param args))
  597. (($ $prompt k kh src escape? tag)
  598. (when src
  599. (emit-source asm src))
  600. (compile-prompt label (skip-elided-conts k) kh escape? tag))
  601. (($ $throw src op param args)
  602. (when src
  603. (emit-source asm src))
  604. (compile-throw op param args))))
  605. (define (compile-cont label cont)
  606. (match cont
  607. (($ $kfun src meta self tail clause)
  608. (when src
  609. (emit-source asm src))
  610. (emit-begin-program asm label meta))
  611. (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alt)
  612. (let ((first? (match (intmap-ref cps (1- label))
  613. (($ $kfun) #t)
  614. (_ #f)))
  615. (has-closure? (match (intmap-ref cps (intmap-next cps))
  616. (($ $kfun src meta self tail) (->bool self))))
  617. (kw-indices (map (match-lambda
  618. ((key name sym)
  619. (cons key (lookup-slot sym allocation))))
  620. kw)))
  621. (unless first?
  622. (emit-end-arity asm))
  623. (emit-label asm label)
  624. (emit-begin-kw-arity asm has-closure? req opt rest kw-indices
  625. allow-other-keys? frame-size alt)
  626. (when has-closure?
  627. ;; Most arities define a closure binding in slot 0.
  628. (emit-definition asm 'closure 0 'scm))
  629. ;; Usually we just fall through, but it could be the body is
  630. ;; contified into another clause.
  631. (let ((body (forward-label body)))
  632. (unless (= body (skip-elided-conts (1+ label)))
  633. (emit-j asm body)))))
  634. (($ $kargs names vars term)
  635. (emit-label asm label)
  636. (for-each (lambda (name var)
  637. (let ((slot (maybe-slot var)))
  638. (when slot
  639. (let ((repr (lookup-representation var allocation)))
  640. (emit-definition asm name slot repr)))))
  641. names vars)
  642. (compile-term label term))
  643. (($ $kreceive arity kargs)
  644. (emit-label asm label))
  645. (($ $ktail)
  646. (emit-end-arity asm)
  647. (emit-end-program asm))))
  648. (intmap-for-each compile-cont cps)))
  649. (define (emit-bytecode exp env opts)
  650. (let ((asm (make-assembler)))
  651. (intmap-for-each (lambda (kfun body)
  652. (compile-function (intmap-select exp body) asm opts))
  653. (compute-reachable-functions exp 0))
  654. (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
  655. env
  656. env)))
  657. (define (lower-cps exp opts)
  658. ;; FIXME: For now the closure conversion pass relies on $rec instances
  659. ;; being separated into SCCs. We should fix this to not be the case,
  660. ;; and instead move the split-rec pass back to
  661. ;; optimize-higher-order-cps.
  662. (set! exp (split-rec exp))
  663. (set! exp (optimize-higher-order-cps exp opts))
  664. (set! exp (convert-closures exp))
  665. (set! exp (optimize-first-order-cps exp opts))
  666. (set! exp (reify-primitives exp))
  667. (set! exp (add-loop-instrumentation exp))
  668. (renumber exp))
  669. (define (compile-bytecode exp env opts)
  670. (set! exp (lower-cps exp opts))
  671. (emit-bytecode exp env opts))