call.scm 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Code to handle the calling and return protocols.
  3. ; *VAL* is the procedure, the arguments are on stack, and the next byte
  4. ; is the number of arguments (all of which are on the stack). This checks
  5. ; that *VAL* is in fact a closure and for the common case of a non-n-ary
  6. ; procedure that has few arguments. This case is handled directly and all
  7. ; others are passed off to PLAIN-PROTOCOL-MATCH.
  8. (define-opcode call
  9. (let ((stack-arg-count (code-byte 2)))
  10. (make-continuation-on-stack (address+ *code-pointer* (code-offset 0))
  11. stack-arg-count)
  12. (goto do-call stack-arg-count)))
  13. (define-opcode tail-call
  14. (let ((stack-arg-count (code-byte 0)))
  15. (move-args-above-cont! stack-arg-count)
  16. (goto do-call (code-byte 0))))
  17. (define (do-call stack-arg-count)
  18. (if (closure? *val*)
  19. (let* ((template (closure-template *val*))
  20. (code (template-code template))
  21. (protocol (code-vector-ref code 1)))
  22. (cond ((= protocol stack-arg-count)
  23. (goto run-body-with-default-space code 2 template))
  24. ((= (native->byte-protocol protocol)
  25. stack-arg-count)
  26. (goto call-native-code-with-default-space 2))
  27. (else
  28. (goto plain-protocol-match stack-arg-count))))
  29. (goto application-exception
  30. (enum exception bad-procedure)
  31. stack-arg-count null 0)))
  32. ;----------------------------------------------------------------
  33. ; Native protocols have the high bit set.
  34. (define (native-protocol? protocol)
  35. (< 127 protocol))
  36. (define (native->byte-protocol protocol)
  37. (bitwise-and protocol 127))
  38. (define (byte->native-protocol protocol)
  39. (bitwise-ior protocol 128))
  40. (define (call-native-code-with-default-space protocol-skip)
  41. (if (and (ensure-default-procedure-space!)
  42. (pending-interrupt?))
  43. (goto handle-native-interrupt protocol-skip)
  44. (goto really-call-native-code protocol-skip)))
  45. (define (call-native-code protocol-skip needed-stack-space)
  46. (if (and (ensure-stack-space! needed-stack-space)
  47. (pending-interrupt?))
  48. (goto handle-native-interrupt protocol-skip)
  49. (goto really-call-native-code protocol-skip)))
  50. (define (really-call-native-code protocol-skip)
  51. (goto post-native-dispatch (s48-call-native-procedure *val* protocol-skip)))
  52. (define s48-*native-protocol*)
  53. ; The external code just sets the C variable directly, but we export this to
  54. ; let the Pre-Scheme compiler know that someone, somewhere, does a set.
  55. (define (s48-set-native-protocol! protocol)
  56. (set! s48-*native-protocol* protocol))
  57. (define (post-native-dispatch tag)
  58. ; (write-string "P" (current-error-port))
  59. ; (write-integer tag (current-error-port))
  60. (let loop ((tag tag))
  61. (case tag
  62. ((0)
  63. (goto return-values s48-*native-protocol* null 0))
  64. ((1)
  65. (goto perform-application s48-*native-protocol*))
  66. ((2)
  67. (let* ((template (pop))
  68. (return-address (pop)))
  69. (cond ((pending-interrupt?)
  70. ;(write-string "interrupt is pending" (current-error-port))
  71. (goto handle-native-poll template return-address))
  72. (else
  73. ;(write-string "no interrupt is pending" (current-error-port))
  74. (loop (s48-jump-native return-address template))))))
  75. ((3)
  76. (goto return-values s48-*native-protocol* null 0))
  77. ((4)
  78. (goto interpret *code-pointer*))
  79. ((5)
  80. (goto do-apply-with-native-cont s48-*native-protocol* (pop)))
  81. ((6)
  82. (raise-exception trap 0 *val*))
  83. (else
  84. (error "unexpected native return value" tag)))))
  85. ;; Not used for now
  86. (define (push-native-exception-continuation)
  87. (push-continuation! (code+pc->code-pointer *native-exception-return-code*
  88. return-code-pc)))
  89. ;----------------------------------------------------------------
  90. ; As above but with a two-byte argument count. The tail and not-tail calls
  91. ; are both done using the same opcode (which is not done above for speed in
  92. ; the tail case; this optimization needs to be tested for effectiveness).
  93. (define-opcode big-call
  94. (let ((stack-arg-count (code-offset 2)))
  95. (maybe-make-continuation stack-arg-count)
  96. (goto perform-application stack-arg-count)))
  97. ; A number of opcodes that make calls use this. We get a the offset of the
  98. ; return address and then either add it as a new continuation below the current
  99. ; arguments or, for tail calls, move those arguments above the current
  100. ; continuation.
  101. (define (maybe-make-continuation stack-arg-count)
  102. (let ((return-pointer-offset (code-offset 0)))
  103. (if (= return-pointer-offset 0)
  104. (move-args-above-cont! stack-arg-count)
  105. (make-continuation-on-stack (address+ *code-pointer*
  106. return-pointer-offset)
  107. stack-arg-count))))
  108. (define (maybe-make-native-continuation stack-arg-count maybe-cont)
  109. (if (= maybe-cont 0)
  110. (move-args-above-cont! stack-arg-count)
  111. (make-continuation-on-stack (integer->address maybe-cont)
  112. stack-arg-count)))
  113. ; Call a template instead of a procedure. This is currently used for
  114. ; stringing together code for top-level forms and doing the same thing for
  115. ; the initialization code made by the static linker.
  116. ;
  117. ; call-template <return-offset> <template-index> <index-within-template> <nargs>
  118. (define-opcode call-template
  119. (let* ((template (get-literal 2))
  120. (code (template-code template))
  121. (nargs (code-byte 6)))
  122. (maybe-make-continuation nargs)
  123. (cond ((= nargs (code-vector-ref code 1))
  124. (goto run-body-with-default-space code 2 template))
  125. ((and (= big-stack-protocol (code-vector-ref code 1))
  126. (= nargs
  127. (code-vector-ref code (- (code-vector-length code) 3))))
  128. (goto run-body
  129. code
  130. 2
  131. template
  132. (code-vector-ref16 code (- (code-vector-length code) 2))))
  133. (else
  134. (raise-exception wrong-type-argument 7 template)))))
  135. ; The following is used only for experiments. The compiler does not use it.
  136. ;(define-opcode goto-template
  137. ; (set-code-pointer! (template-code template) 0)
  138. ; (goto interpret *code-pointer*))
  139. ; APPLY: *VAL* is the procedure, the rest-arg list is on top of the stack,
  140. ; the next two bytes are the number of stack arguments below the rest-args list.
  141. ; We check that the rest-arg list is a proper list and let
  142. ; PERFORM-APPLICATION-WITH-REST-LIST do the work.
  143. (define-opcode apply
  144. (let ((list-args (pop))
  145. (stack-nargs (code-offset 2)))
  146. (receive (okay? length)
  147. (okay-argument-list list-args)
  148. (if okay?
  149. (begin
  150. (maybe-make-continuation stack-nargs)
  151. (goto perform-application-with-rest-list
  152. stack-nargs
  153. list-args
  154. length))
  155. (let ((args (pop-args->list*+gc list-args stack-nargs)))
  156. (raise-exception wrong-type-argument -1 *val* args))))))
  157. (define (do-apply-with-native-cont stack-nargs maybe-cont)
  158. (let ((list-args (pop)))
  159. (receive (okay? length)
  160. (okay-argument-list list-args)
  161. (if okay?
  162. (begin
  163. (maybe-make-native-continuation stack-nargs maybe-cont)
  164. (goto perform-application-with-rest-list
  165. stack-nargs
  166. list-args
  167. length))
  168. (let ((args (pop-args->list*+gc list-args stack-nargs)))
  169. (raise-exception wrong-type-argument -1 *val* args))))))
  170. ; This is only used for the closed-compiled version of APPLY.
  171. ;
  172. ; Stack = arg0 arg1 ... argN rest-list N+1 total-arg-count
  173. ; Arg0 is the procedure.
  174. ;
  175. ; Note that the rest-list on the stack is the rest-list passed to APPLY
  176. ; procedure and not the rest-list to be used in the call to the procedure.
  177. ; Consider (APPLY APPLY (LIST LIST '(1 2 3))), where the initial APPLY
  178. ; is not done in-line. The stack for the inner call to APPLY will be
  179. ; [(<list-procedure> (1 2 3)), 1, 2], whereas for
  180. ; (APPLY APPLY LIST 1 '(2 (3))) the stack will be
  181. ; [<list-procedure>, 1, (2 (3)), 2, 4].
  182. ;
  183. ; We grab the counts and the procedure and copy the rest of the stack arguments
  184. ; down to make us properly tail-recursive. Then we get the true stack-arg count
  185. ; and list args and again let PERFORM-APPLICATION-WITH-REST-LIST do the work.
  186. (define-opcode closed-apply
  187. (let* ((nargs (extract-fixnum (pop)))
  188. (stack-nargs (extract-fixnum (pop))))
  189. (set! *val* (stack-ref stack-nargs)) ; proc in *VAL*
  190. (move-args-above-cont! stack-nargs)
  191. (receive (okay? stack-arg-count list-args list-arg-count)
  192. (get-closed-apply-args nargs stack-nargs)
  193. (if okay?
  194. (begin
  195. (goto perform-application-with-rest-list
  196. stack-arg-count
  197. list-args
  198. list-arg-count))
  199. (let ((args (pop-args->list*+gc list-args stack-arg-count)))
  200. (raise-exception wrong-type-argument -1 *val* args))))))
  201. ; Stack = arg0 arg1 ... argN rest-list
  202. ; This needs to get the last argument, which is either argN or the last
  203. ; element of the rest-list, and splice it into the rest of the arguments.
  204. ; If the rest-list is null, then argN is the last argument and becomes the
  205. ; new rest-list. If the rest-list is non-null, then we go to the end, get
  206. ; the list there, and splice the two together to make a single list.
  207. ; This only happens if someone does (APPLY APPLY ...).
  208. (define (get-closed-apply-args nargs stack-nargs)
  209. (let ((rest-list (pop)))
  210. (receive (list-args stack-nargs)
  211. (cond ((vm-eq? rest-list null)
  212. (values (pop)
  213. (- stack-nargs 2))) ; drop proc and final list
  214. ((vm-eq? (vm-cdr rest-list) null)
  215. (values (vm-car rest-list)
  216. (- stack-nargs 1))) ; drop proc
  217. (else
  218. (let* ((penultimate-cdr (penultimate-cdr rest-list))
  219. (list-args (vm-car (vm-cdr penultimate-cdr))))
  220. (vm-set-cdr! penultimate-cdr list-args)
  221. (values rest-list
  222. (- stack-nargs 1))))) ; drop proc
  223. (receive (okay? list-arg-count)
  224. (okay-argument-list list-args)
  225. (values okay?
  226. stack-nargs
  227. list-args
  228. list-arg-count)))))
  229. ; If LIST is a proper list (the final cdr is null) then we return #T and the
  230. ; length of the list, otherwise we return #F.
  231. (define (okay-argument-list list)
  232. (let loop ((fast list) (len 0) (slow list) (move-slow? #f))
  233. (cond ((vm-eq? null fast)
  234. (values #t len))
  235. ((or (not (vm-pair? fast)))
  236. (values #f 0))
  237. ((not move-slow?)
  238. (loop (vm-cdr fast) (+ len 1) slow #t))
  239. ((vm-eq? fast slow)
  240. (values #f 0))
  241. (else
  242. (loop (vm-cdr fast) (+ len 1) (vm-cdr slow) #f)))))
  243. ; Return the second-to-last cdr of LIST.
  244. (define (penultimate-cdr list)
  245. (let loop ((list (vm-cdr (vm-cdr list))) (follower list))
  246. (if (eq? null list)
  247. follower
  248. (loop (vm-cdr list) (vm-cdr follower)))))
  249. ;----------------
  250. ; Call the procedure in *VAL*. STACK-ARG-COUNT is the number of arguments
  251. ; on the stack, LIST-ARGS is a list of LIST-ARG-COUNT additional arguments.
  252. ;
  253. ; The CLOSURE? and protocol checks must come before the interrupt check because
  254. ; the interrupt code assumes that the correct template is in place. This delays
  255. ; the handling of interrupts by a few instructions; it shouldn't matter.
  256. (define (perform-application stack-arg-count)
  257. (if (closure? *val*)
  258. (goto plain-protocol-match stack-arg-count)
  259. (goto application-exception
  260. (enum exception bad-procedure)
  261. stack-arg-count null 0)))
  262. (define (perform-application-with-rest-list stack-arg-count
  263. list-args list-arg-count)
  264. (cond ((= 0 list-arg-count)
  265. (goto perform-application stack-arg-count))
  266. ((closure? *val*)
  267. (goto list-protocol-match
  268. stack-arg-count list-args list-arg-count))
  269. (else
  270. (goto application-exception
  271. (enum exception bad-procedure)
  272. stack-arg-count list-args list-arg-count))))
  273. (define (wrong-nargs stack-arg-count list-args list-arg-count)
  274. (goto application-exception
  275. (enum exception wrong-number-of-arguments)
  276. stack-arg-count list-args list-arg-count))
  277. ; The main protocol-matching function takes as an argument a token indicating
  278. ; if the called-value is a handler and if so, what kind. A non-negative value
  279. ; is the opcode whose exception handler is begin called. -1 means that the
  280. ; procedure is not a handler. Any other negative value indicates that the
  281. ; procedure is an interrupt handler. The interrupt is (- -2 token).
  282. (define not-a-handler -1)
  283. (define (call-exception-handler stack-arg-count opcode)
  284. (goto real-protocol-match
  285. stack-arg-count
  286. null
  287. 0
  288. opcode))
  289. (define (call-interrupt-handler stack-arg-count interrupt)
  290. (goto real-protocol-match
  291. stack-arg-count
  292. null
  293. 0
  294. (- -2 interrupt)))
  295. ; Check that the arguments match those needed by *VAL*, which is a closure,
  296. ; moving arguments to or from the stack if necessary, and ensure that there
  297. ; is enough stack space for the procedure. The environment needed by *VAL*
  298. ; is created.
  299. (define (plain-protocol-match stack-arg-count)
  300. (goto real-protocol-match stack-arg-count null 0 not-a-handler))
  301. (define (list-protocol-match stack-arg-count list-args list-arg-count)
  302. (goto real-protocol-match
  303. stack-arg-count
  304. list-args
  305. list-arg-count
  306. not-a-handler))
  307. (define (real-protocol-match stack-arg-count
  308. list-args
  309. list-arg-count
  310. handler-tag)
  311. (let ((code (template-code (closure-template *val*)))
  312. (total-arg-count (+ stack-arg-count list-arg-count))
  313. (lose (lambda ()
  314. (cond ((= handler-tag not-a-handler)
  315. (goto wrong-nargs
  316. stack-arg-count list-args list-arg-count))
  317. ((<= 0 handler-tag)
  318. (error "wrong number of arguments to exception handler"
  319. handler-tag))
  320. (else
  321. (error "wrong number of arguments to interrupt handler"
  322. (- -2 handler-tag)))))))
  323. (assert (= (enum op protocol)
  324. (code-vector-ref code 0)))
  325. (let loop ((protocol (code-vector-ref code 1))
  326. (stack-space default-stack-space)
  327. (native? #f))
  328. (let ((win (lambda (skip stack-arg-count)
  329. (if native?
  330. (goto call-native-code skip stack-space)
  331. (let ((template (closure-template *val*)))
  332. (goto run-body (template-code template)
  333. skip
  334. template
  335. stack-space))))))
  336. (let ((fixed-match (lambda (wants skip)
  337. (if (= wants total-arg-count)
  338. (begin
  339. (if (not (= 0 list-arg-count))
  340. (begin
  341. (push-list list-args list-arg-count)
  342. (unspecific))) ; avoid type problem
  343. (win skip total-arg-count))
  344. (lose))))
  345. ;; N-ary procedure.
  346. (n-ary-match (lambda (wants-stack-args skip)
  347. (if (<= wants-stack-args total-arg-count)
  348. (begin
  349. (rest-list-setup+gc wants-stack-args
  350. stack-arg-count
  351. list-args
  352. list-arg-count)
  353. (win skip (+ wants-stack-args 1)))
  354. (lose))))
  355. ;; If there are > 2 args the top two are pushed on the stack.
  356. ;; Then the remaining list, the total number of arguments, and
  357. ;; the number on the stack are pushed on the stack.
  358. (args+nargs-match (lambda (skip)
  359. (let ((final-stack-arg-count
  360. (if (< total-arg-count 3)
  361. total-arg-count
  362. (max 2 stack-arg-count))))
  363. (rest-list-setup+gc (max stack-arg-count
  364. final-stack-arg-count)
  365. stack-arg-count
  366. list-args
  367. list-arg-count)
  368. (push (enter-fixnum final-stack-arg-count))
  369. (push (enter-fixnum total-arg-count))
  370. (win skip (+ final-stack-arg-count 3))))))
  371. (cond ((= protocol nary-dispatch-protocol)
  372. (cond ((< total-arg-count 3)
  373. (let ((skip (code-vector-ref code (+ 3 total-arg-count))))
  374. (if (= 0 skip)
  375. (lose)
  376. (begin
  377. (push-list list-args list-arg-count)
  378. (goto run-nary-dispatch-body code skip)))))
  379. ((= 0 (code-vector-ref code 2))
  380. (lose))
  381. (else
  382. (args+nargs-match 6)))) ; leave env/template
  383. ((<= protocol maximum-stack-args)
  384. (fixed-match protocol 2))
  385. ((= protocol two-byte-nargs+list-protocol)
  386. (n-ary-match (code-vector-ref16 code 2) 4))
  387. ((= protocol args+nargs-protocol)
  388. (if (>= total-arg-count
  389. (code-vector-ref code 2))
  390. (args+nargs-match 3)
  391. (lose)))
  392. ((native-protocol? protocol)
  393. (loop (native->byte-protocol protocol) stack-space #t))
  394. ((= protocol two-byte-nargs-protocol)
  395. (fixed-match (code-vector-ref16 code 2) 4))
  396. ((= protocol big-stack-protocol)
  397. (let ((length (code-vector-length code)))
  398. (loop (code-vector-ref code (- length 3))
  399. (code-vector-ref16 code (- length 2))
  400. native?)))
  401. (else
  402. (error "unknown protocol" protocol)
  403. (lose))))))))
  404. ; Adjusts the number of stack arguments to be WANTS-STACK-ARGS by moving
  405. ; arguments between the stack and LIST-ARGS as necessary. Whatever is left
  406. ; of LIST-ARGS is then copied and the copy is pushed onto the stack.
  407. (define (rest-list-setup+gc wants-stack-args stack-arg-count
  408. list-args list-arg-count)
  409. (cond ((= stack-arg-count wants-stack-args)
  410. (push (copy-list*+gc list-args list-arg-count)))
  411. ((< stack-arg-count wants-stack-args)
  412. (let ((count (- wants-stack-args stack-arg-count)))
  413. (push (copy-list*+gc (push-list list-args count)
  414. (- list-arg-count count)))))
  415. (else ; (> stack-arg-count wants-stack-args)
  416. (let ((count (- stack-arg-count wants-stack-args)))
  417. (push (pop-args->list*+gc (copy-list*+gc list-args list-arg-count)
  418. count))))))
  419. ; Raise an exception, passing to it a list of the arguments on the stack and
  420. ; in LIST-ARGS.
  421. (define (application-exception exception
  422. stack-arg-count list-args list-arg-count)
  423. (let ((args (pop-args->list*+gc (copy-list*+gc list-args list-arg-count)
  424. stack-arg-count)))
  425. (raise-exception* exception -1 *val* args))) ; no next opcode
  426. ;----------------------------------------------------------------
  427. (define (run-body-with-default-space code used template)
  428. (real-run-body-with-default-space code used (+ used 1) template))
  429. (define (run-nary-dispatch-body code start-pc)
  430. (real-run-body-with-default-space code 6 start-pc (closure-template *val*)))
  431. (define (real-run-body-with-default-space code env/temp-offset used template)
  432. (env-and-template-setup (code-vector-ref code env/temp-offset) template)
  433. (set-code-pointer! code used)
  434. (if (and (ensure-default-procedure-space!)
  435. (pending-interrupt?))
  436. (goto handle-interrupt)
  437. (goto interpret *code-pointer*)))
  438. (define (run-body code used template needed-stack-space)
  439. (env-and-template-setup (code-vector-ref code used) template)
  440. (set-code-pointer! code (+ used 1))
  441. (if (and (ensure-stack-space! needed-stack-space)
  442. (pending-interrupt?))
  443. (goto handle-interrupt)
  444. (goto interpret *code-pointer*)))
  445. (define (env-and-template-setup spec template)
  446. (cond ((= #b011 spec)
  447. (push (closure-env *val*))
  448. (push template))
  449. ((= #b001 spec)
  450. (push template))
  451. ((= #b010 spec)
  452. (push (closure-env *val*)))
  453. ;; the next two are for the output of the optimizer,
  454. ;; for closures that have the environment merged in
  455. ((= #b100 spec)
  456. (push *val*)) ; closure
  457. ((= #b110 spec)
  458. (push *val*)
  459. (push (closure-env *val*)))
  460. ;; the following probably won't occur in the wild
  461. ((= #b101 spec)
  462. (push *val*)
  463. (push template))
  464. ((= #b111 spec)
  465. (push *val*)
  466. (push (closure-env *val*))
  467. (push template))))
  468. ;----------------------------------------------------------------
  469. ; Get a two-byte number from CODE-VECTOR.
  470. (define (code-vector-ref16 code-vector index)
  471. (let ((high (code-vector-ref code-vector index)))
  472. (adjoin-bits high
  473. (code-vector-ref code-vector (+ index 1))
  474. bits-used-per-byte)))
  475. (define (code-pointer-ref code-pointer index)
  476. (fetch-byte (address+ code-pointer index)))
  477. (define (code-pointer-ref16 code-pointer index)
  478. (let ((high (code-pointer-ref code-pointer index)))
  479. (adjoin-bits high
  480. (code-pointer-ref code-pointer (+ index 1))
  481. bits-used-per-byte)))
  482. ;----------------
  483. ; Returns - these use many of the same protocols.
  484. ; Invoke the contination, if it can handle a single value. There are four
  485. ; protocols that are okay:
  486. ;
  487. ; 1, ignore-values
  488. ; We just leave *VAL* as is and return.
  489. ; bottom-of-stack
  490. ; Real continuation is either in the heap or #F (if we are really at the
  491. ; bottom of the stack). We get the real continuation and either try again
  492. ; or return from the VM.
  493. ; two-byte-nargs+list
  494. ; Continuation is n-ary. If it want 0 or 1 value on the stack we are okay
  495. ; and do the setup and return.
  496. (define-opcode return
  497. (let loop ()
  498. (let ((code-pointer (current-continuation-code-pointer)))
  499. (assert (= (enum op protocol)
  500. (code-pointer-ref code-pointer 0)))
  501. (let ((protocol (code-pointer-ref code-pointer 1)))
  502. (cond ((or (= protocol 1)
  503. (= protocol ignore-values-protocol))
  504. (pop-continuation!)
  505. (goto continue 1)) ; one protocol byte
  506. ((or (= protocol (byte->native-protocol 1))
  507. (= protocol (byte->native-protocol
  508. ignore-values-protocol)))
  509. (goto native-return 2))
  510. ((= protocol bottom-of-stack-protocol)
  511. (let ((cont (get-continuation-from-heap)))
  512. (if (continuation? cont)
  513. (begin
  514. (copy-continuation-from-heap! cont 0)
  515. (loop))
  516. (goto return-from-vm cont))))
  517. ((= protocol call-with-values-protocol)
  518. (let ((proc (current-continuation-ref 0))
  519. (offset (code-pointer-ref16 code-pointer 2)))
  520. (if (= offset 0)
  521. (skip-current-continuation! 0) ; we're done with it
  522. (begin
  523. (shrink-and-reset-continuation!
  524. (address+ code-pointer offset))
  525. (remove-current-frame)))
  526. (push *val*)
  527. (set! *val* proc)
  528. (goto perform-application-with-rest-list 1 null 0)))
  529. ((= protocol two-byte-nargs+list-protocol)
  530. (let ((wants-stack-args (code-pointer-ref16 code-pointer 2)))
  531. (cond ((= 0 wants-stack-args)
  532. (pop-continuation!)
  533. (push (vm-cons *val* null (ensure-space vm-pair-size)))
  534. (goto continue 3))
  535. ((= 1 wants-stack-args)
  536. (pop-continuation!)
  537. (push *val*)
  538. (push null)
  539. (goto continue 3))
  540. (else
  541. (push *val*)
  542. (goto return-exception 1 null)))))
  543. (else
  544. (push *val*)
  545. (goto return-exception 1 null)))))))
  546. ; CONT is not a continuation. If it is false and *VAL* is a fixnum we can
  547. ; return from the VM. Otherwise we set the continuation to #F and raise an
  548. ; exception.
  549. (define (return-from-vm cont)
  550. (cond ((and (false? cont)
  551. (fixnum? *val*))
  552. (set! s48-*callback-return-stack-block* false) ; not from a callback
  553. (extract-fixnum *val*)) ; VM returns here
  554. (else
  555. (set-current-continuation! false)
  556. (raise-exception wrong-type-argument -1 *val* cont))))
  557. ; This is only used in the closed-compiled version of VALUES.
  558. ; Stack is: arg0 arg1 ... argN rest-list N+1 total-arg-count.
  559. ; If REST-LIST is non-empty then there are at least two arguments on the stack.
  560. (define-opcode closed-values
  561. (let* ((nargs (extract-fixnum (pop)))
  562. (stack-nargs (extract-fixnum (pop)))
  563. (rest-list (pop)))
  564. (goto return-values stack-nargs rest-list (- nargs stack-nargs))))
  565. ; Same as the above, except that the value count is in the instruction stream
  566. ; and all of the arguments are on the stack.
  567. ; This is used for in-lining calls to VALUES.
  568. (define-opcode values
  569. (goto return-values (code-offset 0) null 0))
  570. ; STACK-NARGS return values are on the stack. Find the actual continuation
  571. ; and check the protocol:
  572. ;
  573. ; 1
  574. ; If we have just one value we put it in *VAL* and return.
  575. ; ignore-values
  576. ; Drop everything and just return
  577. ; bottom-of-stack
  578. ; The real continuation is either in the stack or is FALSE (if we are really
  579. ; at the bottom of the stack). If the former we install it and try again.
  580. ; If the latter we can return a single value, but not multiple values.
  581. ; call-with-values
  582. ; Current continuation has a single value, a closure. We remove the closure
  583. ; and invoke it on the current values.
  584. (define (return-values stack-nargs list-args list-arg-count)
  585. (let* ((code-pointer (current-continuation-code-pointer))
  586. (protocol (code-pointer-ref code-pointer 1)))
  587. (assert (= (enum op protocol)
  588. (code-pointer-ref code-pointer 0)))
  589. (cond ((= protocol 1)
  590. (if (= 1 (+ stack-nargs list-arg-count))
  591. (begin
  592. (return-value->*val* stack-nargs list-args)
  593. (pop-continuation!)
  594. (goto continue 1))
  595. (goto return-exception stack-nargs list-args)))
  596. ((= protocol ignore-values-protocol)
  597. (pop-continuation!)
  598. (goto continue 1))
  599. ((native-protocol? protocol)
  600. (goto native-return-values
  601. protocol stack-nargs list-args list-arg-count))
  602. ((= protocol bottom-of-stack-protocol)
  603. (let ((cont (get-continuation-from-heap)))
  604. (cond ((continuation? cont)
  605. (copy-continuation-from-heap! cont stack-nargs)
  606. (goto return-values stack-nargs list-args list-arg-count))
  607. ((= 1 (+ stack-nargs list-arg-count))
  608. (return-value->*val* stack-nargs list-args)
  609. (goto return-from-vm cont))
  610. (else
  611. (goto return-exception stack-nargs list-args)))))
  612. ((= protocol call-with-values-protocol)
  613. (set! *val* (current-continuation-ref 0))
  614. (let ((offset (code-pointer-ref16 code-pointer 2)))
  615. (cond ((= offset 0)
  616. (skip-current-continuation! stack-nargs))
  617. (else
  618. (shrink-and-reset-continuation!
  619. (address+ code-pointer offset))
  620. (move-args-above-cont! stack-nargs))))
  621. (goto perform-application-with-rest-list
  622. stack-nargs
  623. list-args
  624. list-arg-count))
  625. ((<= protocol maximum-stack-args)
  626. (goto fixed-arg-return protocol 1
  627. stack-nargs list-args list-arg-count))
  628. ((= protocol two-byte-nargs+list-protocol)
  629. (goto nary-arg-return (code-pointer-ref16 code-pointer 2) 3
  630. stack-nargs list-args list-arg-count))
  631. ((= protocol two-byte-nargs-protocol)
  632. (goto fixed-arg-return (code-pointer-ref16 code-pointer 2) 3
  633. stack-nargs list-args list-arg-count))
  634. (else
  635. (error "unknown protocol" protocol)
  636. (goto return-exception stack-nargs list-args)))))
  637. (define (native-return-values protocol stack-nargs list-args list-arg-count)
  638. (cond ((= protocol (byte->native-protocol 1))
  639. (if (= 1 (+ stack-nargs list-arg-count))
  640. (begin
  641. (return-value->*val* stack-nargs list-args)
  642. (goto native-return 2))
  643. (goto return-exception stack-nargs list-args)))
  644. ((= protocol (byte->native-protocol ignore-values-protocol))
  645. (goto native-return 2))
  646. (else
  647. (error "unknown native return protocol" protocol)
  648. (goto return-exception stack-nargs list-args))))
  649. (define (native-return protocol-skip)
  650. (goto post-native-dispatch
  651. (s48-invoke-native-continuation
  652. (address->integer (pop-continuation-from-stack))
  653. protocol-skip)))
  654. ; The continuation wants a fixed number of arguments. We pop the current
  655. ; continuation, move the stack arguments down to the new stack top, push
  656. ; any list arguments and off we go.
  657. (define (fixed-arg-return count bytes-used stack-nargs list-args list-arg-count)
  658. (if (= count (+ stack-nargs list-arg-count))
  659. (let ((arg-top (pointer-to-stack-arguments)))
  660. (pop-continuation!)
  661. (move-stack-arguments! arg-top stack-nargs)
  662. (if (not (= 0 list-arg-count))
  663. (begin
  664. (push-list list-args list-arg-count)
  665. (unspecific))) ; avoid type problem
  666. (goto continue bytes-used))
  667. (goto return-exception stack-nargs list-args)))
  668. ; The continuation wants a COUNT arguments on the stack plus a list of any
  669. ; additional arguments. We pop the current continuation, move the stack
  670. ; arguments down to the new stack top, adjust the number of stack arguments,
  671. ; push the remaining list arguments, and off we go.
  672. (define (nary-arg-return count bytes-used stack-nargs list-args list-arg-count)
  673. (if (<= count (+ stack-nargs list-arg-count))
  674. (let ((arg-top (pointer-to-stack-arguments)))
  675. (pop-continuation!)
  676. (move-stack-arguments! arg-top stack-nargs)
  677. (push (if (<= stack-nargs count)
  678. (do ((stack-nargs stack-nargs (+ stack-nargs 1))
  679. (l list-args (vm-cdr l)))
  680. ((= count stack-nargs)
  681. l)
  682. (push (vm-car l)))
  683. (pop-args->list*+gc list-args (- stack-nargs count))))
  684. (goto continue bytes-used))
  685. (goto return-exception stack-nargs list-args)))
  686. ; Move the (single) return value to *VAL*.
  687. (define (return-value->*val* stack-nargs list-args)
  688. (set! *val*
  689. (if (= 1 stack-nargs)
  690. (pop)
  691. (vm-car list-args))))
  692. ; The return protocol doesn't match up so we gather all the return values into
  693. ; a list and raise an exception.
  694. (define (return-exception stack-nargs list-args)
  695. (let ((args (pop-args->list*+gc list-args stack-nargs)))
  696. (raise-exception wrong-number-of-arguments
  697. -1 ; no next opcode
  698. false
  699. args)))
  700. ;----------------
  701. ; Manipulating lists of arguments
  702. ; Push COUNT elements from LIST onto the stack, returning whatever is left.
  703. (define (push-list list count)
  704. (push list)
  705. (if (ensure-stack-space! count) ; This needs a better interface.
  706. (set-interrupt-flag!))
  707. (let ((list (pop)))
  708. (do ((i count (- i 1))
  709. (l list (vm-cdr l)))
  710. ((<= i 0) l)
  711. (push (vm-car l)))))
  712. ; Copy LIST, which has LENGTH elements.
  713. (define (copy-list*+gc list length)
  714. (if (= length 0)
  715. null
  716. (begin
  717. (save-temp0! list)
  718. (let* ((key (ensure-space (* vm-pair-size length)))
  719. (list (recover-temp0!))
  720. (res (vm-cons (vm-car list) null key)))
  721. (do ((l (vm-cdr list) (vm-cdr l))
  722. (last res (let ((next (vm-cons (vm-car l) null key)))
  723. (vm-set-cdr! last next)
  724. next)))
  725. ((vm-eq? null l)
  726. res))))))
  727. ; Pop COUNT arguments into a list with START as the cdr.
  728. (define (pop-args->list*+gc start count)
  729. (save-temp0! start)
  730. (let* ((key (ensure-space (* vm-pair-size count)))
  731. (start (recover-temp0!)))
  732. (do ((args start (vm-cons (pop) args key))
  733. (count count (- count 1)))
  734. ((= count 0)
  735. args))))
  736. ;----------------
  737. ; Opcodes for the closed-compiled versions of arithmetic primitives.
  738. ; The opcode sequences used are:
  739. ; binary-reduce1 binary-op binary-reduce2 return
  740. ; and
  741. ; compare-reduce1 binary-comparison-op compare-reduce2 return
  742. ; The compare version quits if the comparison operator returns false.
  743. ;
  744. ; For ...-reduce1 the stack looks like:
  745. ; arg0 arg1 ... argN rest-list N+1
  746. ; If there are two or more arguments then at least two arguments are on the
  747. ; stack.
  748. ; Turn
  749. ; *stack* = arg0 (arg1 . more) <3
  750. ; into
  751. ; *val* = arg1, *stack* = arg0 (arg1 .more) 1 arg0
  752. ; or turn
  753. ; *stack* = arg0 arg1 ... argN rest-list N+1
  754. ; into
  755. ; *val* = arg1, *stack* = false arg1 ... argN rest-list N arg0
  756. (define-opcode binary-reduce1
  757. (let ((stack-nargs (extract-fixnum (stack-ref 0))))
  758. (if (= stack-nargs 0)
  759. (let ((rest-list (stack-ref 1))
  760. (arg0 (stack-ref 2)))
  761. (push arg0)
  762. (set! *val* (vm-car rest-list)))
  763. (let ((arg0 (stack-ref (+ stack-nargs 1)))
  764. (arg1 (stack-ref stack-nargs)))
  765. (stack-set! (+ stack-nargs 1) false)
  766. (stack-set! 0 (enter-fixnum (- stack-nargs 1)))
  767. (push arg0)
  768. (set! *val* arg1)))
  769. (goto continue 0)))
  770. ; Turn
  771. ; *val* = result, *stack* = arg0 (arg1 . more) 2
  772. ; into
  773. ; *stack* = result more 2
  774. ; or turn
  775. ; *val* = result, *stack* = arg1 ... argN rest-list N
  776. ; into
  777. ; *stack* = result ... argN rest-list N
  778. (define-opcode binary-reduce2
  779. (let* ((stack-nargs (extract-fixnum (stack-ref 0)))
  780. (delta (case stack-nargs
  781. ((0)
  782. (let ((rest-list (stack-ref 1)))
  783. (if (vm-eq? (vm-cdr rest-list) null)
  784. 1
  785. (begin
  786. (stack-set! 1 (vm-cdr rest-list))
  787. (stack-set! 2 *val*)
  788. -2))))
  789. ((1)
  790. (let ((rest-list (stack-ref 1)))
  791. (if (vm-eq? rest-list null)
  792. 1
  793. (begin
  794. (stack-set! 0 (enter-fixnum 0))
  795. (stack-set! 2 *val*)
  796. -2))))
  797. (else
  798. (stack-set! (+ stack-nargs 1) *val*)
  799. -2))))
  800. (set! *code-pointer* (address+ *code-pointer* delta))
  801. (goto interpret *code-pointer*)))
  802. (define-opcode binary-comparison-reduce2
  803. (if (false? *val*)
  804. (goto continue 0)
  805. (let* ((stack-nargs (extract-fixnum (stack-ref 0)))
  806. (delta (case stack-nargs
  807. ((0)
  808. (let ((rest-list (stack-ref 1)))
  809. (if (vm-eq? (vm-cdr rest-list) null)
  810. 1
  811. (begin
  812. (stack-set! 1 (vm-cdr rest-list))
  813. (stack-set! 2 (vm-car rest-list))
  814. -2))))
  815. ((1)
  816. (let ((rest-list (stack-ref 1)))
  817. (if (vm-eq? rest-list null)
  818. 1
  819. (begin
  820. (stack-set! 0 (enter-fixnum 0))
  821. -2))))
  822. (else
  823. -2))))
  824. (set! *code-pointer* (address+ *code-pointer* delta))
  825. (goto interpret *code-pointer*))))
  826. ;----------------
  827. ; Statistics stuff
  828. ;
  829. ;(define call-stats (make-vector 16 0))
  830. ;
  831. ; (let ((i (min stack-arg-count 15)))
  832. ; (vector-set! call-stats i (+ 1 (vector-ref call-stats i))))
  833. ;
  834. ;(define plain-calls (make-vector 7 0))
  835. ;
  836. ;(define (add-plain-call i)
  837. ; (vector-set! plain-calls i (+ (vector-ref plain-calls i) 1)))
  838. ;
  839. ;(define apply-calls (make-vector 7 0))
  840. ;
  841. ;(define (add-apply-call i)
  842. ; (vector-set! apply-calls i (+ (vector-ref apply-calls i) 1)))
  843. ;
  844. ;(define (dump-call-stats)
  845. ; (let ((out (current-output-port)))
  846. ; (write-string "Calls:" out)
  847. ; (do ((i 0 (+ i 1)))
  848. ; ((= i 16))
  849. ; (write-char #\space out)
  850. ; (write-integer (vector-ref call-stats i) out))
  851. ; (write-char #\newline out)
  852. ; (write-string "Plain calls" out)
  853. ; (write-char #\newline out)
  854. ; (do ((i 0 (+ i 1)))
  855. ; ((= i 7))
  856. ; (write-char #\space out)
  857. ; (write-string (vector-ref call-strings i) out)
  858. ; (write-integer (vector-ref plain-calls i) out)
  859. ; (write-char #\newline out))
  860. ; (write-string "Apply calls" out)
  861. ; (write-char #\newline out)
  862. ; (do ((i 0 (+ i 1)))
  863. ; ((= i 7))
  864. ; (write-char #\space out)
  865. ; (write-string (vector-ref call-strings i) out)
  866. ; (write-integer (vector-ref apply-calls i) out)
  867. ; (write-char #\newline out))))
  868. ;
  869. ;(define call-strings
  870. ; '#("nary-dispatch: "
  871. ; "args&nargs: "
  872. ; "no-env: "
  873. ; "two-bytes-nargs+list: "
  874. ; "plain: "
  875. ; "two-byte-nargs: "
  876. ; "big-stack: "))