Remove $void CPS expression type
[bpt/guile.git] / module / language / cps / compile-bytecode.scm
1 ;;; Continuation-passing style (CPS) intermediate language (IL)
2
3 ;; Copyright (C) 2013, 2014, 2015 Free Software Foundation, Inc.
4
5 ;;;; This library is free software; you can redistribute it and/or
6 ;;;; modify it under the terms of the GNU Lesser General Public
7 ;;;; License as published by the Free Software Foundation; either
8 ;;;; version 3 of the License, or (at your option) any later version.
9 ;;;;
10 ;;;; This library is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;;;; Lesser General Public License for more details.
14 ;;;;
15 ;;;; You should have received a copy of the GNU Lesser General Public
16 ;;;; License along with this library; if not, write to the Free Software
17 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 ;;; Commentary:
20 ;;;
21 ;;; Compiling CPS to bytecode. The result is in the bytecode language,
22 ;;; which happens to be an ELF image as a bytecode.
23 ;;;
24 ;;; Code:
25
26 (define-module (language cps compile-bytecode)
27 #:use-module (ice-9 match)
28 #:use-module (srfi srfi-1)
29 #:use-module (language cps)
30 #:use-module (language cps arities)
31 #:use-module (language cps closure-conversion)
32 #:use-module (language cps contification)
33 #:use-module (language cps constructors)
34 #:use-module (language cps cse)
35 #:use-module (language cps dce)
36 #:use-module (language cps dfg)
37 #:use-module (language cps elide-values)
38 #:use-module (language cps primitives)
39 #:use-module (language cps prune-bailouts)
40 #:use-module (language cps prune-top-level-scopes)
41 #:use-module (language cps reify-primitives)
42 #:use-module (language cps renumber)
43 #:use-module (language cps self-references)
44 #:use-module (language cps simplify)
45 #:use-module (language cps slot-allocation)
46 #:use-module (language cps specialize-primcalls)
47 #:use-module (language cps type-fold)
48 #:use-module (system vm assembler)
49 #:export (compile-bytecode))
50
51 ;; TODO: Local var names.
52
53 (define (kw-arg-ref args kw default)
54 (match (memq kw args)
55 ((_ val . _) val)
56 (_ default)))
57
58 (define (optimize exp opts)
59 (define (run-pass exp pass kw default)
60 (if (kw-arg-ref opts kw default)
61 (pass exp)
62 exp))
63
64 ;; The first DCE pass is mainly to eliminate functions that aren't
65 ;; called. The last is mainly to eliminate rest parameters that
66 ;; aren't used, and thus shouldn't be consed.
67
68 (let* ((exp (run-pass exp eliminate-dead-code #:eliminate-dead-code? #t))
69 (exp (run-pass exp prune-top-level-scopes #:prune-top-level-scopes? #t))
70 (exp (run-pass exp simplify #:simplify? #t))
71 (exp (run-pass exp contify #:contify? #t))
72 (exp (run-pass exp inline-constructors #:inline-constructors? #t))
73 (exp (run-pass exp specialize-primcalls #:specialize-primcalls? #t))
74 (exp (run-pass exp elide-values #:elide-values? #t))
75 (exp (run-pass exp prune-bailouts #:prune-bailouts? #t))
76 (exp (run-pass exp eliminate-common-subexpressions #:cse? #t))
77 (exp (run-pass exp type-fold #:type-fold? #t))
78 (exp (run-pass exp resolve-self-references #:resolve-self-references? #t))
79 (exp (run-pass exp eliminate-dead-code #:eliminate-dead-code? #t))
80 (exp (run-pass exp simplify #:simplify? #t)))
81 ;; Passes that are needed:
82 ;;
83 ;; * Abort contification: turning abort primcalls into continuation
84 ;; calls, and eliding prompts if possible.
85 ;;
86 ;; * Loop peeling. Unrolls the first round through a loop if the
87 ;; loop has effects that CSE can work on. Requires effects
88 ;; analysis. When run before CSE, loop peeling is the equivalent
89 ;; of loop-invariant code motion (LICM).
90
91 exp))
92
93 (define (compile-fun f asm)
94 (let* ((dfg (compute-dfg f #:global? #f))
95 (allocation (allocate-slots f dfg)))
96 (define (maybe-slot sym)
97 (lookup-maybe-slot sym allocation))
98
99 (define (slot sym)
100 (lookup-slot sym allocation))
101
102 (define (constant sym)
103 (lookup-constant-value sym allocation))
104
105 (define (maybe-mov dst src)
106 (unless (= dst src)
107 (emit-mov asm dst src)))
108
109 (define (maybe-load-constant slot src)
110 (call-with-values (lambda ()
111 (lookup-maybe-constant-value src allocation))
112 (lambda (has-const? val)
113 (and has-const?
114 (begin
115 (emit-load-constant asm slot val)
116 #t)))))
117
118 (define (compile-entry)
119 (let ((label (dfg-min-label dfg)))
120 (match (lookup-cont label dfg)
121 (($ $kfun src meta self tail clause)
122 (when src
123 (emit-source asm src))
124 (emit-begin-program asm label meta)
125 (compile-clause (1+ label))
126 (emit-end-program asm)))))
127
128 (define (compile-clause label)
129 (match (lookup-cont label dfg)
130 (($ $kclause ($ $arity req opt rest kw allow-other-keys?)
131 body alternate)
132 (let* ((kw-indices (map (match-lambda
133 ((key name sym)
134 (cons key (lookup-slot sym allocation))))
135 kw))
136 (nlocals (lookup-nlocals label allocation)))
137 (emit-label asm label)
138 (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
139 nlocals
140 (match alternate (#f #f) (($ $cont alt) alt)))
141 (let ((next (compile-body (1+ label) nlocals)))
142 (emit-end-arity asm)
143 (match alternate
144 (($ $cont alt)
145 (unless (eq? next alt)
146 (error "unexpected k" alt))
147 (compile-clause next))
148 (#f
149 (unless (= next (+ (dfg-min-label dfg) (dfg-label-count dfg)))
150 (error "unexpected end of clauses")))))))))
151
152 (define (compile-body label nlocals)
153 (let compile-cont ((label label))
154 (if (eq? label (+ (dfg-min-label dfg) (dfg-label-count dfg)))
155 label
156 (match (lookup-cont label dfg)
157 (($ $kclause) label)
158 (($ $kargs names vars term)
159 (emit-label asm label)
160 (for-each (lambda (name var)
161 (let ((slot (maybe-slot var)))
162 (when slot
163 (emit-definition asm name slot))))
164 names vars)
165 (let find-exp ((term term))
166 (match term
167 (($ $letk conts term)
168 (find-exp term))
169 (($ $continue k src exp)
170 (when src
171 (emit-source asm src))
172 (compile-expression label k exp nlocals)
173 (compile-cont (1+ label))))))
174 (_
175 (emit-label asm label)
176 (compile-cont (1+ label)))))))
177
178 (define (compile-expression label k exp nlocals)
179 (let* ((fallthrough? (= k (1+ label))))
180 (define (maybe-emit-jump)
181 (unless fallthrough?
182 (emit-br asm k)))
183 (match (lookup-cont k dfg)
184 (($ $ktail)
185 (compile-tail label exp))
186 (($ $kargs (name) (sym))
187 (let ((dst (maybe-slot sym)))
188 (when dst
189 (compile-value label exp dst nlocals)))
190 (maybe-emit-jump))
191 (($ $kargs () ())
192 (match exp
193 (($ $branch kt exp)
194 (compile-test label exp kt k (1+ label)))
195 (_
196 (compile-effect label exp k nlocals)
197 (maybe-emit-jump))))
198 (($ $kargs names syms)
199 (compile-values label exp syms)
200 (maybe-emit-jump))
201 (($ $kreceive ($ $arity req () rest () #f) kargs)
202 (compile-trunc label k exp (length req)
203 (and rest
204 (match (lookup-cont kargs dfg)
205 (($ $kargs names (_ ... rest)) rest)))
206 nlocals)
207 (unless (and fallthrough? (= kargs (1+ k)))
208 (emit-br asm kargs))))))
209
210 (define (compile-tail label exp)
211 ;; There are only three kinds of expressions in tail position:
212 ;; tail calls, multiple-value returns, and single-value returns.
213 (match exp
214 (($ $call proc args)
215 (for-each (match-lambda
216 ((src . dst) (emit-mov asm dst src)))
217 (lookup-parallel-moves label allocation))
218 (let ((tail-slots (cdr (iota (1+ (length args))))))
219 (for-each maybe-load-constant tail-slots args))
220 (emit-tail-call asm (1+ (length args))))
221 (($ $callk k proc args)
222 (for-each (match-lambda
223 ((src . dst) (emit-mov asm dst src)))
224 (lookup-parallel-moves label allocation))
225 (let ((tail-slots (cdr (iota (1+ (length args))))))
226 (for-each maybe-load-constant tail-slots args))
227 (emit-tail-call-label asm (1+ (length args)) k))
228 (($ $values ())
229 (emit-reset-frame asm 1)
230 (emit-return-values asm))
231 (($ $values (arg))
232 (if (maybe-slot arg)
233 (emit-return asm (slot arg))
234 (begin
235 (emit-load-constant asm 1 (constant arg))
236 (emit-return asm 1))))
237 (($ $values args)
238 (for-each (match-lambda
239 ((src . dst) (emit-mov asm dst src)))
240 (lookup-parallel-moves label allocation))
241 (let ((tail-slots (cdr (iota (1+ (length args))))))
242 (for-each maybe-load-constant tail-slots args))
243 (emit-reset-frame asm (1+ (length args)))
244 (emit-return-values asm))
245 (($ $primcall 'return (arg))
246 (emit-return asm (slot arg)))))
247
248 (define (compile-value label exp dst nlocals)
249 (match exp
250 (($ $values (arg))
251 (or (maybe-load-constant dst arg)
252 (maybe-mov dst (slot arg))))
253 (($ $const exp)
254 (emit-load-constant asm dst exp))
255 (($ $closure k 0)
256 (emit-load-static-procedure asm dst k))
257 (($ $closure k nfree)
258 (emit-make-closure asm dst k nfree))
259 (($ $primcall 'current-module)
260 (emit-current-module asm dst))
261 (($ $primcall 'cached-toplevel-box (scope name bound?))
262 (emit-cached-toplevel-box asm dst (constant scope) (constant name)
263 (constant bound?)))
264 (($ $primcall 'cached-module-box (mod name public? bound?))
265 (emit-cached-module-box asm dst (constant mod) (constant name)
266 (constant public?) (constant bound?)))
267 (($ $primcall 'resolve (name bound?))
268 (emit-resolve asm dst (constant bound?) (slot name)))
269 (($ $primcall 'free-ref (closure idx))
270 (emit-free-ref asm dst (slot closure) (constant idx)))
271 (($ $primcall 'vector-ref (vector index))
272 (emit-vector-ref asm dst (slot vector) (slot index)))
273 (($ $primcall 'make-vector (length init))
274 (emit-make-vector asm dst (slot length) (slot init)))
275 (($ $primcall 'make-vector/immediate (length init))
276 (emit-make-vector/immediate asm dst (constant length) (slot init)))
277 (($ $primcall 'vector-ref/immediate (vector index))
278 (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
279 (($ $primcall 'allocate-struct (vtable nfields))
280 (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
281 (($ $primcall 'allocate-struct/immediate (vtable nfields))
282 (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
283 (($ $primcall 'struct-ref (struct n))
284 (emit-struct-ref asm dst (slot struct) (slot n)))
285 (($ $primcall 'struct-ref/immediate (struct n))
286 (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
287 (($ $primcall 'builtin-ref (name))
288 (emit-builtin-ref asm dst (constant name)))
289 (($ $primcall 'bv-u8-ref (bv idx))
290 (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
291 (($ $primcall 'bv-s8-ref (bv idx))
292 (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
293 (($ $primcall 'bv-u16-ref (bv idx))
294 (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
295 (($ $primcall 'bv-s16-ref (bv idx))
296 (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
297 (($ $primcall 'bv-u32-ref (bv idx val))
298 (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
299 (($ $primcall 'bv-s32-ref (bv idx val))
300 (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
301 (($ $primcall 'bv-u64-ref (bv idx val))
302 (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
303 (($ $primcall 'bv-s64-ref (bv idx val))
304 (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
305 (($ $primcall 'bv-f32-ref (bv idx val))
306 (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
307 (($ $primcall 'bv-f64-ref (bv idx val))
308 (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
309 (($ $primcall name args)
310 ;; FIXME: Inline all the cases.
311 (let ((inst (prim-instruction name)))
312 (emit-text asm `((,inst ,dst ,@(map slot args))))))))
313
314 (define (compile-effect label exp k nlocals)
315 (match exp
316 (($ $values ()) #f)
317 (($ $prompt escape? tag handler)
318 (match (lookup-cont handler dfg)
319 (($ $kreceive ($ $arity req () rest () #f) khandler-body)
320 (let ((receive-args (gensym "handler"))
321 (nreq (length req))
322 (proc-slot (lookup-call-proc-slot handler allocation)))
323 (emit-prompt asm (slot tag) escape? proc-slot receive-args)
324 (emit-br asm k)
325 (emit-label asm receive-args)
326 (unless (and rest (zero? nreq))
327 (emit-receive-values asm proc-slot (->bool rest) nreq))
328 (when (and rest
329 (match (lookup-cont khandler-body dfg)
330 (($ $kargs names (_ ... rest))
331 (maybe-slot rest))))
332 (emit-bind-rest asm (+ proc-slot 1 nreq)))
333 (for-each (match-lambda
334 ((src . dst) (emit-mov asm dst src)))
335 (lookup-parallel-moves handler allocation))
336 (emit-reset-frame asm nlocals)
337 (emit-br asm khandler-body)))))
338 (($ $primcall 'cache-current-module! (sym scope))
339 (emit-cache-current-module! asm (slot sym) (constant scope)))
340 (($ $primcall 'free-set! (closure idx value))
341 (emit-free-set! asm (slot closure) (slot value) (constant idx)))
342 (($ $primcall 'box-set! (box value))
343 (emit-box-set! asm (slot box) (slot value)))
344 (($ $primcall 'struct-set! (struct index value))
345 (emit-struct-set! asm (slot struct) (slot index) (slot value)))
346 (($ $primcall 'struct-set!/immediate (struct index value))
347 (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
348 (($ $primcall 'vector-set! (vector index value))
349 (emit-vector-set! asm (slot vector) (slot index) (slot value)))
350 (($ $primcall 'vector-set!/immediate (vector index value))
351 (emit-vector-set!/immediate asm (slot vector) (constant index)
352 (slot value)))
353 (($ $primcall 'set-car! (pair value))
354 (emit-set-car! asm (slot pair) (slot value)))
355 (($ $primcall 'set-cdr! (pair value))
356 (emit-set-cdr! asm (slot pair) (slot value)))
357 (($ $primcall 'define! (sym value))
358 (emit-define! asm (slot sym) (slot value)))
359 (($ $primcall 'push-fluid (fluid val))
360 (emit-push-fluid asm (slot fluid) (slot val)))
361 (($ $primcall 'pop-fluid ())
362 (emit-pop-fluid asm))
363 (($ $primcall 'wind (winder unwinder))
364 (emit-wind asm (slot winder) (slot unwinder)))
365 (($ $primcall 'bv-u8-set! (bv idx val))
366 (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
367 (($ $primcall 'bv-s8-set! (bv idx val))
368 (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
369 (($ $primcall 'bv-u16-set! (bv idx val))
370 (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
371 (($ $primcall 'bv-s16-set! (bv idx val))
372 (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
373 (($ $primcall 'bv-u32-set! (bv idx val))
374 (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
375 (($ $primcall 'bv-s32-set! (bv idx val))
376 (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
377 (($ $primcall 'bv-u64-set! (bv idx val))
378 (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
379 (($ $primcall 'bv-s64-set! (bv idx val))
380 (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
381 (($ $primcall 'bv-f32-set! (bv idx val))
382 (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
383 (($ $primcall 'bv-f64-set! (bv idx val))
384 (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
385 (($ $primcall 'unwind ())
386 (emit-unwind asm))))
387
388 (define (compile-values label exp syms)
389 (match exp
390 (($ $values args)
391 (for-each (match-lambda
392 ((src . dst) (emit-mov asm dst src)))
393 (lookup-parallel-moves label allocation))
394 (for-each maybe-load-constant (map slot syms) args))))
395
396 (define (compile-test label exp kt kf next-label)
397 (define (unary op sym)
398 (cond
399 ((eq? kt next-label)
400 (op asm (slot sym) #t kf))
401 (else
402 (op asm (slot sym) #f kt)
403 (unless (eq? kf next-label)
404 (emit-br asm kf)))))
405 (define (binary op a b)
406 (cond
407 ((eq? kt next-label)
408 (op asm (slot a) (slot b) #t kf))
409 (else
410 (op asm (slot a) (slot b) #f kt)
411 (unless (eq? kf next-label)
412 (emit-br asm kf)))))
413 (match exp
414 (($ $values (sym))
415 (call-with-values (lambda ()
416 (lookup-maybe-constant-value sym allocation))
417 (lambda (has-const? val)
418 (if has-const?
419 (if val
420 (unless (eq? kt next-label)
421 (emit-br asm kt))
422 (unless (eq? kf next-label)
423 (emit-br asm kf)))
424 (unary emit-br-if-true sym)))))
425 (($ $primcall 'null? (a)) (unary emit-br-if-null a))
426 (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
427 (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
428 (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
429 (($ $primcall 'char? (a)) (unary emit-br-if-char a))
430 (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
431 (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
432 (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
433 (($ $primcall 'string? (a)) (unary emit-br-if-string a))
434 (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
435 (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
436 (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
437 ;; Add more TC7 tests here. Keep in sync with
438 ;; *branching-primcall-arities* in (language cps primitives) and
439 ;; the set of macro-instructions in assembly.scm.
440 (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
441 (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
442 (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
443 (($ $primcall '< (a b)) (binary emit-br-if-< a b))
444 (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
445 (($ $primcall '= (a b)) (binary emit-br-if-= a b))
446 (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
447 (($ $primcall '> (a b)) (binary emit-br-if-< b a))
448 (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
449
450 (define (compile-trunc label k exp nreq rest-var nlocals)
451 (define (do-call proc args emit-call)
452 (let* ((proc-slot (lookup-call-proc-slot label allocation))
453 (nargs (1+ (length args)))
454 (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
455 (for-each (match-lambda
456 ((src . dst) (emit-mov asm dst src)))
457 (lookup-parallel-moves label allocation))
458 (for-each maybe-load-constant arg-slots (cons proc args))
459 (emit-call asm proc-slot nargs)
460 (emit-dead-slot-map asm proc-slot
461 (lookup-dead-slot-map label allocation))
462 (cond
463 ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
464 (match (lookup-parallel-moves k allocation)
465 ((((? (lambda (src) (= src (1+ proc-slot))) src)
466 . dst)) dst)
467 (_ #f)))
468 ;; The usual case: one required live return value, ignoring
469 ;; any additional values.
470 => (lambda (dst)
471 (emit-receive asm dst proc-slot nlocals)))
472 (else
473 (unless (and (zero? nreq) rest-var)
474 (emit-receive-values asm proc-slot (->bool rest-var) nreq))
475 (when (and rest-var (maybe-slot rest-var))
476 (emit-bind-rest asm (+ proc-slot 1 nreq)))
477 (for-each (match-lambda
478 ((src . dst) (emit-mov asm dst src)))
479 (lookup-parallel-moves k allocation))
480 (emit-reset-frame asm nlocals)))))
481 (match exp
482 (($ $call proc args)
483 (do-call proc args
484 (lambda (asm proc-slot nargs)
485 (emit-call asm proc-slot nargs))))
486 (($ $callk k proc args)
487 (do-call proc args
488 (lambda (asm proc-slot nargs)
489 (emit-call-label asm proc-slot nargs k))))))
490
491 (match f
492 (($ $cont k ($ $kfun src meta self tail clause))
493 (compile-entry)))))
494
495 (define (compile-bytecode exp env opts)
496 (let* ((exp (fix-arities exp))
497 (exp (optimize exp opts))
498 (exp (convert-closures exp))
499 ;; first-order optimization should go here
500 (exp (reify-primitives exp))
501 (exp (renumber exp))
502 (asm (make-assembler)))
503 (match exp
504 (($ $program funs)
505 (for-each (lambda (fun) (compile-fun fun asm))
506 funs)))
507 (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
508 env
509 env)))