File: segment.scm

package info (click to toggle)
scsh-0.6 0.6.7-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 15,124 kB
  • ctags: 16,788
  • sloc: lisp: 82,839; ansic: 23,112; sh: 3,116; makefile: 829
file content (331 lines) | stat: -rw-r--r-- 10,421 bytes parent folder | download | duplicates (6)
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.


; The byte code compiler's assembly phase.

(define make-segment cons)
(define segment-size car);number of bytes that will be taken in the code vector
(define segment-emitter cdr)

(define (segment->template segment name pc-in-parent parent-data)
  (let* ((cv (make-code-vector (segment-size segment) 0))
	 (astate (make-astate cv))
	 (name (if (if (string? name)	; only files have strings for names
		       (keep-file-names?)
		       (keep-procedure-names?))
		   name
		   #f))
	 (debug-data (new-debug-data name parent-data pc-in-parent)))
    (let-fluid $debug-data debug-data
      (lambda ()
	(let* ((maps (emit-with-environment-maps! astate segment))
	       (cv (check-stack-use cv)))
	  (set-debug-data-env-maps! debug-data maps)
	  (make-immutable! cv)
	  (segment-data->template cv
				  (debug-data->info debug-data)
				  (reverse (astate-literals astate))))))))

(define (segment-data->template cv debug-data literals)
  (let ((template (make-template (+ template-overhead (length literals)) 0)))
    (set-template-code! template cv)
    (set-template-info! template debug-data)
    (do ((lits literals (cdr lits))
	 (i template-overhead (+ i 1)))
	((null? lits) template)
      (template-set! template i (car lits)))
    template))

; If CV needs more than the default allotment of stack space replace its
; protocol with one that checks that the needed space is available.  The
; original protocol is preserved at the end of the new code vector (to
; preserve the debugging indicies into the original).

(define (check-stack-use cv)
  (let ((uses (maximum-stack-use cv)))
    (cond ((<= uses default-stack-space)
	   cv)
	  ((<= uses available-stack-space)
	   (let* ((length (code-vector-length cv))
		  (new (make-code-vector (+ length 3) 0)))
	     (do ((i 0 (+ i 1)))
		 ((= i length))
	       (code-vector-set! new i (code-vector-ref cv i)))
	     (code-vector-set! new length (code-vector-ref cv 1))
	     (code-vector-set! new 1 big-stack-protocol)
	     (code-vector-set2! new (+ length 1) uses)
	     new))
	  (else
	   (error "VM limit exceeded: procedure requires too much stack space")))))

; "astate" is short for "assembly state"

(define-record-type assembly-state :assembly-state
  (make-assembly-state cv pc count lits)
  (cv    astate-code-vector)
  (pc    astate-pc    set-astate-pc!)
  (count astate-count set-astate-count!)
  (lits  astate-literals  set-astate-literals!))

(define (make-astate cv)
  (make-assembly-state cv 0 template-overhead '()))

(define (emit-byte! a byte)
  (code-vector-set! (astate-code-vector a) (astate-pc a) byte)
  (set-astate-pc! a (+ (astate-pc a) 1)))

(define (literal->index a thing)
  (let ((probe (literal-position thing (astate-literals a)))
	(count (astate-count a)))
    (if probe
	;; +++  Eliminate duplicate entries.
	;; Not necessary, just a modest space saver [how much?].
	;; Measurably slows down compilation.
	;; when 1 thing, lits = (x), count = 3, probe = 0, want 2
	(- (- count probe) 1)
	(begin
	  (if (>= count two-byte-limit)
	      (error "compiler bug: too many literals"
		     thing))
	  (set-astate-literals! a (cons thing (astate-literals a)))
	  (set-astate-count! a (+ count 1))
	  count))))

(define (literal-position thing literals)
  (position (if (thingie? thing)
		(lambda (thing other-thing)
		  (and (thingie? other-thing)
		       (equal? (thingie-name thing)
			       (thingie-name other-thing))))
		equal?)
	    thing
	    literals))

(define (position pred elt list)
  (let loop ((i 0) (l list))
    (cond ((null? l)
	   #f)
	  ((pred elt (car l))
	   i)
	  (else
	   (loop (+ i 1) (cdr l))))))

(define (emit-literal! a thing)
  (let ((index (literal->index a thing)))
    (emit-byte! a (high-byte index))
    (emit-byte! a (low-byte index))))

(define (emit-segment! astate segment)
  ((segment-emitter segment) astate))

; Segment constructors

(define empty-segment
  (make-segment 0 (lambda (astate) #f)))

(define (instruction opcode . operands)
  (make-segment (+ 1 (length operands))
		(lambda (astate)
		  (emit-byte! astate opcode)
		  (for-each (lambda (operand)
			      (emit-byte! astate operand))
			    operands))))

(define (sequentially . segments)
  ;;  (reduce sequentially-2 empty-segment segments)
  ;;+++ this sped the entire compilation process up by several percent
  (cond ((null? segments)
         empty-segment)
        ((null? (cdr segments))
         (car segments))
        ((null? (cddr segments))
         (sequentially-2 (car segments) (cadr segments)))
        (else
         (make-segment (let loop ((segs segments) (s 0))
                         (if (null? segs)
                             s
                             (loop (cdr segs) (+ s (segment-size (car segs))))))
                       (lambda (astate)
                         (let loop ((segs segments))
                           (if (not (null? segs))
                               (begin (emit-segment! astate (car segs))
                                      (loop (cdr segs))))))))))

(define (sequentially-2 seg1 seg2)
  (cond ((eq? seg1 empty-segment) seg2) ;+++ speed up the compiler a tad
	((eq? seg2 empty-segment) seg1) ;+++
	(else
	 (make-segment (+ (segment-size seg1)
			  (segment-size seg2))
		       (lambda (astate)
			 (emit-segment! astate seg1)
			 (emit-segment! astate seg2)))))) ;tail call

; Literals are obtained from the template.

(define (instruction-with-literal opcode thing . operands)
  (make-segment (+ 3 (length operands))
		(lambda (astate)
		  (let ((index (literal->index astate thing)))
		    (if (and (= opcode (enum op literal))
                             (< index byte-limit))
			(begin
			  (emit-byte! astate (enum op small-literal))
			  (emit-byte! astate index)
			  (emit-byte! astate 0))
			(begin
			  (emit-byte! astate opcode)
			  (emit-byte! astate (high-byte index))
			  (emit-byte! astate (low-byte index))))
		    (for-each (lambda (operand)
				(emit-byte! astate operand))
			      operands)))))

; So are locations.

(define (instruction-with-location opcode binding name want-type)
  (make-segment 3
		(lambda (astate)
		  (emit-byte! astate opcode)
		  (emit-literal! astate (make-thingie binding name want-type)))))

; Templates for inferior closures are also obtained from the
; (parent's) template.

(define (template segment name)
  (make-segment 2
  		(lambda (astate)
  		  (emit-literal! astate
  				 (segment->template segment
  						    name
						    (astate-pc astate)
						    (fluid $debug-data))))))

; Labels.  Each label maintains a list of pairs (location . origin).
; Instr is the index of the first of two bytes that will hold the jump
; target offset, and the offset stored will be (- jump-target origin).
;
; The car of a forward label is #F, the car of a backward label is the
; label's PC.

(define (make-label) (list #f))

(define (instruction-using-label opcode label . rest)
  (let ((segment (apply instruction opcode 0 0 rest)))
    (make-segment (segment-size segment)
		  (lambda (astate)
		    (let* ((origin (astate-pc astate))
			   (location (+ origin 1)))
		      (emit-segment! astate segment)
		      (if (car label)
			  (insert-label! (astate-code-vector astate)
					 location
					 (- (car label) origin))
			  (set-cdr! label
				    (cons (cons location origin)
					  (cdr label)))))))))

; computed-goto
; # of labels
; label0
; label1
; ...

(define computed-goto-label-size 2)

(define (computed-goto-instruction labels)
  (let* ((count (length labels))
	 (segment (instruction (enum op computed-goto) count)))
    (make-segment (+ (segment-size segment)
		     (* count computed-goto-label-size))
		  (lambda (astate)
		    (let ((base-address (astate-pc astate)))
		      (emit-segment! astate segment)
		      (set-astate-pc! astate
				      (+ (astate-pc astate)
					 (* count computed-goto-label-size)))
		      (do ((location (+ base-address 2)
				     (+ location computed-goto-label-size))
			   (labels labels (cdr labels)))
			  ((null? labels))
			(let ((label (car labels)))
			  (if (car label)
			      (warn "backward jumps not supported")
			      (set-cdr! label
					(cons (cons location base-address)
					      (cdr label)))))))))))

; LABEL is the label for SEGMENT.  The current PC is used as the value of LABEL.

(define (attach-label label segment)
  (make-segment
     (segment-size segment)
     (lambda (astate)
       (let ((pc (astate-pc astate))
	     (cv (astate-code-vector astate)))
	 (for-each (lambda (instr+origin)
		     (insert-label! cv
				    (car instr+origin)
				    (- pc (cdr instr+origin))))
		   (cdr label))
	 (set-car! label pc)
	 (emit-segment! astate segment)))))

(define (insert-label! cv location offset)
  (code-vector-set2! cv location offset))

(define (code-vector-set2! cv i value)
  (code-vector-set! cv i       (high-byte value))
  (code-vector-set! cv (+ i 1) (low-byte  value)))

(define two-byte-limit (expt 2 (* 2 bits-used-per-byte)))

(define (high-byte n)
  (quotient n byte-limit))

(define (low-byte n)
  (remainder n byte-limit))

; Special segments for maintaining debugging information.  Not
; essential for proper functioning of compiler.

(define $debug-data (make-fluid #f))

; Keep track of source code at continuations.

(define (note-source-code info segment)
  (make-segment (segment-size segment)
		(lambda (astate)
		  (emit-segment! astate segment)
		  (let ((dd (fluid $debug-data)))
		    (set-debug-data-source!
		     dd
		     (cons (cons (astate-pc astate) info)
			   (debug-data-source dd)))))))

; Keep track of variable names from lexical environments.
; Each environment map has the form
;    #(pc-before pc-after (var ...) (env-map ...))

(define (note-environment vars segment)
  (if (keep-environment-maps?)
      (make-segment (segment-size segment)
		    (lambda (astate)
		      (let* ((pc-before (astate-pc astate))
			     (env-maps
			      (emit-with-environment-maps! astate segment)))
			(set-fluid! $environment-maps
				    (cons (vector pc-before
						  (astate-pc astate)
						  (list->vector vars)
						  env-maps)
					  (fluid $environment-maps))))))
      segment))

(define (emit-with-environment-maps! astate segment)
  (let-fluid $environment-maps '()
    (lambda ()
      (emit-segment! astate segment)
      (fluid $environment-maps))))

(define $environment-maps (make-fluid '()))