+75
-43
@@ -7,62 +7,94 @@ this worked quite well until it became time to implement ~call/cc~.
|
||||
we are considering making the following alterations to the VM:
|
||||
- explicitly segment the stack into frames.
|
||||
- passing procedures and return addresses on the stack.
|
||||
- new instructions:
|
||||
+ ~(tail-call /n/)~
|
||||
+ ~(call /n/)~
|
||||
+ ~(load /r/ /n/)~
|
||||
+ ~(return /n/)~
|
||||
|
||||
* scratchpad
|
||||
|
||||
** Scheme source
|
||||
|
||||
#+begin_src scheme
|
||||
(* 2 (call/cc
|
||||
(λ (cc)
|
||||
(begin (cc 6)
|
||||
3))))
|
||||
(letrec ((fac (λ (n)
|
||||
(if (zero? n)
|
||||
1
|
||||
(* n (fac (- n 1)))))))
|
||||
(fac 3))
|
||||
#+end_src
|
||||
|
||||
** CPS
|
||||
|
||||
#+begin_src scheme
|
||||
(λ (ktail0)
|
||||
(letrec ((with-cc
|
||||
(λ (cc ktail1)
|
||||
(letrec ((k0 (κ (_)
|
||||
(continue ktail1 3))))
|
||||
(cc 6 k0)))))
|
||||
(prim (call/cc with-cc)
|
||||
(κ (x1)
|
||||
(prim (* 2 x1)
|
||||
(κ (x2) (continue ktail0 x2)))))))
|
||||
(λ (ktail0)
|
||||
(letrec ((fac
|
||||
(λ (n ktail1)
|
||||
(zero?
|
||||
n
|
||||
(κ (x0)
|
||||
(if x0
|
||||
(continue ktail1 1)
|
||||
(- n 1
|
||||
(κ (x1)
|
||||
(fac x1
|
||||
(κ (x2)
|
||||
(* n x2 ktail1)))))))))))
|
||||
(fac 3)))
|
||||
#+end_src
|
||||
|
||||
** stack VM
|
||||
#+begin_example
|
||||
n ktail1
|
||||
| |
|
||||
| | x0
|
||||
| | |
|
||||
| | ^
|
||||
| |
|
||||
| | x1
|
||||
| | |
|
||||
| | ^
|
||||
| |
|
||||
| | x2
|
||||
| | |
|
||||
^ ^ ^
|
||||
#+end_example
|
||||
|
||||
#+begin_src scheme
|
||||
;; (call n) expects `n' values on the stack as arguments. then the
|
||||
;; procedure is expected at index `n', and the return continuation
|
||||
;; should be at `n+1'.
|
||||
(define $fac-c0
|
||||
(pop! %x0 0) ; [ x0 $fac-c0 n $fac ktail1 ]
|
||||
(if %x0 ; [ $fac-c0 n $fac ktail1 ]
|
||||
;; every variable but `ktail1' is dead so we pop them all.
|
||||
;; this probably means that `if' should take two continuations
|
||||
;; rather than two blocks.
|
||||
(then (push! 1) ; [ $fac-c0 n $fac ktail1 ]
|
||||
(return 1)) ; [ 1 $fac-c0 n $fac ktail1 ]
|
||||
(else (load %n 1) ; [ $fac-c0 n $fac ktail1 ]
|
||||
(prim %x1 (- %n 1)) ; [ $fac-c0 n $fac ktail1 ]
|
||||
(push! $fac-c1) ; [ $fac-c0 n $fac ktail1 ]
|
||||
(push! $fac) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
(push! %x1) ; [ $fac $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
(call 1) ; [ x1 $fac $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
)))
|
||||
|
||||
(define $k0
|
||||
(pop! %_) ; [ _ ret ]
|
||||
(push! 3) ; [ ret ]
|
||||
(tail-call 1) ; [ 3 ret ]
|
||||
)
|
||||
(define $fac-c1
|
||||
(pop! %x2) ; [ x2 $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
(load %n 3) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
(prim %x3 (* %n %x2))
|
||||
(push! %x3) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
(return 1) ; [ x3 $fac-c1 $fac-c0 n $fac ktail1 ]
|
||||
)
|
||||
|
||||
(define $with-cc
|
||||
(pop! %cc) ; [ cc ret ]
|
||||
(push! $k0) ; [ ret ]
|
||||
(push! %cc) ; [ $k0 ret ]
|
||||
(push! 6) ; [ cc $k0 ret ]
|
||||
;; call a procedure with one argument.
|
||||
(call 1) ; [ 6 cc $k0 ret ]
|
||||
)
|
||||
(define $fac
|
||||
(load %ktail1 2) ; [ n $fac ktail1 ]
|
||||
(load %n 0) ; [ n $fac ktail1 ]
|
||||
(push! $fac-c0) ; [ n $fac ktail1 ]
|
||||
(push! $zero?) ; [ $fac-c0 n $fac ktail1 ]
|
||||
(push! %n) ; [ $zero? $fac-c0 n $fac ktail1 ]
|
||||
(call 1) ; [ n $zero? $fac-c0 n $fac ktail1 ]
|
||||
)
|
||||
|
||||
(define $main
|
||||
(pop! %ktail0) ; [ ret ]
|
||||
(prim %x1 (call/cc $with-cc)) ; []
|
||||
(prim %x2 (* 2 %x1)) ; []
|
||||
(push! %ktail0) ; []
|
||||
(push! %x2) ; [ ret ]
|
||||
(tail-call 1) ; [ %x2 ret ]
|
||||
)
|
||||
(define $start
|
||||
(push! $fac) ; [ $start ktail0 ]
|
||||
(push! 3) ; [ $fac $start ktail0 ]
|
||||
(tail-call 1) ; [ 3 $fac $start ktail0 ]
|
||||
;; ↑ `tail-call' knows how to dispose of the caller's stack frame.
|
||||
)
|
||||
#+end_src
|
||||
|
||||
|
||||
Reference in New Issue
Block a user