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
|
; Part of Scheme 48 1.9. See file COPYING for notices and license.
; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
; We only do flat lambdas now.
(define-compilator 'lambda syntax-type
(lambda (node depth frame cont)
(generate-trap depth
frame
cont
"cannot compile non-flat lambda")))
;----------------------------------------------------------------
; (flat-lambda (id ...) (free-id ...) body)
(define-compilator 'flat-lambda syntax-type
(lambda (node depth frame cont)
(let ((exp (node-form node))
(name (cont-name cont)))
(let ((vars (cadr exp))
(free (caddr exp))
(body (cadddr exp)))
(deliver-value (compile-flat-lambda name vars body free depth frame)
cont)))))
; The MAKE-FLAT-ENV instruction is designed to allow us to make nested flat
; environments (i.e. flat environments consisting of a linked chain of vectors)
; but this code doesn't generate them.
;
; We could sort out the two-byte offsets and make a separate big-flat-env that
; becomes the superior env of the regular flat env (instead of the #f that is
; there now).
(define (compile-flat-lambda name vars body free depth frame)
(receive (env-code free-vars)
(if (null? free) ; avoid ENVIRONMENT-OFFSET if no env
(values (instruction (enum op false)) '())
(compile-environment free (+ depth 1)))
(sequentially
(really-compile-flat-lambda name vars body free-vars depth frame)
env-code
(instruction (enum op make-stored-object) 2 (enum stob closure)))))
; Save the current locations of the free variables, compile the lambda, and
; then restore the old locations.
(define (really-compile-flat-lambda name vars body free depth frame)
(let ((old-locations (map name-node-binding free)))
(receive (proc-code proc-frame)
(compile-lambda `(lambda ,vars ,body)
free
name
#f
frame)
(for-each (lambda (node location)
(node-set! node 'binding location))
free
old-locations)
(let ((template (segment->template proc-code proc-frame)))
(let ((offset (template-offset frame depth))
(index (literal->index frame template)))
(or (push+stack-indirect-instruction offset index #f)
(sequentially (stack-indirect-instruction offset index)
push-instruction)))))))
; This is called by REALLY-COMPILE-FLAT-LAMBDA above and by the compilator
; for PURE-LETREC.
(define (compile-lambda exp free lambda-name body-name frame)
(let* ((formals (cadr exp))
(nargs (number-of-required-args formals))
(n-ary? (n-ary? formals))
(stack-nargs (if n-ary? (+ nargs 1) nargs))
(need-env? (not (null? free))) ;+++ ; could just be #t
(frame (make-frame frame lambda-name stack-nargs #t need-env? #f))
(extras (if need-env? 2 1)))
(set-lexical-offsets! free stack-nargs)
(let ((code (compile-lambda-code formals
free
(caddr exp)
(+ stack-nargs extras)
extras
frame
body-name)))
(values (sequentially
(if n-ary?
(nary-lambda-protocol nargs #t need-env? #f)
(lambda-protocol nargs #t need-env? #f))
code)
frame))))
; Give each name node in NAMES a binding record that has its environment's
; stack index and the name's offset within that environment.
(define (set-lexical-offsets! names stack-index)
(let loop ((over 0) (names names))
(if (not (null? names))
(begin
(node-set! (car names)
'binding
(list stack-index over))
(loop (+ over 1) (cdr names))))))
; NAME isn't the name of the procedure, it's the name to be given to
; the value that the procedure will return.
;
; EXTRA is a count of any additional values that may be on the stack above
; the arguments, for example the environment and template.
(define (compile-lambda-code formals free body depth extra frame name)
(let* ((plain-nargs (number-of-required-args formals))
(is-n-ary? (n-ary? formals))
(nargs (if is-n-ary?
(+ plain-nargs 1)
plain-nargs))
(vars (normalize-formals formals)))
(set-frame-locations! vars (- depth extra))
(note-environment (let ((args (map name-node->symbol vars)))
(if (null? free)
args
(append args
(list (map name-node->symbol free)))))
0
(compile body depth frame (return-cont name)))))
; Mark NAMES as being at (- DEPTH 1) and on down in the current frame.
(define (set-frame-locations! names depth)
(let loop ((index (- depth 1)) (names (reverse names)))
(if (not (null? names))
(begin
(node-set! (car names)
'binding
(list index))
(loop (- index 1) (cdr names))))))
(define (name-node->symbol node)
(let ((form (node-form node)))
(cond ((name? form)
(name->symbol form))
((symbol? form)
form)
(else
#f))))
;----------------------------------------------------------------
; Returns the code to create the flat environment and the VARS list put in
; the order in which the variables appear in the environment.
;
; An [BIG-]FLAT-ENV instruction looks like:
;
; (enum op make-[big-]flat-env)
; number of vars
; number of closures
; [offset of template in frame
; offsets of templates in template]
; number of variables in frame
; offsets of vars in frame
; [offset of env in frame
; number of vars in env
; offsets of vars in level]*
;
; For MAKE-FLAT-ENV all values are one byte and for MAKE-BIG-FLAT-ENV they
; are two bytes.
;
; COMPILE-ENVIRONMENT produces flat environments with no closures. The
; PURE-LETREC compilator calls COMPILE-RECURSIVE-ENVIRONMENT to create
; environments that contain closures closed over that same environment.
(define (compile-environment vars depth)
(compile-recursive-environment vars
depth
0
(lambda (vars-in-order) '())))
; The code generator for PURE-LETREC calls this. It needs the VARS-IN-ORDER
; list in order to compile the templates that are used in the recursive
; procedures closed over the flat enviornment.
(define (compile-recursive-environment vars depth template-offset index-proc)
(receive (env-code vars-in-order)
(flat-environment-code vars depth)
(values (finish-flat-env (length vars-in-order)
env-code
template-offset
(index-proc vars-in-order))
vars-in-order)))
; Emit code to make a flat environment. There are two opcodes, a fast one
; that only works for small (< one-byte) environments with small (< one-byte)
; offsets (in other words, almost all of them) and one for two-byte sizes
; and offsets.
(define (finish-flat-env var-count env-code template-offset template-indexes)
(let ((code-bytes `(,(+ var-count
(length template-indexes))
,(length template-indexes)
,@(if (null? template-indexes)
'()
(cons template-offset template-indexes))
. ,env-code)))
(if (any (lambda (b)
(<= byte-limit b))
code-bytes)
(apply instruction
(enum op make-big-flat-env)
(one-byte->two-byte code-bytes))
(apply instruction
(enum op make-flat-env)
code-bytes))))
; Break up a list of numbers into their high bytes and low bytes.
(define (one-byte->two-byte code-bytes)
(let loop ((data (reverse code-bytes)) (res '()))
(if (null? data)
res
(loop (cdr data)
(cons (high-byte (car data))
(cons (low-byte (car data))
res))))))
; Actually make the code. FRAME is a list of (<variable> . <offset>) for
; variables in VARS that are in the current stack frame. INDIRECT is a list
; of lists of the form (<offset> (<variable> <index>) ...) indicating that
; <variable> is found at <index> in the vector at <offset> in the current
; frame. This calls FIGURE-ENV-DATA to make the actual code and constructs
; a copy of VARS that has the variables in the order in which they will appear
; in the environment (to be passed to NOTE-ENVIRONMENT for eventual use by
; the debugger).
(define (flat-environment-code vars depth)
(receive (frame indirect)
(get-variables-locations vars depth)
(values (figure-env-data (map cdr frame)
indirect)
(apply append
(map car frame)
(map (lambda (indirect)
(map car (cdr indirect)))
indirect)))))
; Translates VARS into two lists:
; - ((<variable> . <offset>) ...) for those variables that are in the
; current frame
; - ((<offset> (<variable> <index>) ...) ...) indicating <variable> is at
; <index> in the vector at <offset> in the current frame
(define (get-variables-locations vars depth)
(let loop ((vars vars) (frame '()) (other '()))
(if (null? vars)
(values frame other)
(let* ((var (car vars))
(binding (name-node-binding var)))
(if (pair? binding)
(let ((offset (index->offset (car binding) depth)))
(if (null? (cdr binding))
(loop (cdr vars)
(cons (cons var offset)
frame)
other)
(loop (cdr vars)
frame
(add-variable var offset (cdr binding) other))))
(assertion-violation 'get-variables-locations
"variable in flat-lambda list is not local"
(car vars)))))))
; Add VAR, with stack-offset OFFSET and MORE other indexes, to OTHER, an alist
; indexed by offsets. Currently MORE always has lenth one.
(define (add-variable var offset more other)
(let ((have (assq offset other)))
(if have
(begin
(set-cdr! have (cons (cons var more)
(cdr have)))
other)
`((,offset (,var . ,more))
. ,other))))
; Convert the frame offsets and indirect information into the form used by the
; MAKE{-BIG}-FLAT-ENV opcode by adding length information at appropriate points
; and eliding the variables in INDIRECTS.
(define (figure-env-data frame-offsets indirects)
`(,(length frame-offsets)
,@frame-offsets
. ,(let loop ((indirects indirects) (data '()))
(if (null? indirects)
(reverse data)
(loop (cdr indirects)
(append (reverse (map cadr (cdar indirects)))
(list (length (cdar indirects))
(caar indirects))
data))))))
|