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) | |
23 | :use-syntax (system base syntax) | |
17e90c5e KN |
24 | :use-module (system il glil) |
25 | :use-module (system vm core) | |
26 | :use-module (system vm conv) | |
27 | :use-module (ice-9 match) | |
28 | :use-module (ice-9 regex) | |
29 | :use-module (ice-9 common-list) | |
30 | :export (assemble)) | |
31 | ||
32 | (define (assemble glil env . opts) | |
33 | (dump (codegen (preprocess glil #f) #t))) | |
34 | ||
35 | \f | |
36 | ;;; | |
37 | ;;; Types | |
38 | ;;; | |
39 | ||
40 | (define-structure (<vm-asm> venv glil body)) | |
41 | (define-structure (venv parent nexts closure?)) | |
42 | (define-structure (vmod id)) | |
43 | (define-structure (vlink module name)) | |
44 | (define-structure (bytespec nargs nrest nlocs bytes objs)) | |
45 | ||
46 | \f | |
47 | ;;; | |
48 | ;;; Stage 1: Preprocess | |
49 | ;;; | |
50 | ||
51 | (define (preprocess x e) | |
52 | (match x | |
53 | (($ <glil-asm> nargs nrest nlocs nexts body) | |
54 | (let* ((venv (make-venv e nexts #f)) | |
55 | (body (map (lambda (x) (preprocess x venv)) body))) | |
56 | (make-<vm-asm> venv x body))) | |
57 | (($ <glil-external> op depth index) | |
58 | (do ((d depth (1- d)) | |
59 | (e e (venv-parent e))) | |
60 | ((= d 0)) | |
61 | (set-venv-closure?! e #t)) | |
62 | x) | |
63 | (else x))) | |
64 | ||
65 | \f | |
66 | ;;; | |
67 | ;;; Stage 2: Bytecode generation | |
68 | ;;; | |
69 | ||
70 | (define (codegen glil toplevel) | |
71 | (match glil | |
72 | (($ <vm-asm> venv ($ <glil-asm> nargs nrest nlocs nexts _) body) | |
73 | (let ((stack '()) | |
74 | (label-alist '()) | |
75 | (object-alist '()) | |
76 | (nvars (+ nargs nlocs -1))) | |
77 | (define (current-address) (length stack)) | |
78 | (define (push-code! code) | |
79 | (set! stack (optimizing-push code stack))) | |
80 | (define (object-index obj) | |
81 | (cond ((assq-ref object-alist obj)) | |
82 | (else (let ((index (length object-alist))) | |
83 | (set! object-alist (acons obj index object-alist)) | |
84 | index)))) | |
85 | (define (label-ref key) | |
86 | (assq-ref label-alist key)) | |
87 | (define (label-set key pos) | |
88 | (set! label-alist (assq-set! label-alist key pos))) | |
89 | (define (generate-code x) | |
90 | (match x | |
91 | (($ <vm-asm> env) | |
92 | (push-code! `(object-ref ,(object-index (codegen x #f)))) | |
93 | (if (venv-closure? env) (push-code! `(make-closure)))) | |
94 | ||
95 | (($ <glil-void>) | |
96 | (push-code! `(void))) | |
97 | ||
98 | (($ <glil-const> x) | |
99 | (if toplevel | |
100 | (for-each push-code! (object->dump-code x)) | |
101 | (cond ((object->code x) => push-code!) | |
102 | (else (push-code! `(object-ref ,(object-index x))))))) | |
103 | ||
104 | (($ <glil-argument> op index) | |
105 | (push-code! (list (symbol-append 'local- op) | |
106 | (- nvars index)))) | |
107 | ||
108 | (($ <glil-local> op index) | |
109 | (push-code! (list (symbol-append 'local- op) | |
110 | (- nvars (+ nargs index))))) | |
111 | ||
112 | (($ <glil-external> op depth index) | |
113 | (do ((e venv (venv-parent e)) | |
114 | (d depth (1- d)) | |
115 | (i 0 (+ i (venv-nexts e)))) | |
116 | ((= d 0) | |
117 | (push-code! (list (symbol-append 'external- op) | |
118 | (+ index i)))))) | |
119 | ||
120 | (($ <glil-module> op module name) | |
d4ae3ae6 KN |
121 | (if toplevel |
122 | (begin | |
123 | ;; (push-code! `(load-module ,module)) | |
124 | (push-code! `(load-symbol ,name)) | |
125 | (push-code! `(link/current-module))) | |
126 | ;; (let ((vlink (make-vlink (make-vmod module) name))) | |
127 | (let ((vlink (make-vlink #f name))) | |
128 | (push-code! `(object-ref ,(object-index vlink))))) | |
129 | (push-code! (list (symbol-append 'variable- op)))) | |
17e90c5e KN |
130 | |
131 | (($ <glil-label> label) | |
132 | (label-set label (current-address))) | |
133 | ||
134 | (($ <glil-branch> inst label) | |
135 | (let ((setter (lambda (addr) (- (label-ref label) (1+ addr))))) | |
136 | (push-code! (list inst setter)))) | |
137 | ||
46cd9a34 | 138 | (($ <glil-call> inst nargs) |
17e90c5e | 139 | (if (instruction? inst) |
46cd9a34 KN |
140 | (let ((pops (instruction-pops inst))) |
141 | (cond ((< pops 0) | |
142 | (push-code! (list inst nargs))) | |
143 | ((= pops nargs) | |
144 | (push-code! (list inst))) | |
145 | (else | |
146 | (error "Wrong number of arguments:" inst nargs)))) | |
17e90c5e KN |
147 | (error "Unknown instruction:" inst))))) |
148 | ;; | |
149 | ;; main | |
150 | (if (> nexts 0) (push-code! `(external ,nexts))) | |
151 | (for-each generate-code body) | |
152 | (let ((bytes (code->bytes | |
153 | (map/index (lambda (v n) (if (procedure? v) (v n) v)) | |
154 | (reverse! stack)))) | |
155 | (objs (map car (reverse! object-alist)))) | |
156 | (make-bytespec nargs nrest nlocs bytes objs)))))) | |
157 | ||
158 | (define (map/index f l) | |
159 | (do ((n 0 (1+ n)) | |
160 | (l l (cdr l)) | |
161 | (r '() (cons (f (car l) n) r))) | |
162 | ((null? l) (reverse! r)))) | |
163 | ||
164 | ;; Optimization | |
165 | ||
166 | (define *optimize-table* | |
167 | '((not (not . not-not) | |
168 | (eq? . not-eq?) | |
169 | (null? . not-null?) | |
170 | (not-not . not) | |
171 | (not-eq? . eq?) | |
172 | (not-null? . null?)) | |
173 | (br-if (not . br-if-not) | |
174 | (eq? . br-if-eq) | |
175 | (null? . br-if-null) | |
176 | (not-not . br-if) | |
177 | (not-eq? . br-if-not-eq) | |
178 | (not-null? . br-if-not-null)) | |
179 | (br-if-not (not . br-if) | |
180 | (eq? . br-if-not-eq) | |
181 | (null? . br-if-not-null) | |
182 | (not-not . br-if-not) | |
183 | (not-eq? . br-if-eq) | |
184 | (not-null? . br-if-null)))) | |
185 | ||
186 | (define (optimizing-push code stack) | |
187 | (let ((alist (assq-ref *optimize-table* (car code)))) | |
188 | (cond ((and alist (pair? stack) (assq-ref alist (car stack))) => | |
189 | (lambda (inst) (append! (reverse! (cons inst (cdr code))) | |
190 | (cdr stack)))) | |
191 | (else (append! (reverse! (code-finalize code)) stack))))) | |
192 | ||
193 | \f | |
194 | ;;; | |
195 | ;;; Stage3: Dumpcode generation | |
196 | ;;; | |
197 | ||
198 | (define (dump bytespec) | |
199 | (let* ((table (build-object-table bytespec)) | |
200 | (bytes (bytespec->bytecode bytespec table '(return)))) | |
201 | (if (null? table) | |
202 | bytes | |
203 | (let ((spec (make-bytespec 0 0 (length table) bytes '()))) | |
204 | (bytespec->bytecode spec '() '(tail-call 0)))))) | |
205 | ||
206 | (define (bytespec->bytecode bytespec object-table last-code) | |
207 | (let ((stack '())) | |
208 | (define (push-code! x) | |
209 | (set! stack (cons x stack))) | |
210 | (define (object-index x) | |
211 | (cond ((object-find object-table x) => cdr) | |
212 | (else #f))) | |
213 | (define (dump-table-object! obj+index) | |
214 | (let dump! ((x (car obj+index))) | |
215 | (cond | |
216 | ((vlink? x) | |
fdcedea6 | 217 | ;; (push-code! `(local-ref ,(object-index (vlink-module x)))) |
17e90c5e | 218 | (push-code! `(load-symbol ,(vlink-name x))) |
fdcedea6 | 219 | (push-code! `(link/current-module))) |
17e90c5e KN |
220 | ((vmod? x) |
221 | (push-code! `(load-module ,(vmod-id x)))) | |
222 | (else | |
223 | (for-each push-code! (object->dump-code x))))) | |
224 | (push-code! `(local-set ,(cdr obj+index)))) | |
225 | (define (dump-object! x) | |
226 | (let dump! ((x x)) | |
227 | (cond | |
228 | ((bytespec? x) (dump-bytecode! x)) | |
229 | ((object-index x) => (lambda (i) (push-code! `(local-ref ,i)))) | |
230 | (else | |
231 | (error "Cannot dump:" x))))) | |
232 | (define (dump-bytecode! spec) | |
233 | (let ((nargs (bytespec-nargs spec)) | |
234 | (nrest (bytespec-nrest spec)) | |
235 | (nlocs (bytespec-nlocs spec)) | |
236 | (objs (bytespec-objs spec))) | |
237 | (if (and (null? objs) (< nargs 4) (< nlocs 16)) | |
238 | ;; zero-object encoding | |
239 | (push-code! (object->code (+ (* nargs 32) (* nrest 16) nlocs))) | |
240 | (begin | |
241 | ;; dump parameters | |
242 | (push-code! (object->code nargs)) | |
243 | (push-code! (object->code nrest)) | |
244 | (push-code! (object->code nlocs)) | |
245 | ;; dump object table | |
246 | (cond ((null? objs) (push-code! (object->code #f))) | |
247 | (else | |
17e90c5e | 248 | (for-each dump-object! objs) |
46cd9a34 | 249 | (push-code! `(vector ,(length objs))))))) |
17e90c5e KN |
250 | ;; dump bytecode |
251 | (push-code! `(load-program ,(bytespec-bytes spec))))) | |
252 | ;; | |
253 | ;; main | |
254 | (for-each dump-table-object! object-table) | |
255 | (dump-bytecode! bytespec) | |
256 | (push-code! last-code) | |
257 | (code->bytes (apply append! (map code-finalize (reverse! stack)))))) | |
258 | ||
259 | ;; object table | |
260 | ||
261 | (define (object-find table x) | |
262 | ((if (or (vlink? x) (vmod? x)) assoc assq) x table)) | |
263 | ||
264 | (define (build-object-table bytespec) | |
265 | (let ((table '()) (index 0)) | |
266 | (define (insert! x) | |
d4ae3ae6 | 267 | ;; (if (vlink? x) (begin (insert! (vlink-module x)))) |
17e90c5e KN |
268 | (if (not (object-find table x)) |
269 | (begin | |
270 | (set! table (acons x index table)) | |
271 | (set! index (1+ index))))) | |
272 | (let loop ((spec bytespec)) | |
273 | (for-each (lambda (x) | |
274 | (if (bytespec? x) (loop x) (insert! x))) | |
275 | (bytespec-objs spec))) | |
276 | (reverse! table))) | |
277 | ||
278 | ;; code generation | |
279 | ||
280 | (define (code-finalize code) | |
281 | (match code | |
282 | ((inst (? symbol? s)) | |
ea9b4b29 KN |
283 | (let ((s (symbol->string s))) |
284 | `(,inst ,(string-length s) ,s))) | |
17e90c5e KN |
285 | ((inst (? string? s)) |
286 | `(,inst ,(string-length s) ,s)) | |
287 | (else (code-pack code)))) | |
288 | ||
289 | (define (integer->string n) (make-string 1 (integer->char n))) | |
290 | ||
291 | (define (length->string len) | |
292 | (define C integer->char) | |
293 | (list->string | |
294 | (cond ((< len 254) (list (C len))) | |
295 | ((< len 65536) | |
296 | (list (C 254) (C (quotient len 256)) (C (modulo len 256)))) | |
297 | ((< len most-positive-fixnum) | |
298 | (list (C 255) | |
299 | (C (quotient len (* 256 256 256))) | |
300 | (C (modulo (quotient len (* 256 256)) 256)) | |
301 | (C (modulo (quotient len 256) 256)) | |
302 | (C (modulo len 256)))) | |
303 | (else (error "Too long" len))))) | |
304 | ||
305 | (define (code->bytes code) | |
306 | (let* ((code (list->vector code)) | |
307 | (size (vector-length code))) | |
308 | (let loop ((i 0)) | |
309 | (if (>= i size) | |
310 | (apply string-append (vector->list code)) | |
311 | (let ((inst (vector-ref code i))) | |
312 | (if (not (instruction? inst)) | |
313 | (error "Unknown instruction:" inst)) | |
314 | (vector-set! code i (integer->string (instruction->opcode inst))) | |
315 | (let ((bytes (instruction-length inst))) | |
316 | (cond ((< bytes 0) | |
317 | (vector-set! code i | |
318 | (integer->string (instruction->opcode inst))) | |
319 | (vector-set! code (+ i 1) | |
320 | (length->string (vector-ref code (1+ i)))) | |
321 | (loop (+ i 3))) | |
322 | ((= bytes 0) (loop (+ i 1))) | |
323 | (else | |
324 | (let ((end (+ i 1 bytes))) | |
325 | (do ((j (+ i 1) (1+ j))) | |
326 | ((= j end) (loop end)) | |
327 | (vector-set! code j (integer->string | |
328 | (vector-ref code j))))))))))))) |