File: assembler.jl

package info (click to toggle)
librep 0.92.7-4.1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,200 kB
  • sloc: ansic: 31,304; lisp: 11,265; sh: 3,594; makefile: 456; sed: 93
file content (214 lines) | stat: -rw-r--r-- 5,895 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#| assembler.jl -- higher-level assembler

   $Id$

   Copyright (C) 2000 John Harper <john@dcs.warwick.ac.uk>

   This file is part of librep.

   librep is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   librep is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with librep; see the file COPYING.  If not, write to
   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA
|#

;; The plan is to use in the compiler at some point to remove the ugly
;; lap code representation, instead compile to assembly language, then
;; assemble that.. (with the peephole pass in between)

(define-structure rep.vm.assembler

    (export assemble)

    (open rep
	  rep.vm.bytecodes
	  rep.data.tables
	  rep.data.records)

  (define-record-type :label
    (make-label name)
    labelp
    (name label-name label-name-set)
    (address label-address label-address-set)
    (forwards label-forwards label-forwards-set))

  ;; Syntax of INSNS is a list of `(INSN [ARG])' or `LABEL'. One pseudo
  ;; insn: `(push-label LABEL)'

  ;; Example:

  ;; ((push 0)
  ;;  foo
  ;;  (push 1)
  ;;  (add)
  ;;  (jmp foo))

  ;; Returns (BYTE-CODE-VECTOR . CONSTANT-VECTOR)

  (define (assemble insns #!optional start)
    (let ((code '())
	  (pc (or start 0))
	  (labels (make-table symbol-hash eq))
	  (constants '())
	  (next-const-id 0))

      (define (get-label name)
	(or (table-ref labels name)
	    (let ((l (make-label name)))
	      (table-set labels name l)
	      l)))

      (define (get-const-id value)
	(or (cdr (assoc value constants))
	    (prog1 next-const-id
	      (setq constants (cons (cons value next-const-id) constants))
	      (setq next-const-id (1+ next-const-id)))))

      (define (emit-byte-at byte addr)
	(setq code (cons (cons byte addr) code)))

      (define (emit-byte byte)
	(emit-byte-at byte pc)
	(setq pc (1+ pc)))

      (define (emit-address-at addr pc)
	(emit-byte-at (ash addr -8) pc)
	(emit-byte-at (logand addr 255) (1+ pc)))

      (define (emit-address addr)
	(emit-address-at addr pc)
	(setq pc (+ pc 2)))

      (define (emit-label-addr label)
	(if (label-address label)
	    (emit-address (label-address label))
	  (label-forwards-set label (cons pc (label-forwards label)))
	  (setq pc (+ pc 2))))

      (define (emit-insn insn #!optional arg)
	(let ((op (bytecode-ref insn)))
	  (if (>= op (bytecode last-with-args))
	      (progn
		;; ``normal'' one-byte insn encoding
		(emit-byte op)
		(when arg
		  (cond ((memq op byte-two-byte-insns)
			 (if (< arg 256)
			     (emit-byte arg)
			   (error
			    "Argument overflow in two-byte insn: %s" insn)))

			((memq op byte-three-byte-insns)
			 (if (< arg 65536)
			     (progn
			       (emit-byte (ash arg -8))
			       (emit-byte (logand arg 255)))
			   (error
			    "Argument overflow in three-byte insn: %s" insn)))

			(t (error "Spurious argument to insn: %s" insn)))))

	    ;; insn with embedded argument
	    (cond ((<= arg byte-max-1-byte-arg)
		   (emit-byte (+ op arg)))

		  ((<= arg byte-max-2-byte-arg)
		   (emit-byte (+ op 6))
		   (emit-byte arg))

		  ((<= arg byte-max-3-byte-arg)
		   (emit-byte (+ op 7))
		   (emit-byte (ash arg -8))
		   (emit-byte (logand arg 255)))

		  (t (error "Argument overflow in insn: %s" insn))))))

      (define (emit-jmp insn dest)
	(emit-byte (bytecode-ref insn))
	(emit-label-addr (get-label dest)))

      (define (emit-push arg)
	(cond ((and (fixnump arg) (<= arg 65535) (>= arg -65535))
	       (cond ((zerop arg)
		      (emit-insn 'pushi-0))

		     ((= arg 1)
		      (emit-insn 'pushi-1))

		     ((= arg 2)
		      (emit-insn 'pushi-2))

		     ((= arg -1)
		      (emit-insn 'pushi-minus-1))

		     ((= arg -2)
		      (emit-insn 'pushi-minus-2))

		     ((and (<= arg 127) (>= arg -128))
		      (emit-insn 'pushi (logand arg 255)))

		     ((and (< arg 0) (>= arg -65535))
		      (emit-insn 'pushi-pair-neg (- arg)))

		     (t (emit-insn 'pushi-pair-pos arg))))

	      ((eq arg '()) (emit-insn 'nil))
	      ((eq arg 't) (emit-insn 't))

	      (t (emit-insn 'push (get-const-id arg)))))

      (define (emit-push-label arg)
	;; push address of label
	(emit-byte (bytecode pushi-pair-pos))
	(emit-label-addr (get-label arg)))
	      
      (define (emit-label name)
	(let ((label (get-label name)))
	  (and (label-address label)
	       (error "Multiply-defined label: %s, %s" name insns))
	  (label-address-set label pc)
	  ;; backpatch forward references
	  (do ((refs (label-forwards label) (cdr refs)))
	      ((null refs) (label-forwards-set label '()))
	    (emit-byte-at (ash pc -8) (car refs))
	    (emit-byte-at (logand pc 255) (1+ (car refs))))))
			  
      (let loop ((rest insns))
	(when rest
	  (let ((insn (car rest)))
	    (cond ((symbolp insn) (emit-label insn))

		  ((eq (car insn) 'push) (emit-push (cadr insn)))

		  ((eq (car insn) 'push-label) (emit-push-label (cadr insn)))

		  ((memq (car insn) '(refg setg))
		   ;; instruction with constant
		   (emit-insn (car insn) (get-const-id (cadr insn))))

		  ((memq (car insn) byte-jmp-insns)
		   (emit-jmp (car insn) (cadr insn)))

		  (t (apply emit-insn insn)))
	    (loop (cdr rest)))))

      (let ((byte-vec (make-string pc))
	    (const-vec (make-vector next-const-id)))
	(do ((rest code (cdr rest)))
	    ((null rest))
	  (aset byte-vec (cdar rest) (caar rest)))
	(do ((rest constants (cdr rest)))
	    ((null rest))
	  (aset const-vec (cdar rest) (caar rest)))

	(cons byte-vec const-vec)))))