fix typo in assembler
[bpt/guile.git] / module / system / vm / assemble.scm
CommitLineData
17e90c5e
KN
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 (system vm assemble)
1a1a10d3
AW
23 #:use-syntax (system base syntax)
24 #:use-module (system il 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 assemble))
17e90c5e
KN
34
35(define (assemble glil env . opts)
4bfb26f5 36 (codegen (preprocess glil #f) #t))
17e90c5e
KN
37
38\f
39;;;
40;;; Types
41;;;
42
ac99cb0c
KN
43(define-record (<vm-asm> venv glil body))
44(define-record (<venv> parent nexts closure?))
fd358575
AW
45;; key is either a symbol or the list (MODNAME SYM PUBLIC?)
46(define-record (<vlink-now> key))
47(define-record (<vlink-later> key))
a1122f8c 48(define-record (<vdefine> name))
ac99cb0c 49(define-record (<bytespec> vars bytes meta objs closure?))
17e90c5e
KN
50
51\f
52;;;
53;;; Stage 1: Preprocess
54;;;
55
56(define (preprocess x e)
1aa0dd2b 57 (record-case x
fbde2b91 58 ((<glil-asm> vars meta body)
1a1a10d3 59 (let* ((venv (make-venv #:parent e #:nexts (glil-vars-nexts vars) #:closure? #f))
17e90c5e 60 (body (map (lambda (x) (preprocess x venv)) body)))
1a1a10d3 61 (make-vm-asm #:venv venv #:glil x #:body body)))
1aa0dd2b 62 ((<glil-external> op depth index)
0b5f0e49 63 (do ((d depth (- d 1))
f540e327 64 (e e (venv-parent e)))
b6368dbb 65 ((= d 0))
f540e327 66 (set! (venv-closure? e) #t))
17e90c5e 67 x)
b6368dbb 68 (else x)))
17e90c5e
KN
69
70\f
71;;;
72;;; Stage 2: Bytecode generation
73;;;
74
13906f97
AW
75(define-macro (push x loc)
76 `(set! ,loc (cons ,x ,loc)))
02b1883e
AW
77(define-macro (pop loc)
78 `(let ((_x (car ,loc))) (set! ,loc (cdr ,loc)) _x))
13906f97
AW
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))
1a1a10d3
AW
92 (make-bytespec #:vars (make-glil-vars 0 0 0 0)
93 #:bytes (stack->bytes (reverse! stack) '())
94 #:meta #f #:objs #f #:closure? #f))))
13906f97 95
efbd5892
AW
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
f580ec0f
AW
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
17e90c5e 110(define (codegen glil toplevel)
1aa0dd2b 111 (record-case glil
fbde2b91 112 ((<vm-asm> venv glil body) (record-case glil ((<glil-asm> vars meta) ; body?
17e90c5e 113 (let ((stack '())
02b1883e
AW
114 (open-bindings '())
115 (closed-bindings '())
ac99cb0c 116 (source-alist '())
17e90c5e 117 (label-alist '())
3616e9e9 118 (object-alist '()))
17e90c5e 119 (define (push-code! code)
2d80426a 120; (format #t "push-code! ~a~%" code)
13906f97 121 (push (code->bytes code) stack))
206a0622 122 (define (push-object! x)
880ed584 123 (cond ((object->code x) => push-code!)
13906f97
AW
124 (toplevel
125 (dump-object! push-code! x))
f0c99935 126 (else
880ed584
KN
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))))))
02b1883e
AW
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
f580ec0f
AW
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))
02b1883e
AW
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
f580ec0f
AW
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))
02b1883e
AW
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)))
ac99cb0c 163 (define (current-address)
ac99cb0c 164 (apply + (map byte-length stack)))
17e90c5e 165 (define (generate-code x)
1aa0dd2b
AW
166 (record-case x
167 ((<vm-asm> venv)
f0c99935 168 (push-object! (codegen x #f))
f540e327 169 (if (venv-closure? venv) (push-code! `(make-closure))))
ac99cb0c 170
f540e327 171 ((<glil-bind> (binds vars))
02b1883e 172 (push-bindings! (munge-bindings binds (glil-vars-nargs vars))))
ac99cb0c 173
d51406fe 174 ((<glil-mv-bind> (binds vars) rest)
02b1883e
AW
175 (push-bindings! (munge-bindings binds (glil-vars-nargs vars)))
176 (push-code! `(truncate-values ,(length binds) ,(if rest 1 0))))
d51406fe 177
1aa0dd2b 178 ((<glil-unbind>)
02b1883e 179 (close-binding!))
ac99cb0c 180
1aa0dd2b 181 ((<glil-source> loc)
ac99cb0c 182 (set! source-alist (acons (current-address) loc source-alist)))
17e90c5e 183
1aa0dd2b 184 ((<glil-void>)
41f248a8 185 (push-code! '(void)))
17e90c5e 186
f540e327
AW
187 ((<glil-const> obj)
188 (push-object! obj))
17e90c5e 189
1aa0dd2b 190 ((<glil-argument> op index)
f0c99935 191 (if (eq? op 'ref)
532565b0
KN
192 (push-code! `(local-ref ,index))
193 (push-code! `(local-set ,index))))
17e90c5e 194
1aa0dd2b 195 ((<glil-local> op index)
f0c99935 196 (if (eq? op 'ref)
024e1862
AW
197 (push-code! `(local-ref ,(+ (glil-vars-nargs vars) index)))
198 (push-code! `(local-set ,(+ (glil-vars-nargs vars) index)))))
17e90c5e 199
1aa0dd2b 200 ((<glil-external> op depth index)
024e1862 201 (do ((e venv (venv-parent e))
17e90c5e 202 (d depth (1- d))
024e1862 203 (n 0 (+ n (venv-nexts e))))
17e90c5e 204 ((= d 0)
f0c99935
KN
205 (if (eq? op 'ref)
206 (push-code! `(external-ref ,(+ n index)))
207 (push-code! `(external-set ,(+ n index)))))))
17e90c5e 208
a1122f8c 209 ((<glil-toplevel> op name)
cd9d95d7 210 (case op
6297d229
AW
211 ((ref set)
212 (cond
213 (toplevel
fd358575 214 (push-object! (make-vlink-now #:key name))
6297d229
AW
215 (push-code! (case op
216 ((ref) '(variable-ref))
217 ((set) '(variable-set)))))
218 (else
fd358575 219 (let* ((var (make-vlink-later #:key name))
6297d229
AW
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) `(late-variable-ref ,i))
227 ((set) `(late-variable-set ,i))))))))
cd9d95d7 228 ((define)
a1122f8c 229 (push-object! (make-vdefine #:name name))
6297d229
AW
230 (push-code! '(variable-set)))
231 (else
232 (error "unknown toplevel var kind" op name))))
9cc649b8 233
fd358575
AW
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) `(late-variable-ref ,i))
253 ((set) `(late-variable-set ,i))))))))
254 (else
255 (error "unknown module var kind" op key)))))
256
1aa0dd2b 257 ((<glil-label> label)
ac99cb0c 258 (set! label-alist (assq-set! label-alist label (current-address))))
17e90c5e 259
1aa0dd2b 260 ((<glil-branch> inst label)
13906f97 261 (push (list inst label) stack))
17e90c5e 262
1aa0dd2b 263 ((<glil-call> inst nargs)
17e90c5e 264 (if (instruction? inst)
46cd9a34
KN
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))))
efbd5892
AW
272 (error "Unknown instruction:" inst)))
273
274 ((<glil-mv-call> nargs ra)
275 (push (list 'mv-call nargs ra) stack))))
276
17e90c5e
KN
277 ;;
278 ;; main
17e90c5e 279 (for-each generate-code body)
02b1883e 280 (finish-bindings!)
2d80426a 281; (format #t "codegen: stack = ~a~%" (reverse stack))
41f248a8
KN
282 (let ((bytes (stack->bytes (reverse! stack) label-alist)))
283 (if toplevel
024e1862 284 (bytecode->objcode bytes (glil-vars-nlocs vars) (glil-vars-nexts vars))
1a1a10d3 285 (make-bytespec #:vars vars #:bytes bytes
02b1883e
AW
286 #:meta (make-meta closed-bindings
287 (reverse! source-alist)
288 meta)
1a1a10d3 289 #:objs (let ((objs (map car (reverse! object-alist))))
849cefac 290 (if (null? objs) #f (list->vector objs)))
1a1a10d3 291 #:closure? (venv-closure? venv))))))))))
17e90c5e 292
880ed584 293(define (object-assoc x alist)
1aa0dd2b 294 (record-case x
6297d229
AW
295 ((<vlink-now>) (assoc x alist))
296 ((<vlink-later>) (assoc x alist))
ac99cb0c 297 (else (assq x alist))))
880ed584 298
efbd5892
AW
299(define (check-length len u8v)
300 (or (= len (u8vector-length u8v))
301 (error "the badness!" len u8v))
302 u8v)
303
41f248a8 304(define (stack->bytes stack label-alist)
880ed584 305 (let loop ((result '()) (stack stack) (addr 0))
206a0622 306 (if (null? stack)
efbd5892
AW
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)))))))
17e90c5e
KN
329
330\f
331;;;
4bfb26f5 332;;; Object dump
17e90c5e
KN
333;;;
334
23b587b0 335;; NOTE: undumpped in vm_system.c
17e90c5e 336
f0c99935 337(define (dump-object! push-code! x)
23b587b0
LC
338 (define (too-long x)
339 (error (string-append x " too long")))
340
bd098a1a
KN
341 (let dump! ((x x))
342 (cond
343 ((object->code x) => push-code!)
1aa0dd2b
AW
344 ((record? x)
345 (record-case x
346 ((<bytespec> vars bytes meta objs closure?)
4bfb26f5 347 ;; dump parameters
024e1862
AW
348 (let ((nargs (glil-vars-nargs vars)) (nrest (glil-vars-nrest vars))
349 (nlocs (glil-vars-nlocs vars)) (nexts (glil-vars-nexts vars)))
ac99cb0c 350 (cond
ac99cb0c
KN
351 ((and (< nargs 16) (< nlocs 128) (< nexts 16))
352 ;; 16-bit representation
97f1153a
AW
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)))))))
ac99cb0c
KN
356 (else
357 ;; Other cases
5e390de6
AW
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))
6ce6dc03 362 (if (> nexts 255) (error "too many externals" nexts))
ac99cb0c
KN
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)))))
4bfb26f5 368 ;; dump object table
ac99cb0c
KN
369 (if objs (dump! objs))
370 ;; dump meta data
371 (if meta (dump! meta))
4bfb26f5 372 ;; dump bytecode
13906f97 373 (push-code! `(load-program ,bytes)))
fd358575
AW
374 ((<vlink-later> key)
375 (dump! key))
376 ((<vlink-now> key)
377 (dump! key)
6297d229 378 (push-code! '(link-now)))
a1122f8c 379 ((<vdefine> name)
cd9d95d7 380 (push-code! `(define ,(symbol->string name))))
1aa0dd2b 381 (else
7f52f9e3 382 (error "assemble: unknown record type" (record-type-descriptor x)))))
1aa0dd2b
AW
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)
a52b96a7 396 (push-code! `(load-keyword ,(symbol->string (keyword->symbol x)))))
1aa0dd2b
AW
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)))))