Commit | Line | Data |
---|---|---|
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))))) |