comp-lambda.scm 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; We only do flat lambdas now.
  4. (define-compilator 'lambda syntax-type
  5. (lambda (node depth frame cont)
  6. (generate-trap depth
  7. frame
  8. cont
  9. "cannot compile non-flat lambda")))
  10. ;----------------------------------------------------------------
  11. ; (flat-lambda (id ...) (free-id ...) body)
  12. (define-compilator 'flat-lambda syntax-type
  13. (lambda (node depth frame cont)
  14. (let ((exp (node-form node))
  15. (name (cont-name cont)))
  16. (let ((vars (cadr exp))
  17. (free (caddr exp))
  18. (body (cadddr exp)))
  19. (deliver-value (compile-flat-lambda name vars body free depth frame)
  20. cont)))))
  21. ; The MAKE-FLAT-ENV instruction is designed to allow us to make nested flat
  22. ; environments (i.e. flat environments consisting of a linked chain of vectors)
  23. ; but this code doesn't generate them.
  24. ;
  25. ; We could sort out the two-byte offsets and make a separate big-flat-env that
  26. ; becomes the superior env of the regular flat env (instead of the #f that is
  27. ; there now).
  28. (define (compile-flat-lambda name vars body free depth frame)
  29. (receive (env-code free-vars)
  30. (if (null? free) ; avoid ENVIRONMENT-OFFSET if no env
  31. (values (instruction (enum op false)) '())
  32. (compile-environment free (+ depth 1)))
  33. (sequentially
  34. (really-compile-flat-lambda name vars body free-vars depth frame)
  35. env-code
  36. (instruction (enum op make-stored-object) 2 (enum stob closure)))))
  37. ; Save the current locations of the free variables, compile the lambda, and
  38. ; then restore the old locations.
  39. (define (really-compile-flat-lambda name vars body free depth frame)
  40. (let ((old-locations (map name-node-binding free)))
  41. (receive (proc-code proc-frame)
  42. (compile-lambda `(lambda ,vars ,body)
  43. free
  44. name
  45. #f
  46. frame)
  47. (for-each (lambda (node location)
  48. (node-set! node 'binding location))
  49. free
  50. old-locations)
  51. (let ((template (segment->template proc-code proc-frame)))
  52. (let ((offset (template-offset frame depth))
  53. (index (literal->index frame template)))
  54. (or (push+stack-indirect-instruction offset index #f)
  55. (sequentially (stack-indirect-instruction offset index)
  56. push-instruction)))))))
  57. ; This is called by REALLY-COMPILE-FLAT-LAMBDA above and by the compilator
  58. ; for PURE-LETREC.
  59. (define (compile-lambda exp free lambda-name body-name frame)
  60. (let* ((formals (cadr exp))
  61. (nargs (number-of-required-args formals))
  62. (n-ary? (n-ary? formals))
  63. (stack-nargs (if n-ary? (+ nargs 1) nargs))
  64. (need-env? (not (null? free))) ;+++ ; could just be #t
  65. (frame (make-frame frame lambda-name stack-nargs #t need-env? #f))
  66. (extras (if need-env? 2 1)))
  67. (set-lexical-offsets! free stack-nargs)
  68. (let ((code (compile-lambda-code formals
  69. free
  70. (caddr exp)
  71. (+ stack-nargs extras)
  72. extras
  73. frame
  74. body-name)))
  75. (values (sequentially
  76. (if n-ary?
  77. (nary-lambda-protocol nargs #t need-env? #f)
  78. (lambda-protocol nargs #t need-env? #f))
  79. code)
  80. frame))))
  81. ; Give each name node in NAMES a binding record that has its environment's
  82. ; stack index and the name's offset within that environment.
  83. (define (set-lexical-offsets! names stack-index)
  84. (let loop ((over 0) (names names))
  85. (if (not (null? names))
  86. (begin
  87. (node-set! (car names)
  88. 'binding
  89. (list stack-index over))
  90. (loop (+ over 1) (cdr names))))))
  91. ; NAME isn't the name of the procedure, it's the name to be given to
  92. ; the value that the procedure will return.
  93. ;
  94. ; EXTRA is a count of any additional values that may be on the stack above
  95. ; the arguments, for example the environment and template.
  96. (define (compile-lambda-code formals free body depth extra frame name)
  97. (let* ((plain-nargs (number-of-required-args formals))
  98. (is-n-ary? (n-ary? formals))
  99. (nargs (if is-n-ary?
  100. (+ plain-nargs 1)
  101. plain-nargs))
  102. (vars (normalize-formals formals)))
  103. (set-frame-locations! vars (- depth extra))
  104. (note-environment (let ((args (map name-node->symbol vars)))
  105. (if (null? free)
  106. args
  107. (append args
  108. (list (map name-node->symbol free)))))
  109. 0
  110. (compile body depth frame (return-cont name)))))
  111. ; Mark NAMES as being at (- DEPTH 1) and on down in the current frame.
  112. (define (set-frame-locations! names depth)
  113. (let loop ((index (- depth 1)) (names (reverse names)))
  114. (if (not (null? names))
  115. (begin
  116. (node-set! (car names)
  117. 'binding
  118. (list index))
  119. (loop (- index 1) (cdr names))))))
  120. (define (name-node->symbol node)
  121. (let ((form (node-form node)))
  122. (cond ((name? form)
  123. (name->symbol form))
  124. ((symbol? form)
  125. form)
  126. (else
  127. #f))))
  128. ;----------------------------------------------------------------
  129. ; Returns the code to create the flat environment and the VARS list put in
  130. ; the order in which the variables appear in the environment.
  131. ;
  132. ; An [BIG-]FLAT-ENV instruction looks like:
  133. ;
  134. ; (enum op make-[big-]flat-env)
  135. ; number of vars
  136. ; number of closures
  137. ; [offset of template in frame
  138. ; offsets of templates in template]
  139. ; number of variables in frame
  140. ; offsets of vars in frame
  141. ; [offset of env in frame
  142. ; number of vars in env
  143. ; offsets of vars in level]*
  144. ;
  145. ; For MAKE-FLAT-ENV all values are one byte and for MAKE-BIG-FLAT-ENV they
  146. ; are two bytes.
  147. ;
  148. ; COMPILE-ENVIRONMENT produces flat environments with no closures. The
  149. ; PURE-LETREC compilator calls COMPILE-RECURSIVE-ENVIRONMENT to create
  150. ; environments that contain closures closed over that same environment.
  151. (define (compile-environment vars depth)
  152. (compile-recursive-environment vars
  153. depth
  154. 0
  155. (lambda (vars-in-order) '())))
  156. ; The code generator for PURE-LETREC calls this. It needs the VARS-IN-ORDER
  157. ; list in order to compile the templates that are used in the recursive
  158. ; procedures closed over the flat enviornment.
  159. (define (compile-recursive-environment vars depth template-offset index-proc)
  160. (receive (env-code vars-in-order)
  161. (flat-environment-code vars depth)
  162. (values (finish-flat-env (length vars-in-order)
  163. env-code
  164. template-offset
  165. (index-proc vars-in-order))
  166. vars-in-order)))
  167. ; Emit code to make a flat environment. There are two opcodes, a fast one
  168. ; that only works for small (< one-byte) environments with small (< one-byte)
  169. ; offsets (in other words, almost all of them) and one for two-byte sizes
  170. ; and offsets.
  171. (define (finish-flat-env var-count env-code template-offset template-indexes)
  172. (let ((code-bytes `(,(+ var-count
  173. (length template-indexes))
  174. ,(length template-indexes)
  175. ,@(if (null? template-indexes)
  176. '()
  177. (cons template-offset template-indexes))
  178. . ,env-code)))
  179. (if (any (lambda (b)
  180. (<= byte-limit b))
  181. code-bytes)
  182. (apply instruction
  183. (enum op make-big-flat-env)
  184. (one-byte->two-byte code-bytes))
  185. (apply instruction
  186. (enum op make-flat-env)
  187. code-bytes))))
  188. ; Break up a list of numbers into their high bytes and low bytes.
  189. (define (one-byte->two-byte code-bytes)
  190. (let loop ((data (reverse code-bytes)) (res '()))
  191. (if (null? data)
  192. res
  193. (loop (cdr data)
  194. (cons (high-byte (car data))
  195. (cons (low-byte (car data))
  196. res))))))
  197. ; Actually make the code. FRAME is a list of (<variable> . <offset>) for
  198. ; variables in VARS that are in the current stack frame. INDIRECT is a list
  199. ; of lists of the form (<offset> (<variable> <index>) ...) indicating that
  200. ; <variable> is found at <index> in the vector at <offset> in the current
  201. ; frame. This calls FIGURE-ENV-DATA to make the actual code and constructs
  202. ; a copy of VARS that has the variables in the order in which they will appear
  203. ; in the environment (to be passed to NOTE-ENVIRONMENT for eventual use by
  204. ; the debugger).
  205. (define (flat-environment-code vars depth)
  206. (receive (frame indirect)
  207. (get-variables-locations vars depth)
  208. (values (figure-env-data (map cdr frame)
  209. indirect)
  210. (apply append
  211. (map car frame)
  212. (map (lambda (indirect)
  213. (map car (cdr indirect)))
  214. indirect)))))
  215. ; Translates VARS into two lists:
  216. ; - ((<variable> . <offset>) ...) for those variables that are in the
  217. ; current frame
  218. ; - ((<offset> (<variable> <index>) ...) ...) indicating <variable> is at
  219. ; <index> in the vector at <offset> in the current frame
  220. (define (get-variables-locations vars depth)
  221. (let loop ((vars vars) (frame '()) (other '()))
  222. (if (null? vars)
  223. (values frame other)
  224. (let* ((var (car vars))
  225. (binding (name-node-binding var)))
  226. (if (pair? binding)
  227. (let ((offset (index->offset (car binding) depth)))
  228. (if (null? (cdr binding))
  229. (loop (cdr vars)
  230. (cons (cons var offset)
  231. frame)
  232. other)
  233. (loop (cdr vars)
  234. frame
  235. (add-variable var offset (cdr binding) other))))
  236. (assertion-violation 'get-variables-locations
  237. "variable in flat-lambda list is not local"
  238. (car vars)))))))
  239. ; Add VAR, with stack-offset OFFSET and MORE other indexes, to OTHER, an alist
  240. ; indexed by offsets. Currently MORE always has lenth one.
  241. (define (add-variable var offset more other)
  242. (let ((have (assq offset other)))
  243. (if have
  244. (begin
  245. (set-cdr! have (cons (cons var more)
  246. (cdr have)))
  247. other)
  248. `((,offset (,var . ,more))
  249. . ,other))))
  250. ; Convert the frame offsets and indirect information into the form used by the
  251. ; MAKE{-BIG}-FLAT-ENV opcode by adding length information at appropriate points
  252. ; and eliding the variables in INDIRECTS.
  253. (define (figure-env-data frame-offsets indirects)
  254. `(,(length frame-offsets)
  255. ,@frame-offsets
  256. . ,(let loop ((indirects indirects) (data '()))
  257. (if (null? indirects)
  258. (reverse data)
  259. (loop (cdr indirects)
  260. (append (reverse (map cadr (cdar indirects)))
  261. (list (length (cdar indirects))
  262. (caar indirects))
  263. data))))))