123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979 |
- ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
- ; Code to handle the calling and return protocols.
- ; *VAL* is the procedure, the arguments are on stack, and the next byte
- ; is the number of arguments (all of which are on the stack). This checks
- ; that *VAL* is in fact a closure and for the common case of a non-n-ary
- ; procedure that has few arguments. This case is handled directly and all
- ; others are passed off to PLAIN-PROTOCOL-MATCH.
- (define-opcode call
- (let ((stack-arg-count (code-byte 2)))
- (make-continuation-on-stack (address+ *code-pointer* (code-offset 0))
- stack-arg-count)
- (goto do-call stack-arg-count)))
- (define-opcode tail-call
- (let ((stack-arg-count (code-byte 0)))
- (move-args-above-cont! stack-arg-count)
- (goto do-call (code-byte 0))))
- (define (do-call stack-arg-count)
- (if (closure? *val*)
- (let* ((template (closure-template *val*))
- (code (template-code template))
- (protocol (code-vector-ref code 1)))
- (cond ((= protocol stack-arg-count)
- (goto run-body-with-default-space code 2 template))
- ((= (native->byte-protocol protocol)
- stack-arg-count)
- (goto call-native-code-with-default-space 2))
- (else
- (goto plain-protocol-match stack-arg-count))))
- (goto application-exception
- (enum exception bad-procedure)
- stack-arg-count null 0)))
- ;----------------------------------------------------------------
- ; Native protocols have the high bit set.
- (define (native-protocol? protocol)
- (< 127 protocol))
- (define (native->byte-protocol protocol)
- (bitwise-and protocol 127))
- (define (byte->native-protocol protocol)
- (bitwise-ior protocol 128))
- (define (call-native-code-with-default-space protocol-skip)
- (if (and (ensure-default-procedure-space!)
- (pending-interrupt?))
- (goto handle-native-interrupt protocol-skip)
- (goto really-call-native-code protocol-skip)))
- (define (call-native-code protocol-skip needed-stack-space)
- (if (and (ensure-stack-space! needed-stack-space)
- (pending-interrupt?))
- (goto handle-native-interrupt protocol-skip)
- (goto really-call-native-code protocol-skip)))
- (define (really-call-native-code protocol-skip)
- (goto post-native-dispatch (s48-call-native-procedure *val* protocol-skip)))
- (define s48-*native-protocol*)
- ; The external code just sets the C variable directly, but we export this to
- ; let the Pre-Scheme compiler know that someone, somewhere, does a set.
- (define (s48-set-native-protocol! protocol)
- (set! s48-*native-protocol* protocol))
- (define (post-native-dispatch tag)
- ; (write-string "P" (current-error-port))
- ; (write-integer tag (current-error-port))
- (let loop ((tag tag))
- (case tag
- ((0)
- (goto return-values s48-*native-protocol* null 0))
- ((1)
- (goto perform-application s48-*native-protocol*))
- ((2)
- (let* ((template (pop))
- (return-address (pop)))
- (cond ((pending-interrupt?)
- ;(write-string "interrupt is pending" (current-error-port))
- (goto handle-native-poll template return-address))
- (else
- ;(write-string "no interrupt is pending" (current-error-port))
- (loop (s48-jump-native return-address template))))))
- ((3)
- (goto return-values s48-*native-protocol* null 0))
- ((4)
- (goto interpret *code-pointer*))
- ((5)
- (goto do-apply-with-native-cont s48-*native-protocol* (pop)))
- ((6)
- (raise-exception trap 0 *val*))
- (else
- (error "unexpected native return value" tag)))))
- ;; Not used for now
- (define (push-native-exception-continuation)
- (push-continuation! (code+pc->code-pointer *native-exception-return-code*
- return-code-pc)))
- ;----------------------------------------------------------------
- ; As above but with a two-byte argument count. The tail and not-tail calls
- ; are both done using the same opcode (which is not done above for speed in
- ; the tail case; this optimization needs to be tested for effectiveness).
- (define-opcode big-call
- (let ((stack-arg-count (code-offset 2)))
- (maybe-make-continuation stack-arg-count)
- (goto perform-application stack-arg-count)))
- ; A number of opcodes that make calls use this. We get a the offset of the
- ; return address and then either add it as a new continuation below the current
- ; arguments or, for tail calls, move those arguments above the current
- ; continuation.
- (define (maybe-make-continuation stack-arg-count)
- (let ((return-pointer-offset (code-offset 0)))
- (if (= return-pointer-offset 0)
- (move-args-above-cont! stack-arg-count)
- (make-continuation-on-stack (address+ *code-pointer*
- return-pointer-offset)
- stack-arg-count))))
- (define (maybe-make-native-continuation stack-arg-count maybe-cont)
- (if (= maybe-cont 0)
- (move-args-above-cont! stack-arg-count)
- (make-continuation-on-stack (integer->address maybe-cont)
- stack-arg-count)))
- ; Call a template instead of a procedure. This is currently used for
- ; stringing together code for top-level forms and doing the same thing for
- ; the initialization code made by the static linker.
- ;
- ; call-template <return-offset> <template-index> <index-within-template> <nargs>
- (define-opcode call-template
- (let* ((template (get-literal 2))
- (code (template-code template))
- (nargs (code-byte 6)))
- (maybe-make-continuation nargs)
- (cond ((= nargs (code-vector-ref code 1))
- (goto run-body-with-default-space code 2 template))
- ((and (= big-stack-protocol (code-vector-ref code 1))
- (= nargs
- (code-vector-ref code (- (code-vector-length code) 3))))
- (goto run-body
- code
- 2
- template
- (code-vector-ref16 code (- (code-vector-length code) 2))))
- (else
- (raise-exception wrong-type-argument 7 template)))))
- ; The following is used only for experiments. The compiler does not use it.
- ;(define-opcode goto-template
- ; (set-code-pointer! (template-code template) 0)
- ; (goto interpret *code-pointer*))
- ; APPLY: *VAL* is the procedure, the rest-arg list is on top of the stack,
- ; the next two bytes are the number of stack arguments below the rest-args list.
- ; We check that the rest-arg list is a proper list and let
- ; PERFORM-APPLICATION-WITH-REST-LIST do the work.
-
- (define-opcode apply
- (let ((list-args (pop))
- (stack-nargs (code-offset 2)))
- (receive (okay? length)
- (okay-argument-list list-args)
- (if okay?
- (begin
- (maybe-make-continuation stack-nargs)
- (goto perform-application-with-rest-list
- stack-nargs
- list-args
- length))
- (let ((args (pop-args->list*+gc list-args stack-nargs)))
- (raise-exception wrong-type-argument -1 *val* args))))))
- (define (do-apply-with-native-cont stack-nargs maybe-cont)
- (let ((list-args (pop)))
- (receive (okay? length)
- (okay-argument-list list-args)
- (if okay?
- (begin
- (maybe-make-native-continuation stack-nargs maybe-cont)
- (goto perform-application-with-rest-list
- stack-nargs
- list-args
- length))
- (let ((args (pop-args->list*+gc list-args stack-nargs)))
- (raise-exception wrong-type-argument -1 *val* args))))))
-
- ; This is only used for the closed-compiled version of APPLY.
- ;
- ; Stack = arg0 arg1 ... argN rest-list N+1 total-arg-count
- ; Arg0 is the procedure.
- ;
- ; Note that the rest-list on the stack is the rest-list passed to APPLY
- ; procedure and not the rest-list to be used in the call to the procedure.
- ; Consider (APPLY APPLY (LIST LIST '(1 2 3))), where the initial APPLY
- ; is not done in-line. The stack for the inner call to APPLY will be
- ; [(<list-procedure> (1 2 3)), 1, 2], whereas for
- ; (APPLY APPLY LIST 1 '(2 (3))) the stack will be
- ; [<list-procedure>, 1, (2 (3)), 2, 4].
- ;
- ; We grab the counts and the procedure and copy the rest of the stack arguments
- ; down to make us properly tail-recursive. Then we get the true stack-arg count
- ; and list args and again let PERFORM-APPLICATION-WITH-REST-LIST do the work.
- (define-opcode closed-apply
- (let* ((nargs (extract-fixnum (pop)))
- (stack-nargs (extract-fixnum (pop))))
- (set! *val* (stack-ref stack-nargs)) ; proc in *VAL*
- (move-args-above-cont! stack-nargs)
- (receive (okay? stack-arg-count list-args list-arg-count)
- (get-closed-apply-args nargs stack-nargs)
- (if okay?
- (begin
- (goto perform-application-with-rest-list
- stack-arg-count
- list-args
- list-arg-count))
- (let ((args (pop-args->list*+gc list-args stack-arg-count)))
- (raise-exception wrong-type-argument -1 *val* args))))))
- ; Stack = arg0 arg1 ... argN rest-list
- ; This needs to get the last argument, which is either argN or the last
- ; element of the rest-list, and splice it into the rest of the arguments.
- ; If the rest-list is null, then argN is the last argument and becomes the
- ; new rest-list. If the rest-list is non-null, then we go to the end, get
- ; the list there, and splice the two together to make a single list.
- ; This only happens if someone does (APPLY APPLY ...).
- (define (get-closed-apply-args nargs stack-nargs)
- (let ((rest-list (pop)))
- (receive (list-args stack-nargs)
- (cond ((vm-eq? rest-list null)
- (values (pop)
- (- stack-nargs 2))) ; drop proc and final list
- ((vm-eq? (vm-cdr rest-list) null)
- (values (vm-car rest-list)
- (- stack-nargs 1))) ; drop proc
- (else
- (let* ((penultimate-cdr (penultimate-cdr rest-list))
- (list-args (vm-car (vm-cdr penultimate-cdr))))
- (vm-set-cdr! penultimate-cdr list-args)
- (values rest-list
- (- stack-nargs 1))))) ; drop proc
- (receive (okay? list-arg-count)
- (okay-argument-list list-args)
- (values okay?
- stack-nargs
- list-args
- list-arg-count)))))
- ; If LIST is a proper list (the final cdr is null) then we return #T and the
- ; length of the list, otherwise we return #F.
- (define (okay-argument-list list)
- (let loop ((fast list) (len 0) (slow list) (move-slow? #f))
- (cond ((vm-eq? null fast)
- (values #t len))
- ((or (not (vm-pair? fast)))
- (values #f 0))
- ((not move-slow?)
- (loop (vm-cdr fast) (+ len 1) slow #t))
- ((vm-eq? fast slow)
- (values #f 0))
- (else
- (loop (vm-cdr fast) (+ len 1) (vm-cdr slow) #f)))))
- ; Return the second-to-last cdr of LIST.
- (define (penultimate-cdr list)
- (let loop ((list (vm-cdr (vm-cdr list))) (follower list))
- (if (eq? null list)
- follower
- (loop (vm-cdr list) (vm-cdr follower)))))
- ;----------------
- ; Call the procedure in *VAL*. STACK-ARG-COUNT is the number of arguments
- ; on the stack, LIST-ARGS is a list of LIST-ARG-COUNT additional arguments.
- ;
- ; The CLOSURE? and protocol checks must come before the interrupt check because
- ; the interrupt code assumes that the correct template is in place. This delays
- ; the handling of interrupts by a few instructions; it shouldn't matter.
- (define (perform-application stack-arg-count)
- (if (closure? *val*)
- (goto plain-protocol-match stack-arg-count)
- (goto application-exception
- (enum exception bad-procedure)
- stack-arg-count null 0)))
- (define (perform-application-with-rest-list stack-arg-count
- list-args list-arg-count)
- (cond ((= 0 list-arg-count)
- (goto perform-application stack-arg-count))
- ((closure? *val*)
- (goto list-protocol-match
- stack-arg-count list-args list-arg-count))
- (else
- (goto application-exception
- (enum exception bad-procedure)
- stack-arg-count list-args list-arg-count))))
- (define (wrong-nargs stack-arg-count list-args list-arg-count)
- (goto application-exception
- (enum exception wrong-number-of-arguments)
- stack-arg-count list-args list-arg-count))
- ; The main protocol-matching function takes as an argument a token indicating
- ; if the called-value is a handler and if so, what kind. A non-negative value
- ; is the opcode whose exception handler is begin called. -1 means that the
- ; procedure is not a handler. Any other negative value indicates that the
- ; procedure is an interrupt handler. The interrupt is (- -2 token).
- (define not-a-handler -1)
- (define (call-exception-handler stack-arg-count opcode)
- (goto real-protocol-match
- stack-arg-count
- null
- 0
- opcode))
-
- (define (call-interrupt-handler stack-arg-count interrupt)
- (goto real-protocol-match
- stack-arg-count
- null
- 0
- (- -2 interrupt)))
- ; Check that the arguments match those needed by *VAL*, which is a closure,
- ; moving arguments to or from the stack if necessary, and ensure that there
- ; is enough stack space for the procedure. The environment needed by *VAL*
- ; is created.
- (define (plain-protocol-match stack-arg-count)
- (goto real-protocol-match stack-arg-count null 0 not-a-handler))
- (define (list-protocol-match stack-arg-count list-args list-arg-count)
- (goto real-protocol-match
- stack-arg-count
- list-args
- list-arg-count
- not-a-handler))
- (define (real-protocol-match stack-arg-count
- list-args
- list-arg-count
- handler-tag)
- (let ((code (template-code (closure-template *val*)))
- (total-arg-count (+ stack-arg-count list-arg-count))
- (lose (lambda ()
- (cond ((= handler-tag not-a-handler)
- (goto wrong-nargs
- stack-arg-count list-args list-arg-count))
- ((<= 0 handler-tag)
- (error "wrong number of arguments to exception handler"
- handler-tag))
- (else
- (error "wrong number of arguments to interrupt handler"
- (- -2 handler-tag)))))))
- (assert (= (enum op protocol)
- (code-vector-ref code 0)))
- (let loop ((protocol (code-vector-ref code 1))
- (stack-space default-stack-space)
- (native? #f))
- (let ((win (lambda (skip stack-arg-count)
- (if native?
- (goto call-native-code skip stack-space)
- (let ((template (closure-template *val*)))
- (goto run-body (template-code template)
- skip
- template
- stack-space))))))
- (let ((fixed-match (lambda (wants skip)
- (if (= wants total-arg-count)
- (begin
- (if (not (= 0 list-arg-count))
- (begin
- (push-list list-args list-arg-count)
- (unspecific))) ; avoid type problem
- (win skip total-arg-count))
- (lose))))
- ;; N-ary procedure.
- (n-ary-match (lambda (wants-stack-args skip)
- (if (<= wants-stack-args total-arg-count)
- (begin
- (rest-list-setup+gc wants-stack-args
- stack-arg-count
- list-args
- list-arg-count)
- (win skip (+ wants-stack-args 1)))
- (lose))))
- ;; If there are > 2 args the top two are pushed on the stack.
- ;; Then the remaining list, the total number of arguments, and
- ;; the number on the stack are pushed on the stack.
- (args+nargs-match (lambda (skip)
- (let ((final-stack-arg-count
- (if (< total-arg-count 3)
- total-arg-count
- (max 2 stack-arg-count))))
- (rest-list-setup+gc (max stack-arg-count
- final-stack-arg-count)
- stack-arg-count
- list-args
- list-arg-count)
- (push (enter-fixnum final-stack-arg-count))
- (push (enter-fixnum total-arg-count))
- (win skip (+ final-stack-arg-count 3))))))
- (cond ((= protocol nary-dispatch-protocol)
- (cond ((< total-arg-count 3)
- (let ((skip (code-vector-ref code (+ 3 total-arg-count))))
- (if (= 0 skip)
- (lose)
- (begin
- (push-list list-args list-arg-count)
- (goto run-nary-dispatch-body code skip)))))
- ((= 0 (code-vector-ref code 2))
- (lose))
- (else
- (args+nargs-match 6)))) ; leave env/template
- ((<= protocol maximum-stack-args)
- (fixed-match protocol 2))
- ((= protocol two-byte-nargs+list-protocol)
- (n-ary-match (code-vector-ref16 code 2) 4))
- ((= protocol args+nargs-protocol)
- (if (>= total-arg-count
- (code-vector-ref code 2))
- (args+nargs-match 3)
- (lose)))
- ((native-protocol? protocol)
- (loop (native->byte-protocol protocol) stack-space #t))
- ((= protocol two-byte-nargs-protocol)
- (fixed-match (code-vector-ref16 code 2) 4))
- ((= protocol big-stack-protocol)
- (let ((length (code-vector-length code)))
- (loop (code-vector-ref code (- length 3))
- (code-vector-ref16 code (- length 2))
- native?)))
- (else
- (error "unknown protocol" protocol)
- (lose))))))))
- ; Adjusts the number of stack arguments to be WANTS-STACK-ARGS by moving
- ; arguments between the stack and LIST-ARGS as necessary. Whatever is left
- ; of LIST-ARGS is then copied and the copy is pushed onto the stack.
- (define (rest-list-setup+gc wants-stack-args stack-arg-count
- list-args list-arg-count)
- (cond ((= stack-arg-count wants-stack-args)
- (push (copy-list*+gc list-args list-arg-count)))
- ((< stack-arg-count wants-stack-args)
- (let ((count (- wants-stack-args stack-arg-count)))
- (push (copy-list*+gc (push-list list-args count)
- (- list-arg-count count)))))
- (else ; (> stack-arg-count wants-stack-args)
- (let ((count (- stack-arg-count wants-stack-args)))
- (push (pop-args->list*+gc (copy-list*+gc list-args list-arg-count)
- count))))))
- ; Raise an exception, passing to it a list of the arguments on the stack and
- ; in LIST-ARGS.
- (define (application-exception exception
- stack-arg-count list-args list-arg-count)
- (let ((args (pop-args->list*+gc (copy-list*+gc list-args list-arg-count)
- stack-arg-count)))
- (raise-exception* exception -1 *val* args))) ; no next opcode
- ;----------------------------------------------------------------
- (define (run-body-with-default-space code used template)
- (real-run-body-with-default-space code used (+ used 1) template))
- (define (run-nary-dispatch-body code start-pc)
- (real-run-body-with-default-space code 6 start-pc (closure-template *val*)))
- (define (real-run-body-with-default-space code env/temp-offset used template)
- (env-and-template-setup (code-vector-ref code env/temp-offset) template)
- (set-code-pointer! code used)
- (if (and (ensure-default-procedure-space!)
- (pending-interrupt?))
- (goto handle-interrupt)
- (goto interpret *code-pointer*)))
- (define (run-body code used template needed-stack-space)
- (env-and-template-setup (code-vector-ref code used) template)
- (set-code-pointer! code (+ used 1))
- (if (and (ensure-stack-space! needed-stack-space)
- (pending-interrupt?))
- (goto handle-interrupt)
- (goto interpret *code-pointer*)))
- (define (env-and-template-setup spec template)
- (cond ((= #b011 spec)
- (push (closure-env *val*))
- (push template))
- ((= #b001 spec)
- (push template))
- ((= #b010 spec)
- (push (closure-env *val*)))
- ;; the next two are for the output of the optimizer,
- ;; for closures that have the environment merged in
- ((= #b100 spec)
- (push *val*)) ; closure
- ((= #b110 spec)
- (push *val*)
- (push (closure-env *val*)))
- ;; the following probably won't occur in the wild
- ((= #b101 spec)
- (push *val*)
- (push template))
- ((= #b111 spec)
- (push *val*)
- (push (closure-env *val*))
- (push template))))
- ;----------------------------------------------------------------
- ; Get a two-byte number from CODE-VECTOR.
- (define (code-vector-ref16 code-vector index)
- (let ((high (code-vector-ref code-vector index)))
- (adjoin-bits high
- (code-vector-ref code-vector (+ index 1))
- bits-used-per-byte)))
- (define (code-pointer-ref code-pointer index)
- (fetch-byte (address+ code-pointer index)))
- (define (code-pointer-ref16 code-pointer index)
- (let ((high (code-pointer-ref code-pointer index)))
- (adjoin-bits high
- (code-pointer-ref code-pointer (+ index 1))
- bits-used-per-byte)))
- ;----------------
- ; Returns - these use many of the same protocols.
- ; Invoke the contination, if it can handle a single value. There are four
- ; protocols that are okay:
- ;
- ; 1, ignore-values
- ; We just leave *VAL* as is and return.
- ; bottom-of-stack
- ; Real continuation is either in the heap or #F (if we are really at the
- ; bottom of the stack). We get the real continuation and either try again
- ; or return from the VM.
- ; two-byte-nargs+list
- ; Continuation is n-ary. If it want 0 or 1 value on the stack we are okay
- ; and do the setup and return.
- (define-opcode return
- (let loop ()
- (let ((code-pointer (current-continuation-code-pointer)))
- (assert (= (enum op protocol)
- (code-pointer-ref code-pointer 0)))
- (let ((protocol (code-pointer-ref code-pointer 1)))
- (cond ((or (= protocol 1)
- (= protocol ignore-values-protocol))
- (pop-continuation!)
- (goto continue 1)) ; one protocol byte
- ((or (= protocol (byte->native-protocol 1))
- (= protocol (byte->native-protocol
- ignore-values-protocol)))
- (goto native-return 2))
- ((= protocol bottom-of-stack-protocol)
- (let ((cont (get-continuation-from-heap)))
- (if (continuation? cont)
- (begin
- (copy-continuation-from-heap! cont 0)
- (loop))
- (goto return-from-vm cont))))
- ((= protocol call-with-values-protocol)
- (let ((proc (current-continuation-ref 0))
- (offset (code-pointer-ref16 code-pointer 2)))
- (if (= offset 0)
- (skip-current-continuation! 0) ; we're done with it
- (begin
- (shrink-and-reset-continuation!
- (address+ code-pointer offset))
- (remove-current-frame)))
- (push *val*)
- (set! *val* proc)
- (goto perform-application-with-rest-list 1 null 0)))
- ((= protocol two-byte-nargs+list-protocol)
- (let ((wants-stack-args (code-pointer-ref16 code-pointer 2)))
- (cond ((= 0 wants-stack-args)
- (pop-continuation!)
- (push (vm-cons *val* null (ensure-space vm-pair-size)))
- (goto continue 3))
- ((= 1 wants-stack-args)
- (pop-continuation!)
- (push *val*)
- (push null)
- (goto continue 3))
- (else
- (push *val*)
- (goto return-exception 1 null)))))
- (else
- (push *val*)
- (goto return-exception 1 null)))))))
- ; CONT is not a continuation. If it is false and *VAL* is a fixnum we can
- ; return from the VM. Otherwise we set the continuation to #F and raise an
- ; exception.
- (define (return-from-vm cont)
- (cond ((and (false? cont)
- (fixnum? *val*))
- (set! s48-*callback-return-stack-block* false) ; not from a callback
- (extract-fixnum *val*)) ; VM returns here
- (else
- (set-current-continuation! false)
- (raise-exception wrong-type-argument -1 *val* cont))))
- ; This is only used in the closed-compiled version of VALUES.
- ; Stack is: arg0 arg1 ... argN rest-list N+1 total-arg-count.
- ; If REST-LIST is non-empty then there are at least two arguments on the stack.
- (define-opcode closed-values
- (let* ((nargs (extract-fixnum (pop)))
- (stack-nargs (extract-fixnum (pop)))
- (rest-list (pop)))
- (goto return-values stack-nargs rest-list (- nargs stack-nargs))))
- ; Same as the above, except that the value count is in the instruction stream
- ; and all of the arguments are on the stack.
- ; This is used for in-lining calls to VALUES.
- (define-opcode values
- (goto return-values (code-offset 0) null 0))
- ; STACK-NARGS return values are on the stack. Find the actual continuation
- ; and check the protocol:
- ;
- ; 1
- ; If we have just one value we put it in *VAL* and return.
- ; ignore-values
- ; Drop everything and just return
- ; bottom-of-stack
- ; The real continuation is either in the stack or is FALSE (if we are really
- ; at the bottom of the stack). If the former we install it and try again.
- ; If the latter we can return a single value, but not multiple values.
- ; call-with-values
- ; Current continuation has a single value, a closure. We remove the closure
- ; and invoke it on the current values.
- (define (return-values stack-nargs list-args list-arg-count)
- (let* ((code-pointer (current-continuation-code-pointer))
- (protocol (code-pointer-ref code-pointer 1)))
- (assert (= (enum op protocol)
- (code-pointer-ref code-pointer 0)))
- (cond ((= protocol 1)
- (if (= 1 (+ stack-nargs list-arg-count))
- (begin
- (return-value->*val* stack-nargs list-args)
- (pop-continuation!)
- (goto continue 1))
- (goto return-exception stack-nargs list-args)))
- ((= protocol ignore-values-protocol)
- (pop-continuation!)
- (goto continue 1))
- ((native-protocol? protocol)
- (goto native-return-values
- protocol stack-nargs list-args list-arg-count))
- ((= protocol bottom-of-stack-protocol)
- (let ((cont (get-continuation-from-heap)))
- (cond ((continuation? cont)
- (copy-continuation-from-heap! cont stack-nargs)
- (goto return-values stack-nargs list-args list-arg-count))
- ((= 1 (+ stack-nargs list-arg-count))
- (return-value->*val* stack-nargs list-args)
- (goto return-from-vm cont))
- (else
- (goto return-exception stack-nargs list-args)))))
- ((= protocol call-with-values-protocol)
- (set! *val* (current-continuation-ref 0))
- (let ((offset (code-pointer-ref16 code-pointer 2)))
- (cond ((= offset 0)
- (skip-current-continuation! stack-nargs))
- (else
- (shrink-and-reset-continuation!
- (address+ code-pointer offset))
- (move-args-above-cont! stack-nargs))))
- (goto perform-application-with-rest-list
- stack-nargs
- list-args
- list-arg-count))
- ((<= protocol maximum-stack-args)
- (goto fixed-arg-return protocol 1
- stack-nargs list-args list-arg-count))
- ((= protocol two-byte-nargs+list-protocol)
- (goto nary-arg-return (code-pointer-ref16 code-pointer 2) 3
- stack-nargs list-args list-arg-count))
- ((= protocol two-byte-nargs-protocol)
- (goto fixed-arg-return (code-pointer-ref16 code-pointer 2) 3
- stack-nargs list-args list-arg-count))
- (else
- (error "unknown protocol" protocol)
- (goto return-exception stack-nargs list-args)))))
- (define (native-return-values protocol stack-nargs list-args list-arg-count)
- (cond ((= protocol (byte->native-protocol 1))
- (if (= 1 (+ stack-nargs list-arg-count))
- (begin
- (return-value->*val* stack-nargs list-args)
- (goto native-return 2))
- (goto return-exception stack-nargs list-args)))
- ((= protocol (byte->native-protocol ignore-values-protocol))
- (goto native-return 2))
- (else
- (error "unknown native return protocol" protocol)
- (goto return-exception stack-nargs list-args))))
- (define (native-return protocol-skip)
- (goto post-native-dispatch
- (s48-invoke-native-continuation
- (address->integer (pop-continuation-from-stack))
- protocol-skip)))
-
- ; The continuation wants a fixed number of arguments. We pop the current
- ; continuation, move the stack arguments down to the new stack top, push
- ; any list arguments and off we go.
- (define (fixed-arg-return count bytes-used stack-nargs list-args list-arg-count)
- (if (= count (+ stack-nargs list-arg-count))
- (let ((arg-top (pointer-to-stack-arguments)))
- (pop-continuation!)
- (move-stack-arguments! arg-top stack-nargs)
- (if (not (= 0 list-arg-count))
- (begin
- (push-list list-args list-arg-count)
- (unspecific))) ; avoid type problem
- (goto continue bytes-used))
- (goto return-exception stack-nargs list-args)))
- ; The continuation wants a COUNT arguments on the stack plus a list of any
- ; additional arguments. We pop the current continuation, move the stack
- ; arguments down to the new stack top, adjust the number of stack arguments,
- ; push the remaining list arguments, and off we go.
- (define (nary-arg-return count bytes-used stack-nargs list-args list-arg-count)
- (if (<= count (+ stack-nargs list-arg-count))
- (let ((arg-top (pointer-to-stack-arguments)))
- (pop-continuation!)
- (move-stack-arguments! arg-top stack-nargs)
- (push (if (<= stack-nargs count)
- (do ((stack-nargs stack-nargs (+ stack-nargs 1))
- (l list-args (vm-cdr l)))
- ((= count stack-nargs)
- l)
- (push (vm-car l)))
- (pop-args->list*+gc list-args (- stack-nargs count))))
- (goto continue bytes-used))
- (goto return-exception stack-nargs list-args)))
- ; Move the (single) return value to *VAL*.
- (define (return-value->*val* stack-nargs list-args)
- (set! *val*
- (if (= 1 stack-nargs)
- (pop)
- (vm-car list-args))))
- ; The return protocol doesn't match up so we gather all the return values into
- ; a list and raise an exception.
- (define (return-exception stack-nargs list-args)
- (let ((args (pop-args->list*+gc list-args stack-nargs)))
- (raise-exception wrong-number-of-arguments
- -1 ; no next opcode
- false
- args)))
- ;----------------
- ; Manipulating lists of arguments
- ; Push COUNT elements from LIST onto the stack, returning whatever is left.
- (define (push-list list count)
- (push list)
- (if (ensure-stack-space! count) ; This needs a better interface.
- (set-interrupt-flag!))
- (let ((list (pop)))
- (do ((i count (- i 1))
- (l list (vm-cdr l)))
- ((<= i 0) l)
- (push (vm-car l)))))
- ; Copy LIST, which has LENGTH elements.
- (define (copy-list*+gc list length)
- (if (= length 0)
- null
- (begin
- (save-temp0! list)
- (let* ((key (ensure-space (* vm-pair-size length)))
- (list (recover-temp0!))
- (res (vm-cons (vm-car list) null key)))
- (do ((l (vm-cdr list) (vm-cdr l))
- (last res (let ((next (vm-cons (vm-car l) null key)))
- (vm-set-cdr! last next)
- next)))
- ((vm-eq? null l)
- res))))))
- ; Pop COUNT arguments into a list with START as the cdr.
- (define (pop-args->list*+gc start count)
- (save-temp0! start)
- (let* ((key (ensure-space (* vm-pair-size count)))
- (start (recover-temp0!)))
- (do ((args start (vm-cons (pop) args key))
- (count count (- count 1)))
- ((= count 0)
- args))))
- ;----------------
- ; Opcodes for the closed-compiled versions of arithmetic primitives.
- ; The opcode sequences used are:
- ; binary-reduce1 binary-op binary-reduce2 return
- ; and
- ; compare-reduce1 binary-comparison-op compare-reduce2 return
- ; The compare version quits if the comparison operator returns false.
- ;
- ; For ...-reduce1 the stack looks like:
- ; arg0 arg1 ... argN rest-list N+1
- ; If there are two or more arguments then at least two arguments are on the
- ; stack.
- ; Turn
- ; *stack* = arg0 (arg1 . more) <3
- ; into
- ; *val* = arg1, *stack* = arg0 (arg1 .more) 1 arg0
- ; or turn
- ; *stack* = arg0 arg1 ... argN rest-list N+1
- ; into
- ; *val* = arg1, *stack* = false arg1 ... argN rest-list N arg0
- (define-opcode binary-reduce1
- (let ((stack-nargs (extract-fixnum (stack-ref 0))))
- (if (= stack-nargs 0)
- (let ((rest-list (stack-ref 1))
- (arg0 (stack-ref 2)))
- (push arg0)
- (set! *val* (vm-car rest-list)))
- (let ((arg0 (stack-ref (+ stack-nargs 1)))
- (arg1 (stack-ref stack-nargs)))
- (stack-set! (+ stack-nargs 1) false)
- (stack-set! 0 (enter-fixnum (- stack-nargs 1)))
- (push arg0)
- (set! *val* arg1)))
- (goto continue 0)))
- ; Turn
- ; *val* = result, *stack* = arg0 (arg1 . more) 2
- ; into
- ; *stack* = result more 2
- ; or turn
- ; *val* = result, *stack* = arg1 ... argN rest-list N
- ; into
- ; *stack* = result ... argN rest-list N
- (define-opcode binary-reduce2
- (let* ((stack-nargs (extract-fixnum (stack-ref 0)))
- (delta (case stack-nargs
- ((0)
- (let ((rest-list (stack-ref 1)))
- (if (vm-eq? (vm-cdr rest-list) null)
- 1
- (begin
- (stack-set! 1 (vm-cdr rest-list))
- (stack-set! 2 *val*)
- -2))))
- ((1)
- (let ((rest-list (stack-ref 1)))
- (if (vm-eq? rest-list null)
- 1
- (begin
- (stack-set! 0 (enter-fixnum 0))
- (stack-set! 2 *val*)
- -2))))
- (else
- (stack-set! (+ stack-nargs 1) *val*)
- -2))))
- (set! *code-pointer* (address+ *code-pointer* delta))
- (goto interpret *code-pointer*)))
- (define-opcode binary-comparison-reduce2
- (if (false? *val*)
- (goto continue 0)
- (let* ((stack-nargs (extract-fixnum (stack-ref 0)))
- (delta (case stack-nargs
- ((0)
- (let ((rest-list (stack-ref 1)))
- (if (vm-eq? (vm-cdr rest-list) null)
- 1
- (begin
- (stack-set! 1 (vm-cdr rest-list))
- (stack-set! 2 (vm-car rest-list))
- -2))))
- ((1)
- (let ((rest-list (stack-ref 1)))
- (if (vm-eq? rest-list null)
- 1
- (begin
- (stack-set! 0 (enter-fixnum 0))
- -2))))
- (else
- -2))))
- (set! *code-pointer* (address+ *code-pointer* delta))
- (goto interpret *code-pointer*))))
- ;----------------
- ; Statistics stuff
- ;
- ;(define call-stats (make-vector 16 0))
- ;
- ; (let ((i (min stack-arg-count 15)))
- ; (vector-set! call-stats i (+ 1 (vector-ref call-stats i))))
- ;
- ;(define plain-calls (make-vector 7 0))
- ;
- ;(define (add-plain-call i)
- ; (vector-set! plain-calls i (+ (vector-ref plain-calls i) 1)))
- ;
- ;(define apply-calls (make-vector 7 0))
- ;
- ;(define (add-apply-call i)
- ; (vector-set! apply-calls i (+ (vector-ref apply-calls i) 1)))
- ;
- ;(define (dump-call-stats)
- ; (let ((out (current-output-port)))
- ; (write-string "Calls:" out)
- ; (do ((i 0 (+ i 1)))
- ; ((= i 16))
- ; (write-char #\space out)
- ; (write-integer (vector-ref call-stats i) out))
- ; (write-char #\newline out)
- ; (write-string "Plain calls" out)
- ; (write-char #\newline out)
- ; (do ((i 0 (+ i 1)))
- ; ((= i 7))
- ; (write-char #\space out)
- ; (write-string (vector-ref call-strings i) out)
- ; (write-integer (vector-ref plain-calls i) out)
- ; (write-char #\newline out))
- ; (write-string "Apply calls" out)
- ; (write-char #\newline out)
- ; (do ((i 0 (+ i 1)))
- ; ((= i 7))
- ; (write-char #\space out)
- ; (write-string (vector-ref call-strings i) out)
- ; (write-integer (vector-ref apply-calls i) out)
- ; (write-char #\newline out))))
- ;
- ;(define call-strings
- ; '#("nary-dispatch: "
- ; "args&nargs: "
- ; "no-env: "
- ; "two-bytes-nargs+list: "
- ; "plain: "
- ; "two-byte-nargs: "
- ; "big-stack: "))
-
|