5259ca0af595b9153741158ee2868d7925248f2a
[bpt/guile.git] / module / language / glil / compile-objcode.scm
1 ;;; Guile VM assembler
2
3 ;; Copyright (C) 2001 Free Software Foundation, Inc.
4
5 ;; This program is free software; you can redistribute it and/or modify
6 ;; it under the terms of the GNU General Public License as published by
7 ;; the Free Software Foundation; either version 2, or (at your option)
8 ;; any later version.
9 ;;
10 ;; This program 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
13 ;; GNU General Public License for more details.
14 ;;
15 ;; You should have received a copy of the GNU General Public License
16 ;; along with this program; see the file COPYING. If not, write to
17 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 ;; Boston, MA 02111-1307, USA.
19
20 ;;; Code:
21
22 (define-module (language glil compile-objcode)
23 #:use-syntax (system base syntax)
24 #:use-module (language glil)
25 #:use-module (system vm instruction)
26 #:use-module (system vm objcode)
27 #:use-module ((system vm program) #:select (make-binding))
28 #:use-module (system vm conv)
29 #:use-module (ice-9 regex)
30 #:use-module (ice-9 common-list)
31 #:use-module (srfi srfi-4)
32 #:use-module ((srfi srfi-1) #:select (append-map))
33 #:export (preprocess codegen compile-objcode))
34
35 (define (compile-objcode glil env . opts)
36 (codegen (preprocess glil #f) #t))
37
38 \f
39 ;;;
40 ;;; Types
41 ;;;
42
43 (define-record <vm-asm> venv glil body)
44 (define-record <venv> parent nexts closure?)
45 ;; key is either a symbol or the list (MODNAME SYM PUBLIC?)
46 (define-record <vlink-now> key)
47 (define-record <vlink-later> key)
48 (define-record <vdefine> name)
49 (define-record <bytespec> vars bytes meta objs closure?)
50
51 \f
52 ;;;
53 ;;; Stage 1: Preprocess
54 ;;;
55
56 (define (preprocess x e)
57 (record-case x
58 ((<glil-asm> vars meta body)
59 (let* ((venv (make-venv #:parent e #:nexts (glil-vars-nexts vars) #:closure? #f))
60 (body (map (lambda (x) (preprocess x venv)) body)))
61 (make-vm-asm #:venv venv #:glil x #:body body)))
62 ((<glil-external> op depth index)
63 (do ((d depth (- d 1))
64 (e e (venv-parent e)))
65 ((= d 0))
66 (set! (venv-closure? e) #t))
67 x)
68 (else x)))
69
70 \f
71 ;;;
72 ;;; Stage 2: Bytecode generation
73 ;;;
74
75 (define-macro (push x loc)
76 `(set! ,loc (cons ,x ,loc)))
77 (define-macro (pop loc)
78 `(let ((_x (car ,loc))) (set! ,loc (cdr ,loc)) _x))
79
80 ;; this is to avoid glil-const's desire to put constants in the object
81 ;; array -- instead we explicitly want them in the code, because meta
82 ;; info is infrequently used. to load it up always would make garbage,
83 ;; needlessly. so hide it behind a lambda.
84 (define (make-meta bindings sources tail)
85 (if (and (null? bindings) (null? sources) (null? tail))
86 #f
87 (let ((stack '()))
88 (define (push-code! code)
89 (push (code->bytes code) stack))
90 (dump-object! push-code! `(,bindings ,sources ,@tail))
91 (push-code! '(return))
92 (make-bytespec #:vars (make-glil-vars 0 0 0 0)
93 #:bytes (stack->bytes (reverse! stack) '())
94 #:meta #f #:objs #f #:closure? #f))))
95
96 (define (byte-length x)
97 (cond ((u8vector? x) (u8vector-length x))
98 ((>= (instruction-length (car x)) 0)
99 ;; one byte for the instruction itself
100 (1+ (instruction-length (car x))))
101 (else (error "variable-length instruction?" x))))
102
103 ;; a binding that doesn't yet know its extents
104 (define (make-temp-binding name ext? index)
105 (list name ext? index))
106 (define btemp:name car)
107 (define btemp:extp cadr)
108 (define btemp:index caddr)
109
110 (define (codegen glil toplevel)
111 (record-case glil
112 ((<vm-asm> venv glil body) (record-case glil ((<glil-asm> vars meta) ; body?
113 (let ((stack '())
114 (open-bindings '())
115 (closed-bindings '())
116 (source-alist '())
117 (label-alist '())
118 (object-alist '()))
119 (define (push-code! code)
120 ; (format #t "push-code! ~a~%" code)
121 (push (code->bytes code) stack))
122 (define (push-object! x)
123 (cond ((object->code x) => push-code!)
124 (toplevel
125 (dump-object! push-code! x))
126 (else
127 (let ((i (cond ((object-assoc x object-alist) => cdr)
128 (else
129 (let ((i (length object-alist)))
130 (set! object-alist (acons x i object-alist))
131 i)))))
132 (push-code! `(object-ref ,i))))))
133 (define (munge-bindings bindings nargs)
134 (map
135 (lambda (v)
136 (let ((name (car v)) (type (cadr v)) (i (caddr v)))
137 (case type
138 ((argument) (make-temp-binding name #f i))
139 ((local) (make-temp-binding name #f (+ nargs i)))
140 ((external) (make-temp-binding name #t i))
141 (else (error "unknown binding type" name type)))))
142 bindings))
143 (define (push-bindings! bindings)
144 (push (cons (current-address) bindings) open-bindings))
145 (define (close-binding!)
146 (let* ((bindings (pop open-bindings))
147 (start (car bindings))
148 (end (current-address)))
149 (for-each
150 (lambda (open)
151 ;; the cons is for dsu sort
152 (push (cons start
153 (make-binding (btemp:name open) (btemp:extp open)
154 (btemp:index open) start end))
155 closed-bindings))
156 (cdr bindings))))
157 (define (finish-bindings!)
158 (while (not (null? open-bindings)) (close-binding!))
159 (set! closed-bindings
160 (stable-sort! (reverse! closed-bindings)
161 (lambda (x y) (< (car x) (car y)))))
162 (set! closed-bindings (map cdr closed-bindings)))
163 (define (current-address)
164 (apply + (map byte-length stack)))
165 (define (generate-code x)
166 (record-case x
167 ((<vm-asm> venv)
168 (push-object! (codegen x #f))
169 (if (venv-closure? venv) (push-code! `(make-closure))))
170
171 ((<glil-bind> (binds vars))
172 (push-bindings! (munge-bindings binds (glil-vars-nargs vars))))
173
174 ((<glil-mv-bind> (binds vars) rest)
175 (push-bindings! (munge-bindings binds (glil-vars-nargs vars)))
176 (push-code! `(truncate-values ,(length binds) ,(if rest 1 0))))
177
178 ((<glil-unbind>)
179 (close-binding!))
180
181 ((<glil-source> loc)
182 (set! source-alist (acons (current-address) loc source-alist)))
183
184 ((<glil-void>)
185 (push-code! '(void)))
186
187 ((<glil-const> obj)
188 (push-object! obj))
189
190 ((<glil-argument> op index)
191 (if (eq? op 'ref)
192 (push-code! `(local-ref ,index))
193 (push-code! `(local-set ,index))))
194
195 ((<glil-local> op index)
196 (if (eq? op 'ref)
197 (push-code! `(local-ref ,(+ (glil-vars-nargs vars) index)))
198 (push-code! `(local-set ,(+ (glil-vars-nargs vars) index)))))
199
200 ((<glil-external> op depth index)
201 (do ((e venv (venv-parent e))
202 (d depth (1- d))
203 (n 0 (+ n (venv-nexts e))))
204 ((= d 0)
205 (if (eq? op 'ref)
206 (push-code! `(external-ref ,(+ n index)))
207 (push-code! `(external-set ,(+ n index)))))))
208
209 ((<glil-toplevel> op name)
210 (case op
211 ((ref set)
212 (cond
213 (toplevel
214 (push-object! (make-vlink-now #:key name))
215 (push-code! (case op
216 ((ref) '(variable-ref))
217 ((set) '(variable-set)))))
218 (else
219 (let* ((var (make-vlink-later #:key name))
220 (i (cond ((object-assoc var object-alist) => cdr)
221 (else
222 (let ((i (length object-alist)))
223 (set! object-alist (acons var i object-alist))
224 i)))))
225 (push-code! (case op
226 ((ref) `(toplevel-ref ,i))
227 ((set) `(toplevel-set ,i))))))))
228 ((define)
229 (push-object! (make-vdefine #:name name))
230 (push-code! '(variable-set)))
231 (else
232 (error "unknown toplevel var kind" op name))))
233
234 ((<glil-module> op mod name public?)
235 (let ((key (list mod name public?)))
236 (case op
237 ((ref set)
238 (cond
239 (toplevel
240 (push-object! (make-vlink-now #:key key))
241 (push-code! (case op
242 ((ref) '(variable-ref))
243 ((set) '(variable-set)))))
244 (else
245 (let* ((var (make-vlink-later #:key key))
246 (i (cond ((object-assoc var object-alist) => cdr)
247 (else
248 (let ((i (length object-alist)))
249 (set! object-alist (acons var i object-alist))
250 i)))))
251 (push-code! (case op
252 ((ref) `(toplevel-ref ,i))
253 ((set) `(toplevel-set ,i))))))))
254 (else
255 (error "unknown module var kind" op key)))))
256
257 ((<glil-label> label)
258 (set! label-alist (assq-set! label-alist label (current-address))))
259
260 ((<glil-branch> inst label)
261 (push (list inst label) stack))
262
263 ((<glil-call> inst nargs)
264 (if (instruction? inst)
265 (let ((pops (instruction-pops inst)))
266 (cond ((< pops 0)
267 (push-code! (list inst nargs)))
268 ((= pops nargs)
269 (push-code! (list inst)))
270 (else
271 (error "Wrong number of arguments:" inst nargs))))
272 (error "Unknown instruction:" inst)))
273
274 ((<glil-mv-call> nargs ra)
275 (push (list 'mv-call nargs ra) stack))))
276
277 ;;
278 ;; main
279 (for-each generate-code body)
280 (finish-bindings!)
281 ; (format #t "codegen: stack = ~a~%" (reverse stack))
282 (let ((bytes (stack->bytes (reverse! stack) label-alist)))
283 (if toplevel
284 (bytecode->objcode bytes (glil-vars-nlocs vars) (glil-vars-nexts vars))
285 (make-bytespec #:vars vars #:bytes bytes
286 #:meta (make-meta closed-bindings
287 (reverse! source-alist)
288 meta)
289 #:objs (let ((objs (map car (reverse! object-alist))))
290 (if (null? objs) #f (list->vector objs)))
291 #:closure? (venv-closure? venv))))))))))
292
293 (define (object-assoc x alist)
294 (record-case x
295 ((<vlink-now>) (assoc x alist))
296 ((<vlink-later>) (assoc x alist))
297 (else (assq x alist))))
298
299 (define (check-length len u8v)
300 (or (= len (u8vector-length u8v))
301 (error "the badness!" len u8v))
302 u8v)
303
304 (define (stack->bytes stack label-alist)
305 (let loop ((result '()) (stack stack) (addr 0))
306 (if (null? stack)
307 (check-length
308 addr
309 (list->u8vector
310 (append-map u8vector->list (reverse! result))))
311 (let ((elt (car stack)))
312 (cond
313 ((u8vector? elt)
314 (loop (cons elt result)
315 (cdr stack)
316 (+ addr (byte-length elt))))
317 ((symbol? (car (last-pair elt)))
318 ;; not yet code because labels needed to be resolved
319 (let* ((head (list-head elt (1- (length elt))))
320 (label-addr (assq-ref label-alist (car (last-pair elt))))
321 (offset (- label-addr (+ addr (byte-length elt))))
322 (n (if (< offset 0) (+ offset 65536) offset)))
323 (loop (cons (code->bytes
324 (append head (list (quotient n 256) (modulo n 256))))
325 result)
326 (cdr stack)
327 (+ addr (byte-length elt)))))
328 (else (error "bad code" elt)))))))
329
330 \f
331 ;;;
332 ;;; Object dump
333 ;;;
334
335 ;; NOTE: undumpped in vm_system.c
336
337 (define (dump-object! push-code! x)
338 (define (too-long x)
339 (error (string-append x " too long")))
340
341 (let dump! ((x x))
342 (cond
343 ((object->code x) => push-code!)
344 ((record? x)
345 (record-case x
346 ((<bytespec> vars bytes meta objs closure?)
347 ;; dump parameters
348 (let ((nargs (glil-vars-nargs vars)) (nrest (glil-vars-nrest vars))
349 (nlocs (glil-vars-nlocs vars)) (nexts (glil-vars-nexts vars)))
350 (cond
351 ((and (< nargs 16) (< nlocs 128) (< nexts 16))
352 ;; 16-bit representation
353 (let ((x (logior
354 (ash nargs 12) (ash nrest 11) (ash nlocs 4) nexts)))
355 (push-code! `(make-int16 ,(ash x -8) ,(logand x (1- (ash 1 8)))))))
356 (else
357 ;; Other cases
358 (if (> (+ nargs nlocs) 255)
359 (error "too many locals" nargs nlocs))
360 ;; really it should be a flag..
361 (if (> nrest 1) (error "nrest should be 0 or 1" nrest))
362 (if (> nexts 255) (error "too many externals" nexts))
363 (push-code! (object->code nargs))
364 (push-code! (object->code nrest))
365 (push-code! (object->code nlocs))
366 (push-code! (object->code nexts))
367 (push-code! (object->code #f)))))
368 ;; dump object table
369 (if objs (dump! objs))
370 ;; dump meta data
371 (if meta (dump! meta))
372 ;; dump bytecode
373 (push-code! `(load-program ,bytes)))
374 ((<vlink-later> key)
375 (dump! key))
376 ((<vlink-now> key)
377 (dump! key)
378 (push-code! '(link-now)))
379 ((<vdefine> name)
380 (push-code! `(define ,(symbol->string name))))
381 (else
382 (error "assemble: unknown record type" (record-type-descriptor x)))))
383 ((and (integer? x) (exact? x))
384 (let ((str (do ((n x (quotient n 256))
385 (l '() (cons (modulo n 256) l)))
386 ((= n 0)
387 (apply u8vector l)))))
388 (push-code! `(load-integer ,str))))
389 ((number? x)
390 (push-code! `(load-number ,(number->string x))))
391 ((string? x)
392 (push-code! `(load-string ,x)))
393 ((symbol? x)
394 (push-code! `(load-symbol ,(symbol->string x))))
395 ((keyword? x)
396 (push-code! `(load-keyword ,(symbol->string (keyword->symbol x)))))
397 ((list? x)
398 (for-each dump! x)
399 (let ((len (length x)))
400 (if (>= len 65536) (too-long 'list))
401 (push-code! `(list ,(quotient len 256) ,(modulo len 256)))))
402 ((pair? x)
403 (dump! (car x))
404 (dump! (cdr x))
405 (push-code! `(cons)))
406 ((vector? x)
407 (for-each dump! (vector->list x))
408 (let ((len (vector-length x)))
409 (if (>= len 65536) (too-long 'vector))
410 (push-code! `(vector ,(quotient len 256) ,(modulo len 256)))))
411 (else
412 (error "assemble: unrecognized object" x)))))