1 ;;; Continuation-passing style (CPS) intermediate language (IL)
3 ;; Copyright (C) 2013 Free Software Foundation, Inc.
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.
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.
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
21 ;;; Compiling CPS to RTL. The result is in the RTL language, which
22 ;;; happens to be an ELF image as a bytecode.
26 (define-module (language cps compile-rtl)
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 dfg)
34 #:use-module (language cps primitives)
35 #:use-module (language cps reify-primitives)
36 #:use-module (language cps slot-allocation)
37 #:use-module (system vm assembler)
38 #:export (compile-rtl))
40 ;; TODO: Source info, local var names. Needs work in the linker and the
43 (define (kw-arg-ref args kw default)
48 (define (optimize exp opts)
49 (define (run-pass exp pass kw default)
50 (if (kw-arg-ref opts kw default)
54 ;; Calls to source-to-source optimization passes go here.
55 (let* ((exp (run-pass exp contify #:contify? #t)))
56 ;; Passes that are needed:
58 ;; * Abort contification: turning abort primcalls into continuation
59 ;; calls, and eliding prompts if possible.
61 ;; * Common subexpression elimination. Desperately needed. Requires
64 ;; * Loop peeling. Unrolls the first round through a loop if the
65 ;; loop has effects that CSE can work on. Requires effects
66 ;; analysis. When run before CSE, loop peeling is the equivalent
67 ;; of loop-invariant code motion (LICM).
69 ;; * Generic simplification pass, to be run as needed. Used to
70 ;; "clean up", both on the original raw input and after specific
71 ;; optimization passes.
75 (define (visit-funs proc exp)
78 (visit-funs proc exp))
80 (($ $fun meta free body)
82 (visit-funs proc body))
85 (visit-funs proc body)
86 (for-each (lambda (cont) (visit-funs proc cont)) conts))
88 (($ $cont sym src ($ $kargs names syms body))
89 (visit-funs proc body))
91 (($ $cont sym src ($ $kclause arity body))
92 (visit-funs proc body))
94 (($ $cont sym src ($ $kentry self tail clauses))
95 (for-each (lambda (clause) (visit-funs proc clause)) clauses))
99 (define (emit-rtl-sequence asm exp allocation nlocals cont-table)
101 (lookup-slot sym allocation))
103 (define (constant sym)
104 (lookup-constant-value sym allocation))
106 (define (emit-rtl label k exp next-label)
107 (define (maybe-mov dst src)
109 (emit-mov asm dst src)))
111 (define (maybe-jump label)
112 (unless (eq? label next-label)
113 (emit-br asm label)))
115 (define (maybe-load-constant slot src)
116 (call-with-values (lambda ()
117 (lookup-maybe-constant-value src allocation))
118 (lambda (has-const? val)
121 (emit-load-constant asm slot val)
125 ;; There are only three kinds of expressions in tail position:
126 ;; tail calls, multiple-value returns, and single-value returns.
129 (for-each (match-lambda
130 ((src . dst) (emit-mov asm dst src)))
131 (lookup-parallel-moves label allocation))
132 (let ((tail-slots (cdr (iota (1+ (length args))))))
133 (for-each maybe-load-constant tail-slots args))
134 (emit-tail-call asm (1+ (length args))))
136 (let ((tail-slots (cdr (iota (1+ (length args))))))
137 (for-each (match-lambda
138 ((src . dst) (emit-mov asm dst src)))
139 (lookup-parallel-moves label allocation))
140 (for-each maybe-load-constant tail-slots args))
141 (emit-reset-frame asm (1+ (length args)))
142 (emit-return-values asm))
143 (($ $primcall 'return (arg))
144 (emit-return asm (slot arg)))))
146 (define (emit-val sym)
147 (let ((dst (slot sym)))
150 (maybe-mov dst (slot sym)))
153 (emit-load-constant asm dst *unspecified*)))
156 (emit-load-constant asm dst exp)))
157 (($ $fun meta () ($ $cont k))
158 (emit-load-static-procedure asm dst k))
159 (($ $fun meta free ($ $cont k))
160 (emit-make-closure asm dst k (length free)))
162 (let ((proc-slot (lookup-call-proc-slot label allocation))
163 (nargs (length args)))
164 (or (maybe-load-constant proc-slot proc)
165 (maybe-mov proc-slot (slot proc)))
166 (let lp ((n (1+ proc-slot)) (args args))
169 (emit-call asm proc-slot (+ nargs 1))
170 (emit-receive asm dst proc-slot nlocals))
172 (or (maybe-load-constant n arg)
173 (maybe-mov n (slot arg)))
174 (lp (1+ n) args))))))
175 (($ $primcall 'current-module)
176 (emit-current-module asm dst))
177 (($ $primcall 'cached-toplevel-box (scope name bound?))
178 (emit-cached-toplevel-box asm dst (constant scope) (constant name)
180 (($ $primcall 'cached-module-box (mod name public? bound?))
181 (emit-cached-module-box asm dst (constant mod) (constant name)
182 (constant public?) (constant bound?)))
183 (($ $primcall 'resolve (name bound?))
184 (emit-resolve asm dst (constant bound?) (slot name)))
185 (($ $primcall 'free-ref (closure idx))
186 (emit-free-ref asm dst (slot closure) (constant idx)))
187 (($ $primcall name args)
188 ;; FIXME: Inline all the cases.
189 (let ((inst (prim-rtl-instruction name)))
190 (emit-text asm `((,inst ,dst ,@(map slot args))))))
192 (or (maybe-load-constant dst arg)
193 (maybe-mov dst (slot arg))))
194 (($ $prompt escape? tag handler)
195 (emit-prompt asm escape? tag handler)))
198 (define (emit-vals syms)
200 (($ $primcall name args)
201 (error "unimplemented primcall in values context" name))
203 (for-each (match-lambda
204 ((src . dst) (emit-mov asm dst src)))
205 (lookup-parallel-moves label allocation))
206 (for-each maybe-load-constant (map slot syms) args)))
211 (($ $primcall 'cache-current-module! (sym scope))
212 (emit-cache-current-module! asm (slot sym) (constant scope)))
213 (($ $primcall 'free-set! (closure idx value))
214 (emit-free-set! asm (slot closure) (slot value) (constant idx)))
215 (($ $primcall 'box-set! (box value))
216 (emit-box-set! asm (slot box) (slot value)))
217 (($ $primcall 'struct-set! (struct index value))
218 (emit-struct-set! asm (slot struct) (slot index) (slot value)))
219 (($ $primcall 'vector-set! (vector index value))
220 (emit-vector-set asm (slot vector) (slot index) (slot value)))
221 (($ $primcall 'set-car! (pair value))
222 (emit-set-car! asm (slot pair) (slot value)))
223 (($ $primcall 'set-cdr! (pair value))
224 (emit-set-cdr! asm (slot pair) (slot value)))
225 (($ $primcall 'define! (sym value))
226 (emit-define asm (slot sym) (slot value)))
227 (($ $primcall name args)
228 (error "unhandled primcall in seq context" name))
232 (define (emit-test kt kf)
233 (define (unary op sym)
236 (op asm (slot sym) #t kf))
238 (op asm (slot sym) #f kt)
240 (define (binary op a b)
243 (op asm (slot a) (slot b) #t kf))
245 (op asm (slot a) (slot b) #f kt)
248 (($ $var sym) (unary emit-br-if-true sym))
249 (($ $primcall 'null? (a)) (unary emit-br-if-null a))
250 (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
251 (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
252 (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
253 (($ $primcall 'char? (a)) (unary emit-br-if-char a))
254 ;; Add TC7 tests here
255 (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
256 (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
257 (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
258 (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
259 (($ $primcall '< (a b)) (binary emit-br-if-< a b))
260 (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
261 (($ $primcall '= (a b)) (binary emit-br-if-= a b))
262 (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
263 (($ $primcall '> (a b)) (binary emit-br-if-< b a))))
265 (define (emit-trunc nreq rest? k)
268 (let ((proc-slot (lookup-call-proc-slot label allocation))
269 (nargs (length args)))
270 (or (maybe-load-constant proc-slot proc)
271 (maybe-mov proc-slot (slot proc)))
272 (let lp ((n (1+ proc-slot)) (args args))
275 (emit-call asm proc-slot (+ nargs 1))
276 ;; FIXME: Only allow more values if there is a rest arg.
277 ;; Express values truncation by the presence of an
278 ;; unused rest arg instead of implicitly.
279 (emit-receive-values asm proc-slot #t nreq)
281 (emit-bind-rest asm (+ proc-slot 1 nreq)))
282 (for-each (match-lambda
283 ((src . dst) (emit-mov asm dst src)))
284 (lookup-parallel-moves label allocation))
285 (emit-reset-frame asm nlocals))
287 (or (maybe-load-constant n arg)
288 (maybe-mov n (slot arg)))
289 (lp (1+ n) args)))))))
292 (match (lookup-cont k cont-table)
293 (($ $ktail) (emit-tail))
294 (($ $kargs (name) (sym)) (emit-val sym))
295 (($ $kargs () ()) (emit-seq))
296 (($ $kargs names syms) (emit-vals syms))
297 (($ $kargs (name) (sym)) (emit-val sym))
298 (($ $kif kt kf) (emit-test kt kf))
299 (($ $ktrunc ($ $arity req () rest () #f) k)
300 (emit-trunc (length req) (and rest #t) k))))
302 (define (collect-exps k src cont tail)
303 (define (find-exp k src term)
305 (($ $continue exp-k exp)
306 (cons (list k src exp-k exp) tail))
307 (($ $letk conts body)
308 (find-exp k src body))))
310 (($ $kargs names syms body)
311 (find-exp k src body))
314 (let lp ((exps (reverse (fold-local-conts collect-exps '() exp))))
317 (((k src exp-k exp) . exps)
318 (let ((next-label (match exps
323 (emit-source asm src))
324 (emit-rtl k exp-k exp next-label)
327 (define (compile-fun f asm)
328 (let ((allocation (allocate-slots f))
330 (($ $fun meta free body)
331 (build-local-cont-table body)))))
332 (define (emit-fun-clause clause alternate)
335 ($ $kclause ($ $arity req opt rest kw allow-other-keys?)
337 (let ((kw-indices (map (match-lambda
339 (cons key (lookup-slot sym allocation))))
341 (nlocals (lookup-nlocals k allocation)))
344 (emit-source asm src))
345 (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
347 (emit-rtl-sequence asm body allocation nlocals cont-table)
348 (emit-end-arity asm)))))
350 (define (emit-fun-clauses clauses)
353 (let ((kalternate (match clauses
355 ((($ $cont k) . _) k))))
356 (emit-fun-clause clause kalternate)
358 (emit-fun-clauses clauses))))))
361 (($ $fun meta free ($ $cont k src ($ $kentry self tail clauses)))
362 (emit-begin-program asm k (or meta '()))
364 (emit-source asm src))
365 (emit-fun-clauses clauses)
366 (emit-end-program asm)))))
368 (define (compile-rtl exp env opts)
369 (let* ((exp (fix-arities exp))
370 (exp (optimize exp opts))
371 (exp (convert-closures exp))
372 (exp (reify-primitives exp))
373 (asm (make-assembler)))
374 (visit-funs (lambda (fun)
375 (compile-fun fun asm))
377 (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))