compile lexical variable access and closure creation to the new ops
[bpt/guile.git] / module / language / glil / compile-assembly.scm
CommitLineData
f1d7723b
AW
1;;; Guile VM assembler
2
b912a1cd 3;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
f1d7723b 4
53befeb7
NJ
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
f1d7723b
AW
18
19;;; Code:
20
21(define-module (language glil compile-assembly)
22 #:use-module (system base syntax)
23 #:use-module (system base pmatch)
24 #:use-module (language glil)
25 #:use-module (language assembly)
26 #:use-module (system vm instruction)
27 #:use-module ((system vm program) #:select (make-binding))
f1d7723b
AW
28 #:use-module (ice-9 receive)
29 #:use-module ((srfi srfi-1) #:select (fold))
782a82ee 30 #:use-module (rnrs bytevector)
f1d7723b
AW
31 #:export (compile-assembly))
32
33;; Variable cache cells go in the object table, and serialize as their
34;; keys. The reason we wrap the keys in these records is so they don't
35;; compare as `equal?' to other objects in the object table.
36;;
37;; `key' is either a symbol or the list (MODNAME SYM PUBLIC?)
38
39(define-record <variable-cache-cell> key)
40
41;; Subprograms can be loaded into an object table as well. We need a
2cf1705c
AW
42;; disjoint type here too. (Subprograms have their own object tables --
43;; though probably we should just make one table per compilation unit.)
f1d7723b 44
2cf1705c 45(define-record <subprogram> table prog)
f1d7723b
AW
46
47
028e3d06
AW
48(define (limn-sources sources)
49 (let lp ((in sources) (out '()) (filename #f))
50 (if (null? in)
51 (reverse! out)
52 (let ((addr (caar in))
53 (new-filename (assq-ref (cdar in ) 'filename))
54 (line (assq-ref (cdar in) 'line))
55 (column (assq-ref (cdar in) 'column)))
56 (cond
57 ((not (equal? new-filename filename))
58 (lp (cdr in)
59 `((,addr . (,line . ,column))
ff33605d 60 (filename . ,new-filename)
028e3d06
AW
61 . ,out)
62 new-filename))
63 ((or (null? out) (not (equal? (cdar out) `(,line . ,column))))
64 (lp (cdr in)
65 `((,addr . (,line . ,column))
66 . ,out)
67 filename))
68 (else
69 (lp (cdr in) out filename)))))))
70
f1d7723b
AW
71(define (make-meta bindings sources tail)
72 (if (and (null? bindings) (null? sources) (null? tail))
73 #f
ac47d5f6
AW
74 (compile-assembly
75 (make-glil-program 0 0 0 0 '()
76 (list
77 (make-glil-const `(,bindings ,sources ,@tail))
131f7d6c 78 (make-glil-call 'return 1))))))
f1d7723b
AW
79
80;; A functional stack of names of live variables.
66d3e9a3
AW
81(define (make-open-binding name boxed? index)
82 (list name boxed? index))
f1d7723b
AW
83(define (make-closed-binding open-binding start end)
84 (make-binding (car open-binding) (cadr open-binding)
85 (caddr open-binding) start end))
cf10678f 86(define (open-binding bindings vars start)
f1d7723b
AW
87 (cons
88 (acons start
89 (map
90 (lambda (v)
91 (pmatch v
66d3e9a3
AW
92 ((,name ,boxed? ,i)
93 (make-open-binding name boxed? i))
cf10678f 94 (else (error "unknown binding type" v))))
f1d7723b
AW
95 vars)
96 (car bindings))
97 (cdr bindings)))
98(define (close-binding bindings end)
99 (pmatch bindings
100 ((((,start . ,closing) . ,open) . ,closed)
101 (cons open
102 (fold (lambda (o tail)
103 ;; the cons is for dsu sort
104 (acons start (make-closed-binding o start end)
105 tail))
106 closed
107 closing)))
108 (else (error "broken bindings" bindings))))
109(define (close-all-bindings bindings end)
110 (if (null? (car bindings))
111 (map cdr
112 (stable-sort (reverse (cdr bindings))
113 (lambda (x y) (< (car x) (car y)))))
114 (close-all-bindings (close-binding bindings end) end)))
115
116;; A functional object table.
ac47d5f6 117(define *module* 1)
53e28ed9
AW
118(define (assoc-ref-or-acons alist x make-y)
119 (cond ((assoc-ref alist x)
f1d7723b
AW
120 => (lambda (y) (values y alist)))
121 (else
122 (let ((y (make-y x alist)))
53e28ed9 123 (values y (acons x y alist))))))
f1d7723b 124(define (object-index-and-alist x alist)
53e28ed9 125 (assoc-ref-or-acons alist x
f1d7723b 126 (lambda (x alist)
ac47d5f6 127 (+ (length alist) *module*))))
f1d7723b
AW
128
129(define (compile-assembly glil)
130 (receive (code . _)
cf10678f 131 (glil->assembly glil '() '(()) '() '() #f -1)
f1d7723b 132 (car code)))
ac47d5f6
AW
133(define (make-object-table objects)
134 (and (not (null? objects))
135 (list->vector (cons #f objects))))
f1d7723b 136
cf10678f 137(define (glil->assembly glil nexts-stack bindings
f1d7723b
AW
138 source-alist label-alist object-alist addr)
139 (define (emit-code x)
194566b0 140 (values (map assembly-pack x) bindings source-alist label-alist object-alist))
f1d7723b 141 (define (emit-code/object x object-alist)
194566b0 142 (values (map assembly-pack x) bindings source-alist label-alist object-alist))
f1d7723b
AW
143
144 (record-case glil
53e28ed9
AW
145 ((<glil-program> nargs nrest nlocs nexts meta body closure-level)
146 (let ((toplevel? (null? nexts-stack)))
147 (define (process-body)
148 (let ((nexts-stack (cons nexts nexts-stack)))
149 (let lp ((body body) (code '()) (bindings '(())) (source-alist '())
150 (label-alist '()) (object-alist (if toplevel? #f '())) (addr 0))
151 (cond
152 ((null? body)
153 (values (reverse code)
154 (close-all-bindings bindings addr)
028e3d06 155 (limn-sources (reverse! source-alist))
53e28ed9
AW
156 (reverse label-alist)
157 (and object-alist (map car (reverse object-alist)))
158 addr))
159 (else
160 (receive (subcode bindings source-alist label-alist object-alist)
cf10678f 161 (glil->assembly (car body) nexts-stack bindings
53e28ed9
AW
162 source-alist label-alist object-alist addr)
163 (lp (cdr body) (append (reverse subcode) code)
164 bindings source-alist label-alist object-alist
2cf1705c 165 (addr+ addr subcode))))))))
53e28ed9
AW
166
167 (receive (code bindings sources labels objects len)
168 (process-body)
169 (let ((prog `(load-program ,nargs ,nrest ,nlocs ,nexts ,labels
ac47d5f6
AW
170 ,len
171 ,(make-meta bindings sources meta)
172 . ,code)))
f1d7723b 173 (cond
53e28ed9
AW
174 (toplevel?
175 ;; toplevel bytecode isn't loaded by the vm, no way to do
176 ;; object table or closure capture (not in the bytecode,
177 ;; anyway)
1005628a 178 (emit-code (align-program prog addr)))
f1d7723b 179 (else
ac47d5f6 180 (let ((table (dump-object (make-object-table objects) addr))
53e28ed9
AW
181 (closure (if (> closure-level 0) '((make-closure)) '())))
182 (cond
183 (object-alist
184 ;; if we are being compiled from something with an object
185 ;; table, cache the program there
186 (receive (i object-alist)
2cf1705c 187 (object-index-and-alist (make-subprogram table prog)
53e28ed9 188 object-alist)
a9b0f876
AW
189 (emit-code/object `(,(if (< i 256)
190 `(object-ref ,i)
191 `(long-object-ref ,(quotient i 256)
192 ,(modulo i 256)))
193 ,@closure)
53e28ed9
AW
194 object-alist)))
195 (else
196 ;; otherwise emit a load directly
1005628a
AW
197 (emit-code `(,@table ,@(align-program prog (addr+ addr table))
198 ,@closure)))))))))))
f1d7723b
AW
199
200 ((<glil-bind> vars)
201 (values '()
cf10678f 202 (open-binding bindings vars addr)
f1d7723b
AW
203 source-alist
204 label-alist
205 object-alist))
206
207 ((<glil-mv-bind> vars rest)
208 (values `((truncate-values ,(length vars) ,(if rest 1 0)))
cf10678f 209 (open-binding bindings vars addr)
f1d7723b
AW
210 source-alist
211 label-alist
212 object-alist))
213
214 ((<glil-unbind>)
215 (values '()
216 (close-binding bindings addr)
217 source-alist
218 label-alist
219 object-alist))
220
028e3d06 221 ((<glil-source> props)
f1d7723b
AW
222 (values '()
223 bindings
028e3d06 224 (acons addr props source-alist)
f1d7723b
AW
225 label-alist
226 object-alist))
227
228 ((<glil-void>)
229 (emit-code '((void))))
230
231 ((<glil-const> obj)
232 (cond
4b318482 233 ((object->assembly obj)
f1d7723b
AW
234 => (lambda (code)
235 (emit-code (list code))))
236 ((not object-alist)
237 (emit-code (dump-object obj addr)))
238 (else
239 (receive (i object-alist)
240 (object-index-and-alist obj object-alist)
a9b0f876
AW
241 (emit-code/object (if (< i 256)
242 `((object-ref ,i))
243 `((long-object-ref ,(quotient i 256)
244 ,(modulo i 256))))
f1d7723b
AW
245 object-alist)))))
246
cf10678f 247 ((<glil-local> op index)
f1d7723b
AW
248 (emit-code (if (eq? op 'ref)
249 `((local-ref ,index))
250 `((local-set ,index)))))
251
f1d7723b
AW
252 ((<glil-external> op depth index)
253 (emit-code (let lp ((d depth) (n 0) (stack nexts-stack))
254 (if (> d 0)
255 (lp (1- d) (+ n (car stack)) (cdr stack))
256 (if (eq? op 'ref)
257 `((external-ref ,(+ n index)))
258 `((external-set ,(+ n index))))))))
259
66d3e9a3
AW
260 ((<glil-lexical> local? boxed? op index)
261 (emit-code
262 `((,(if local?
263 (case op
264 ((ref) (if boxed? 'local-boxed-ref 'local-ref))
265 ((set) (if boxed? 'local-boxed-set 'local-set))
266 ((box) 'box)
267 ((empty-box) 'empty-box)
268 (else (error "what" op)))
269 (case op
270 ((ref) (if boxed? 'closure-boxed-ref 'closure-ref))
271 ((set) (if boxed? 'closure-boxed-set (error "what." glil)))
272 (else (error "what" op))))
273 ,index))))
274
f1d7723b
AW
275 ((<glil-toplevel> op name)
276 (case op
277 ((ref set)
278 (cond
279 ((not object-alist)
280 (emit-code `(,@(dump-object name addr)
281 (link-now)
282 ,(case op
283 ((ref) '(variable-ref))
284 ((set) '(variable-set))))))
285 (else
286 (receive (i object-alist)
287 (object-index-and-alist (make-variable-cache-cell name)
288 object-alist)
a9b0f876
AW
289 (emit-code/object (if (< i 256)
290 `((,(case op
291 ((ref) 'toplevel-ref)
292 ((set) 'toplevel-set))
293 ,i))
294 `((,(case op
295 ((ref) 'long-toplevel-ref)
296 ((set) 'long-toplevel-set))
297 ,(quotient i 256)
298 ,(modulo i 256))))
f1d7723b
AW
299 object-alist)))))
300 ((define)
301 (emit-code `((define ,(symbol->string name))
302 (variable-set))))
303 (else
304 (error "unknown toplevel var kind" op name))))
305
306 ((<glil-module> op mod name public?)
307 (let ((key (list mod name public?)))
308 (case op
309 ((ref set)
310 (cond
311 ((not object-alist)
312 (emit-code `(,@(dump-object key addr)
313 (link-now)
314 ,(case op
315 ((ref) '(variable-ref))
316 ((set) '(variable-set))))))
317 (else
318 (receive (i object-alist)
53e28ed9 319 (object-index-and-alist (make-variable-cache-cell key)
f1d7723b
AW
320 object-alist)
321 (emit-code/object (case op
322 ((ref) `((toplevel-ref ,i)))
323 ((set) `((toplevel-set ,i))))
324 object-alist)))))
325 (else
326 (error "unknown module var kind" op key)))))
327
328 ((<glil-label> label)
329 (values '()
330 bindings
331 source-alist
332 (acons label addr label-alist)
333 object-alist))
334
335 ((<glil-branch> inst label)
336 (emit-code `((,inst ,label))))
337
338 ;; nargs is number of stack args to insn. probably should rename.
339 ((<glil-call> inst nargs)
340 (if (not (instruction? inst))
341 (error "Unknown instruction:" inst))
342 (let ((pops (instruction-pops inst)))
343 (cond ((< pops 0)
c11f46af
AW
344 (case (instruction-length inst)
345 ((1) (emit-code `((,inst ,nargs))))
346 ((2) (emit-code `((,inst ,(quotient nargs 256)
347 ,(modulo nargs 256)))))
348 (else (error "Unknown length for variable-arg instruction:"
349 inst (instruction-length inst)))))
f1d7723b
AW
350 ((= pops nargs)
351 (emit-code `((,inst))))
352 (else
353 (error "Wrong number of stack arguments to instruction:" inst nargs)))))
354
355 ((<glil-mv-call> nargs ra)
356 (emit-code `((mv-call ,nargs ,ra))))))
357
f1d7723b
AW
358(define (dump-object x addr)
359 (define (too-long x)
360 (error (string-append x " too long")))
361
2cf1705c
AW
362 (cond
363 ((object->assembly x) => list)
364 ((variable-cache-cell? x) (dump-object (variable-cache-cell-key x) addr))
365 ((subprogram? x)
366 `(,@(subprogram-table x)
367 ,@(align-program (subprogram-prog x)
368 (addr+ addr (subprogram-table x)))))
2cf1705c
AW
369 ((number? x)
370 `((load-number ,(number->string x))))
371 ((string? x)
372 `((load-string ,x)))
373 ((symbol? x)
374 `((load-symbol ,(symbol->string x))))
375 ((keyword? x)
376 `((load-keyword ,(symbol->string (keyword->symbol x)))))
377 ((list? x)
378 (let ((tail (let ((len (length x)))
379 (if (>= len 65536) (too-long "list"))
380 `((list ,(quotient len 256) ,(modulo len 256))))))
381 (let dump-objects ((objects x) (codes '()) (addr addr))
382 (if (null? objects)
383 (fold append tail codes)
384 (let ((code (dump-object (car objects) addr)))
385 (dump-objects (cdr objects) (cons code codes)
386 (addr+ addr code)))))))
387 ((pair? x)
388 (let ((kar (dump-object (car x) addr)))
389 `(,@kar
390 ,@(dump-object (cdr x) (addr+ addr kar))
391 (cons))))
392 ((vector? x)
393 (let* ((len (vector-length x))
394 (tail (if (>= len 65536)
395 (too-long "vector")
396 `((vector ,(quotient len 256) ,(modulo len 256))))))
397 (let dump-objects ((i 0) (codes '()) (addr addr))
398 (if (>= i len)
399 (fold append tail codes)
400 (let ((code (dump-object (vector-ref x i) addr)))
401 (dump-objects (1+ i) (cons code codes)
402 (addr+ addr code)))))))
782a82ee
AW
403 ((and (array? x) (symbol? (array-type x)))
404 (let* ((type (dump-object (array-type x) addr))
405 (shape (dump-object (array-shape x) (addr+ addr type))))
406 `(,@type
407 ,@shape
408 ,@(align-code
409 `(load-array ,(uniform-array->bytevector x))
410 (addr+ (addr+ addr type) shape)
411 8
412 4))))
2cf1705c
AW
413 (else
414 (error "assemble: unrecognized object" x))))
f1d7723b 415