segment.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Martin Gasbichler, Mike Sperber
  3. ; The byte code compiler's assembly phase.
  4. (define make-segment cons)
  5. (define segment-size car);number of bytes that will be taken in the code vector
  6. (define segment-emitter cdr)
  7. (define (segment->cv segment frame)
  8. (let* ((big-stack? (check-stack-use (frame-size frame)))
  9. (cv (make-code-vector (+ (segment-size segment)
  10. (if big-stack? 3 0))
  11. 0))
  12. (astate (make-astate cv))
  13. (debug-data (frame-debug-data frame)))
  14. (if (> (segment-size segment) 65535)
  15. (assertion-violation 'segment->cv
  16. "VM limit exceeded: segment too large" (segment-size segment)))
  17. (emit-segment! astate segment)
  18. (if big-stack?
  19. (add-big-stack-protocol! cv (frame-size frame)))
  20. (set-debug-data-env-maps! debug-data
  21. (astate-env-maps astate))
  22. (set-debug-data-jump-back-dests! debug-data
  23. (astate-jump-back-dests astate))
  24. (make-immutable! cv)
  25. (values cv
  26. (debug-data->info debug-data)
  27. (reverse (frame-literals frame)))))
  28. (define (segment->template segment frame)
  29. (call-with-values
  30. (lambda () (segment->cv segment frame))
  31. segment-data->template))
  32. (define (segment-data->template cv debug-data literals)
  33. (let ((template (make-template (+ template-overhead (length literals)) 0)))
  34. (set-template-code! template cv)
  35. (set-template-byte-code! template cv)
  36. (set-template-info! template debug-data)
  37. (set-template-package-id! template (fluid $package-key))
  38. (do ((lits literals (cdr lits))
  39. (i template-overhead (+ i 1)))
  40. ((null? lits) template)
  41. (template-set! template i (car lits)))
  42. template))
  43. (define $package-key (make-fluid #f))
  44. (define (with-package-key package-key thunk)
  45. (let-fluid $package-key package-key thunk))
  46. ; If CV needs more than the default allotment of stack space we add a new
  47. ; protocol onto the front.
  48. (define (check-stack-use frame-size)
  49. (cond ((<= frame-size default-stack-space)
  50. #f)
  51. ((<= frame-size available-stack-space)
  52. #t)
  53. (else
  54. (assertion-violation 'check-stack-use
  55. "VM limit exceeded: procedure requires too much stack space"
  56. frame-size))))
  57. ; We put the length and the original protocol at the end of the code vector
  58. ; so that the original protocol's data doesn't have to be moved (which would
  59. ; complicate the already-complicated VM code for protocol dispatch).
  60. (define (add-big-stack-protocol! cv frame-size)
  61. (let ((length (code-vector-length cv)))
  62. (code-vector-set! cv (- length 3) (code-vector-ref cv 1))
  63. (code-vector-set! cv (- length 2) (high-byte frame-size))
  64. (code-vector-set! cv (- length 1) (low-byte frame-size))
  65. (code-vector-set! cv 1 big-stack-protocol)))
  66. ; "astate" is short for "assembly state"
  67. (define-record-type assembly-state :assembly-state
  68. (make-assembly-state cv pc env-maps jump-back-dests)
  69. (cv astate-code-vector)
  70. (pc astate-pc set-astate-pc!)
  71. (env-maps astate-env-maps set-astate-env-maps!)
  72. (jump-back-dests astate-jump-back-dests set-astate-jump-back-dests!))
  73. (define (make-astate cv)
  74. (make-assembly-state cv 0 '() '()))
  75. (define (emit-byte! a byte)
  76. (code-vector-set! (astate-code-vector a) (astate-pc a) byte)
  77. (set-astate-pc! a (+ (astate-pc a) 1)))
  78. (define (emit-segment! astate segment)
  79. ((segment-emitter segment) astate))
  80. ; Segment constructors
  81. (define empty-segment
  82. (make-segment 0 (lambda (astate) #f)))
  83. (define (instruction opcode . operands)
  84. (make-segment (+ 1 (length operands))
  85. (lambda (astate)
  86. ; (format #t "[emit ~D(~D) -> ~S ~S]~%"
  87. ; (astate-pc astate)
  88. ; (code-vector-length (astate-code-vector astate))
  89. ; (enumerand->name opcode op)
  90. ; (cons opcode operands))
  91. (emit-byte! astate opcode)
  92. (for-each (lambda (operand)
  93. (emit-byte! astate operand))
  94. operands))))
  95. (define (sequentially . segments)
  96. (if (not (car segments))
  97. (assertion-violation 'sequentially "bad call to SEQUENTIALLY"))
  98. ;; (reduce sequentially-2 empty-segment segments)
  99. ;;+++ this sped the entire compilation process up by several percent
  100. (cond ((null? segments)
  101. empty-segment)
  102. ((null? (cdr segments))
  103. (car segments))
  104. ((null? (cddr segments))
  105. (sequentially-2 (car segments) (cadr segments)))
  106. (else
  107. (make-segment (let loop ((segs segments) (s 0))
  108. (if (null? segs)
  109. s
  110. (loop (cdr segs) (+ s (segment-size (car segs))))))
  111. (lambda (astate)
  112. (let loop ((segs segments))
  113. (if (not (null? segs))
  114. (begin (emit-segment! astate (car segs))
  115. (loop (cdr segs))))))))))
  116. (define (sequentially-2 seg1 seg2)
  117. (cond ((eq? seg1 empty-segment) seg2) ;+++ speed up the compiler a tad
  118. ((eq? seg2 empty-segment) seg1) ;+++
  119. (else
  120. (make-segment (+ (segment-size seg1)
  121. (segment-size seg2))
  122. (lambda (astate)
  123. (emit-segment! astate seg1)
  124. (emit-segment! astate seg2)))))) ;tail call
  125. (define (continuation-data live-offsets depth template)
  126. (let* ((gc-mask
  127. (if live-offsets
  128. (let ((provisional
  129. (bits->bytes (live-mask live-offsets depth))))
  130. (if (null? provisional)
  131. '(0)
  132. provisional))
  133. '()))
  134. (gc-mask-size (length gc-mask))
  135. (size (+ 10 ; header (3)
  136. ; gc-mask, low bytes first (see below)
  137. ; + template (2)
  138. ; + offset (2)
  139. ; + gc-mask size (1)
  140. ; + depth (2)
  141. gc-mask-size)))
  142. (make-segment size
  143. (lambda (astate)
  144. (let ((offset (+ (astate-pc astate) size))
  145. (template (or template #xffff)))
  146. (emit-byte! astate (enum op cont-data))
  147. (emit-byte! astate (high-byte size))
  148. (emit-byte! astate (low-byte size))
  149. (for-each (lambda (byte)
  150. (emit-byte! astate byte))
  151. gc-mask)
  152. (emit-byte! astate (high-byte template))
  153. (emit-byte! astate (low-byte template))
  154. (emit-byte! astate (high-byte offset))
  155. (emit-byte! astate (low-byte offset))
  156. (emit-byte! astate gc-mask-size)
  157. (emit-byte! astate (high-byte depth))
  158. (emit-byte! astate (low-byte depth)))))))
  159. (define (live-mask offsets depth)
  160. (do ((offsets offsets (cdr offsets))
  161. (mask 0
  162. (bitwise-ior mask
  163. (arithmetic-shift 1 (car offsets)))))
  164. ((null? offsets)
  165. mask)))
  166. ; low bytes first
  167. (define (bits->bytes n)
  168. (do ((n n (arithmetic-shift n -8))
  169. (b '() (cons (bitwise-and n #xFF) b)))
  170. ((= 0 n)
  171. (reverse b))))
  172. ;;;;;;;;;;;;;;;;;;;;
  173. ; Emitting the PROTOCOL pseudo instruction
  174. (define (make-push-byte need-template? need-env? need-closure?)
  175. (bitwise-ior (if need-template?
  176. #b001
  177. #b000)
  178. (if need-env?
  179. #b010
  180. #b000)
  181. (if need-closure?
  182. #b100
  183. #b000)))
  184. (define (lambda-protocol nargs need-template? need-env? need-closure?)
  185. (let ((push-byte (make-push-byte need-template? need-env? need-closure?)))
  186. (cond ((<= nargs maximum-stack-args)
  187. (instruction (enum op protocol) nargs push-byte))
  188. ((<= nargs available-stack-space)
  189. (instruction (enum op protocol)
  190. two-byte-nargs-protocol
  191. (high-byte nargs)
  192. (low-byte nargs)
  193. push-byte))
  194. (else
  195. (assertion-violation 'lambda-protocol
  196. "compiler bug: too many formals" nargs)))))
  197. (define (nary-lambda-protocol nargs need-template? need-env? need-closure?)
  198. (let ((push-byte (make-push-byte need-template? need-env? need-closure?)))
  199. (cond ((<= nargs available-stack-space)
  200. (instruction (enum op protocol)
  201. two-byte-nargs+list-protocol
  202. (high-byte nargs)
  203. (low-byte nargs)
  204. push-byte))
  205. (else
  206. (assertion-violation 'nary-lambda-protocol
  207. "compiler bug: too many formals" nargs)))))
  208. (define (nary-primitive-protocol min-nargs)
  209. (instruction (enum op protocol) args+nargs-protocol min-nargs #b00))
  210. ; Building primitives that use the computed-goto provided by the
  211. ; protocol dispatcher.
  212. (define dispatch-protocol-size
  213. (segment-size (instruction (enum op protocol) nary-dispatch-protocol
  214. 0 ; 3+
  215. 0 ; 0
  216. 0 ; 1
  217. 0 ; 2
  218. 0))) ; env/template
  219. ; For a silly reason involving the way the call-setup code in the VM is
  220. ; organized we have to the THREE-PLUS-ARGS offset and code come before
  221. ; the others.
  222. (define (make-dispatch-protocol zero-args one-arg two-args three-plus-args)
  223. (let ((segments (list three-plus-args zero-args one-arg two-args)))
  224. (let loop ((to-do segments)
  225. (offset dispatch-protocol-size)
  226. (offsets '()))
  227. (if (null? to-do)
  228. (apply sequentially
  229. (apply instruction
  230. (enum op protocol)
  231. nary-dispatch-protocol
  232. (reverse (cons #b00 offsets))) ; no env, no template
  233. segments)
  234. (loop (cdr to-do)
  235. (+ offset (segment-size (car to-do)))
  236. (cons (if (empty-segment? (car to-do))
  237. 0
  238. offset)
  239. offsets))))))
  240. (define (continuation-protocol n-args n-ary?)
  241. (cond ((and n-ary?
  242. (zero? n-args))
  243. (instruction (enum op protocol) ignore-values-protocol))
  244. ((not n-ary?)
  245. (instruction (enum op protocol) n-args))
  246. (else
  247. (let ((n-args-min (- n-args 1)))
  248. (instruction (enum op protocol)
  249. two-byte-nargs+list-protocol
  250. (high-byte n-args-min)
  251. (low-byte n-args-min))))))
  252. (define (cwv-continuation-protocol maybe-label)
  253. (if maybe-label
  254. (optional-label-reference
  255. (instruction (enum op protocol)
  256. call-with-values-protocol)
  257. maybe-label
  258. empty-segment)
  259. (instruction (enum op protocol)
  260. call-with-values-protocol
  261. 0
  262. 0)))
  263. ; Labels. Each label maintains a list of pairs (location . origin).
  264. ; Location is the index of the first of two bytes that will hold the jump
  265. ; target offset, and the offset stored will be (- jump-target origin).
  266. ;
  267. ; The car of a forward label is #F, the car of a backward label is the
  268. ; label's PC.
  269. (define-record-type label :label
  270. (really-make-label pc mappings)
  271. label?
  272. (pc label-pc set-label-pc!)
  273. (mappings label-mappings set-label-mappings!))
  274. (define (make-label) (really-make-label #f '()))
  275. (define (label-reference before label after)
  276. (let ((segment (sequentially before
  277. (instruction 0 0)
  278. after)))
  279. (make-segment (segment-size segment)
  280. (lambda (astate)
  281. (let* ((origin (astate-pc astate))
  282. (location (+ origin (segment-size before))))
  283. (emit-segment! astate segment)
  284. (cond
  285. ((label-pc label)
  286. ;; backward label
  287. => (lambda (label-pc)
  288. (insert-backward-label! astate
  289. location
  290. label-pc
  291. (- label-pc origin))))
  292. (else
  293. ;; forward label
  294. (set-label-mappings! label
  295. (cons (cons location origin)
  296. (label-mappings label))))))))))
  297. (define (jump-instruction label)
  298. (make-segment 3
  299. (lambda (astate)
  300. (let* ((origin (astate-pc astate))
  301. (label-location (+ origin 1)))
  302. (cond
  303. ((label-pc label)
  304. => (lambda (label-pc)
  305. ;; backward label
  306. (emit-byte! astate (enum op jump-back))
  307. (set-astate-pc! astate (+ (astate-pc astate) 2))
  308. (insert-backward-label! astate
  309. label-location
  310. label-pc
  311. (- origin label-pc))))
  312. (else
  313. ;; forward label
  314. (begin
  315. (emit-byte! astate (enum op jump))
  316. (set-astate-pc! astate (+ (astate-pc astate) 2))
  317. (set-label-mappings! label
  318. (cons (cons label-location origin)
  319. (label-mappings label))))))))))
  320. (define (instruction-using-label opcode label . rest)
  321. (label-reference (instruction opcode)
  322. label
  323. (bytes->segment rest)))
  324. (define (optional-label-reference before maybe-label after)
  325. (if maybe-label
  326. (label-reference before maybe-label after)
  327. (sequentially before
  328. (instruction 0 0)
  329. after)))
  330. (define (using-optional-label opcode maybe-label . rest)
  331. (optional-label-reference (instruction opcode)
  332. maybe-label
  333. (bytes->segment rest)))
  334. (define (bytes->segment bytes)
  335. (make-segment (length bytes)
  336. (lambda (astate)
  337. (for-each (lambda (operand)
  338. (emit-byte! astate operand))
  339. bytes))))
  340. ; computed-goto
  341. ; # of labels
  342. ; label0
  343. ; label1
  344. ; ...
  345. (define computed-goto-label-size 2)
  346. (define (computed-goto-instruction labels)
  347. (let* ((count (length labels))
  348. (segment (instruction (enum op computed-goto) count)))
  349. (make-segment (+ (segment-size segment)
  350. (* count computed-goto-label-size))
  351. (lambda (astate)
  352. (let ((base-address (astate-pc astate)))
  353. (emit-segment! astate segment)
  354. (set-astate-pc! astate
  355. (+ (astate-pc astate)
  356. (* count computed-goto-label-size)))
  357. (do ((location (+ base-address 2)
  358. (+ location computed-goto-label-size))
  359. (labels labels (cdr labels)))
  360. ((null? labels))
  361. (let ((label (car labels)))
  362. (if (label-pc label)
  363. (warning 'computed-goto-instruction "backward jumps not supported")
  364. (set-label-mappings! label
  365. (cons (cons location base-address)
  366. (label-mappings label)))))))))))
  367. ; stack-shuffle! <count> <from> <to> ...
  368. ; where <from> and <to> are stack indexes
  369. ; pushes, copies <from> to <to>, pops
  370. ; A simple swap between offsets 6 and 9 is one instruction taking up eight bytes:
  371. ; stack-shuffle! 3 7 0 10 7 0 10
  372. ; Takes list of (<from> . <to>) pairs.
  373. (define (stack-shuffle-instruction moves)
  374. (let ((n-moves (length moves))
  375. (flattened (flatten-moves moves)))
  376. (if (or (>= n-moves byte-limit)
  377. (any (lambda (index)
  378. (>= index byte-limit))
  379. flattened))
  380. (apply instruction
  381. (enum op big-stack-shuffle!)
  382. (high-byte n-moves)
  383. (low-byte n-moves)
  384. (apply append
  385. (map (lambda (arg)
  386. (list (high-byte arg) (low-byte arg)))
  387. flattened)))
  388. (apply instruction
  389. (enum op stack-shuffle!)
  390. n-moves
  391. flattened))))
  392. (define (flatten-moves moves)
  393. (let loop ((moves moves)
  394. (args '()))
  395. (if (null? moves)
  396. (reverse args)
  397. (loop (cdr moves)
  398. (cons (cdar moves)
  399. (cons (caar moves)
  400. args))))))
  401. ; LABEL is the label for SEGMENT. The current PC is used as the value of LABEL.
  402. (define (attach-label label segment)
  403. (make-segment
  404. (segment-size segment)
  405. (lambda (astate)
  406. (let ((pc (astate-pc astate))
  407. (cv (astate-code-vector astate)))
  408. (for-each (lambda (instr+origin)
  409. (insert-label! cv
  410. (car instr+origin)
  411. (- pc (cdr instr+origin))))
  412. (label-mappings label))
  413. (set-label-pc! label pc)
  414. (emit-segment! astate segment)))))
  415. (define (insert-label! cv location offset)
  416. (code-vector-set2! cv location offset))
  417. (define (insert-backward-label! astate location label-pc offset)
  418. (let ((cv (astate-code-vector astate)))
  419. (set-astate-jump-back-dests! astate
  420. (cons label-pc
  421. (astate-jump-back-dests astate)))
  422. (insert-label! cv location offset)))
  423. (define (code-vector-set2! cv i value)
  424. (code-vector-set! cv i (high-byte value))
  425. (code-vector-set! cv (+ i 1) (low-byte value)))
  426. (define (high-byte n)
  427. (quotient n byte-limit))
  428. (define (low-byte n)
  429. (remainder n byte-limit))
  430. ; Keep track of source code at continuations.
  431. (define (note-source-code info segment frame)
  432. (make-segment (segment-size segment)
  433. (lambda (astate)
  434. (let ((dd (frame-debug-data frame)))
  435. (set-debug-data-source!
  436. dd
  437. (cons (cons (astate-pc astate) info)
  438. (debug-data-source dd))))
  439. (emit-segment! astate segment))))
  440. ; Keep track of variable names from lexical environments.
  441. ; Each environment map has the form
  442. ; #(pc-before pc-after (var ...) (env-map ...))
  443. ;
  444. ; It's a bit more complex now. Variables are found in the frame itself and
  445. ; in vectors within the frame.
  446. ; #(pc-before pc-after offset names more)
  447. ; We need a way to distinguish between names in the frame and names in vectors.
  448. ; Put the vector ones in lists.
  449. ; (lambda (x y)
  450. ; (lambda (a b)
  451. ; ...))
  452. ; -> (0 <last-pc> 0 (a b (x y)) . more)
  453. ; The (X Y) are in the free-variable vector.
  454. ;
  455. ; Could also add PC's that correspond to calls to mark the values with
  456. ; the source that they were returned from.
  457. (define (note-environment vars offset segment)
  458. (if (keep-environment-maps?)
  459. (make-segment
  460. (segment-size segment)
  461. (lambda (astate)
  462. (let* ((pc-before (astate-pc astate))
  463. (old (astate-env-maps astate)))
  464. (set-astate-env-maps! astate '())
  465. (emit-segment! astate segment)
  466. (let ((new (astate-env-maps astate)))
  467. (set-astate-env-maps! astate
  468. (cons (vector pc-before
  469. (astate-pc astate)
  470. offset
  471. (list->vector vars)
  472. new)
  473. old))))))
  474. segment))
  475. ; --------------------
  476. ; Utilities
  477. (define (empty-segment? segment)
  478. (= (segment-size segment)
  479. 0))