Encode the length of constant lists/vectors on 2 octets instead of 1.
[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)
ac99cb0c 23 :use-syntax (system base syntax)
17e90c5e 24 :use-module (system il glil)
884d46de
LC
25 :use-module ((system vm core)
26 :select (instruction? instruction-pops
27 make-binding
28 bytecode->objcode))
17e90c5e
KN
29 :use-module (system vm conv)
30 :use-module (ice-9 match)
31 :use-module (ice-9 regex)
32 :use-module (ice-9 common-list)
054599f1 33 :use-module (srfi srfi-4)
0b5f0e49 34 :export (preprocess codegen assemble))
17e90c5e
KN
35
36(define (assemble glil env . opts)
4bfb26f5 37 (codegen (preprocess glil #f) #t))
17e90c5e
KN
38
39\f
40;;;
41;;; Types
42;;;
43
ac99cb0c
KN
44(define-record (<vm-asm> venv glil body))
45(define-record (<venv> parent nexts closure?))
46(define-record (<vmod> id))
47(define-record (<vlink> module name))
48(define-record (<bytespec> vars bytes meta objs closure?))
17e90c5e
KN
49
50\f
51;;;
52;;; Stage 1: Preprocess
53;;;
54
55(define (preprocess x e)
56 (match x
ac99cb0c 57 (($ <glil-asm> vars body)
0b5f0e49 58 (let* ((venv (<venv> :parent e :nexts (slot vars 'nexts) :closure? #f))
17e90c5e 59 (body (map (lambda (x) (preprocess x venv)) body)))
ac99cb0c 60 (<vm-asm> :venv venv :glil x :body body)))
17e90c5e 61 (($ <glil-external> op depth index)
0b5f0e49 62 (do ((d depth (- d 1))
b6368dbb
LC
63 (e e (slot e 'parent)))
64 ((= d 0))
0b5f0e49 65 (set! (slot e 'closure?) #t))
17e90c5e 66 x)
b6368dbb 67 (else x)))
17e90c5e
KN
68
69\f
70;;;
71;;; Stage 2: Bytecode generation
72;;;
73
74(define (codegen glil toplevel)
75 (match glil
ac99cb0c 76 (($ <vm-asm> venv ($ <glil-asm> vars _) body)
17e90c5e 77 (let ((stack '())
af988bbf 78 (binding-alist '())
ac99cb0c 79 (source-alist '())
17e90c5e 80 (label-alist '())
3616e9e9 81 (object-alist '()))
17e90c5e 82 (define (push-code! code)
2d80426a 83; (format #t "push-code! ~a~%" code)
41f248a8 84 (set! stack (cons (code->bytes code) stack)))
206a0622 85 (define (push-object! x)
880ed584 86 (cond ((object->code x) => push-code!)
41f248a8 87 (toplevel (dump-object! push-code! x))
f0c99935 88 (else
880ed584
KN
89 (let ((i (cond ((object-assoc x object-alist) => cdr)
90 (else
91 (let ((i (length object-alist)))
92 (set! object-alist (acons x i object-alist))
93 i)))))
94 (push-code! `(object-ref ,i))))))
ac99cb0c
KN
95 (define (current-address)
96 (define (byte-length x)
fa19602c 97 (cond ((u8vector? x) (u8vector-length x))
ac99cb0c
KN
98 (else 3)))
99 (apply + (map byte-length stack)))
17e90c5e
KN
100 (define (generate-code x)
101 (match x
bd098a1a 102 (($ <vm-asm> venv)
f0c99935 103 (push-object! (codegen x #f))
0b5f0e49 104 (if (slot venv 'closure?) (push-code! `(make-closure))))
ac99cb0c
KN
105
106 (($ <glil-bind> binds)
af988bbf
KN
107 (let ((bindings
108 (map (lambda (v)
109 (let ((name (car v)) (type (cadr v)) (i (caddr v)))
110 (case type
111 ((argument) (make-binding name #f i))
112 ((local) (make-binding name #f (+ vars.nargs i)))
113 ((external) (make-binding name #t i)))))
114 binds)))
115 (set! binding-alist
116 (acons (current-address) bindings binding-alist))))
ac99cb0c
KN
117
118 (($ <glil-unbind>)
af988bbf 119 (set! binding-alist (acons (current-address) #f binding-alist)))
ac99cb0c
KN
120
121 (($ <glil-source> loc)
122 (set! source-alist (acons (current-address) loc source-alist)))
17e90c5e
KN
123
124 (($ <glil-void>)
41f248a8 125 (push-code! '(void)))
17e90c5e
KN
126
127 (($ <glil-const> x)
f0c99935 128 (push-object! x))
17e90c5e
KN
129
130 (($ <glil-argument> op index)
f0c99935 131 (if (eq? op 'ref)
532565b0
KN
132 (push-code! `(local-ref ,index))
133 (push-code! `(local-set ,index))))
17e90c5e
KN
134
135 (($ <glil-local> op index)
f0c99935 136 (if (eq? op 'ref)
ac99cb0c
KN
137 (push-code! `(local-ref ,(+ vars.nargs index)))
138 (push-code! `(local-set ,(+ vars.nargs index)))))
17e90c5e
KN
139
140 (($ <glil-external> op depth index)
ac99cb0c 141 (do ((e venv e.parent)
17e90c5e 142 (d depth (1- d))
ac99cb0c 143 (n 0 (+ n e.nexts)))
17e90c5e 144 ((= d 0)
f0c99935
KN
145 (if (eq? op 'ref)
146 (push-code! `(external-ref ,(+ n index)))
147 (push-code! `(external-set ,(+ n index)))))))
17e90c5e
KN
148
149 (($ <glil-module> op module name)
ac99cb0c 150 (push-object! (<vlink> :module #f :name name))
f0c99935
KN
151 (if (eq? op 'ref)
152 (push-code! '(variable-ref))
153 (push-code! '(variable-set))))
17e90c5e
KN
154
155 (($ <glil-label> label)
ac99cb0c 156 (set! label-alist (assq-set! label-alist label (current-address))))
17e90c5e
KN
157
158 (($ <glil-branch> inst label)
41f248a8 159 (set! stack (cons (list inst label) stack)))
17e90c5e 160
46cd9a34 161 (($ <glil-call> inst nargs)
17e90c5e 162 (if (instruction? inst)
46cd9a34
KN
163 (let ((pops (instruction-pops inst)))
164 (cond ((< pops 0)
165 (push-code! (list inst nargs)))
166 ((= pops nargs)
167 (push-code! (list inst)))
168 (else
169 (error "Wrong number of arguments:" inst nargs))))
17e90c5e
KN
170 (error "Unknown instruction:" inst)))))
171 ;;
172 ;; main
17e90c5e 173 (for-each generate-code body)
2d80426a 174; (format #t "codegen: stack = ~a~%" (reverse stack))
41f248a8
KN
175 (let ((bytes (stack->bytes (reverse! stack) label-alist)))
176 (if toplevel
ac99cb0c
KN
177 (bytecode->objcode bytes vars.nlocs vars.nexts)
178 (<bytespec> :vars vars :bytes bytes
af988bbf 179 :meta (if (and (null? binding-alist)
ac99cb0c
KN
180 (null? source-alist))
181 #f
af988bbf 182 (cons (reverse! binding-alist)
ac99cb0c
KN
183 (reverse! source-alist)))
184 :objs (let ((objs (map car (reverse! object-alist))))
185 (if (null? objs) #f (list->vector objs)))
186 :closure? venv.closure?)))))))
17e90c5e 187
880ed584 188(define (object-assoc x alist)
ac99cb0c
KN
189 (match x
190 (($ <vlink>) (assoc x alist))
191 (else (assq x alist))))
880ed584 192
41f248a8 193(define (stack->bytes stack label-alist)
880ed584 194 (let loop ((result '()) (stack stack) (addr 0))
206a0622 195 (if (null? stack)
054599f1
LC
196 (apply u8vector
197 (apply append
198 (map u8vector->list (reverse! result))))
41f248a8
KN
199 (let ((bytes (car stack)))
200 (if (pair? bytes)
201 (let* ((offset (- (assq-ref label-alist (cadr bytes))
202 (+ addr 3)))
203 (n (if (< offset 0) (+ offset 65536) offset)))
204 (set! bytes (code->bytes (list (car bytes)
205 (quotient n 256)
206 (modulo n 256))))))
207 (loop (cons bytes result)
208 (cdr stack)
054599f1 209 (+ addr (u8vector-length bytes)))))))
17e90c5e
KN
210
211\f
212;;;
4bfb26f5 213;;; Object dump
17e90c5e
KN
214;;;
215
23b587b0 216;; NOTE: undumpped in vm_system.c
17e90c5e 217
f0c99935 218(define (dump-object! push-code! x)
23b587b0
LC
219 (define (too-long x)
220 (error (string-append x " too long")))
221
bd098a1a
KN
222 (let dump! ((x x))
223 (cond
224 ((object->code x) => push-code!)
ac99cb0c 225 (else
4bfb26f5 226 (match x
ac99cb0c 227 (($ <bytespec> vars bytes meta objs closure?)
4bfb26f5 228 ;; dump parameters
ac99cb0c
KN
229 (let ((nargs vars.nargs) (nrest vars.nrest)
230 (nlocs vars.nlocs) (nexts vars.nexts))
231 (cond
232 ((and (< nargs 4) (< nlocs 8) (< nexts 4))
233 ;; 8-bit representation
234 (let ((x (+ (* nargs 64) (* nrest 32) (* nlocs 4) nexts)))
235 (push-code! `(make-int8 ,x))))
236 ((and (< nargs 16) (< nlocs 128) (< nexts 16))
237 ;; 16-bit representation
238 (let ((x (+ (* nargs 4096) (* nrest 2048) (* nlocs 16) nexts)))
239 (push-code! `(make-int16 ,(quotient x 256) ,(modulo x 256)))))
240 (else
241 ;; Other cases
242 (push-code! (object->code nargs))
243 (push-code! (object->code nrest))
244 (push-code! (object->code nlocs))
245 (push-code! (object->code nexts))
246 (push-code! (object->code #f)))))
4bfb26f5 247 ;; dump object table
ac99cb0c
KN
248 (if objs (dump! objs))
249 ;; dump meta data
250 (if meta (dump! meta))
4bfb26f5 251 ;; dump bytecode
ac99cb0c
KN
252 (push-code! `(load-program ,bytes)))
253 (($ <vlink> module name)
254 ;; FIXME: dump module
fa19602c 255 (push-code! `(link ,(symbol->string name))))
ac99cb0c
KN
256 (($ <vmod> id)
257 (push-code! `(load-module ,id)))
258 ((and ($ integer) ($ exact))
259 (let ((str (do ((n x (quotient n 256))
260 (l '() (cons (modulo n 256) l)))
261 ((= n 0)
054599f1 262 (apply u8vector l)))))
ac99cb0c
KN
263 (push-code! `(load-integer ,str))))
264 (($ number)
fa19602c 265 (push-code! `(load-number ,(number->string x))))
ac99cb0c 266 (($ string)
238e7a11 267 (push-code! `(load-string ,x)))
ac99cb0c 268 (($ symbol)
fa19602c 269 (push-code! `(load-symbol ,(symbol->string x))))
ac99cb0c
KN
270 (($ keyword)
271 (push-code! `(load-keyword
fa19602c 272 ,(symbol->string (keyword-dash-symbol x)))))
ac99cb0c
KN
273 (($ list)
274 (for-each dump! x)
23b587b0
LC
275 (let ((len (length x)))
276 (if (>= len 65536) (too-long 'list))
277 (push-code! `(list ,(quotient len 256) ,(modulo len 256)))))
ac99cb0c
KN
278 (($ pair)
279 (dump! (car x))
280 (dump! (cdr x))
281 (push-code! `(cons)))
282 (($ vector)
283 (for-each dump! (vector->list x))
23b587b0
LC
284 (let ((len (vector-length x)))
285 (if (>= len 65536) (too-long 'vector))
286 (push-code! `(vector ,(quotient len 256) ,(modulo len 256)))))
ac99cb0c 287 (else
884d46de 288 (error "assemble: unrecognized object" x)))))))