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 '()))
|