135 lines
4.3 KiB
Org Mode
135 lines
4.3 KiB
Org Mode
#+title: closure-conversion
|
|
|
|
the closure-conversion phase makes closed-over variables explicit by addition of the primitive ~make-closure~, taking a code pointer (in the CPS language, bare lambda) and the environment.
|
|
|
|
nice testable properties of closure-converted code:
|
|
- code pointers only appear in function position
|
|
- no function has free variables
|
|
|
|
multiple ~env-ref~ calls could probably be replaced with a primitive that loads the entire environment at once, returning multiple variables.
|
|
|
|
* scratchpad
|
|
|
|
** example
|
|
|
|
#+caption: scheme source
|
|
#+begin_src scheme
|
|
(letrec ((curried-add (λ (n)
|
|
(λ (m)
|
|
(+ n m)))))
|
|
((curried-add 3) 4))
|
|
#+end_src
|
|
|
|
#+caption: cps
|
|
#+begin_src scheme
|
|
(letrec ((curried-add
|
|
(λ (n ktail0)
|
|
(letrec ((curried-add-in
|
|
(λ (m ktail1)
|
|
(prim (+ n m)
|
|
(κ (x0) (continue ktail1 x0))))))
|
|
(continue ktail0 curried-add-in)))))
|
|
(letrec ((k0 (κ (adder) (adder 4 halt))))
|
|
(curried-add 3 k0)))
|
|
#+end_src
|
|
|
|
#+caption: closure-converted
|
|
#+begin_src scheme
|
|
(letrec ((curried-add
|
|
(λ (n ktail0)
|
|
(letrec ((curried-add-in-code
|
|
(λ (env m ktail1)
|
|
(prim (env-ref 0 env)
|
|
(κ (n)
|
|
(prim (+ n m)
|
|
(κ (x0) (continue ktail1 x0))))))))
|
|
(prim (make-closure curried-add-in-code n)
|
|
(κ (curried-add-in)
|
|
(continue ktail0 curried-add-in)))))))
|
|
(letrec ((k0 (κ (adder-closure)
|
|
(prim (closure-code adder-closure)
|
|
(κ (adder)
|
|
(adder adder-closure 4 halt))))))
|
|
(curried-add 3 k0)))
|
|
#+end_src
|
|
|
|
** wasm
|
|
|
|
#+begin_src scheme
|
|
(letrec ((make-adder
|
|
(lambda (n)
|
|
(lambda (x)
|
|
(+ n x)))))
|
|
((make-adder 3) 2))
|
|
#+end_src
|
|
|
|
#+begin_src scheme
|
|
(define add-code
|
|
(lambda (n env)
|
|
(+ n (env-ref env 'x))))
|
|
|
|
(define make-adder-code
|
|
(lambda (n)
|
|
(make-closure add-code ('x n))))
|
|
|
|
(define make-adder (make-closure make-adder-code))
|
|
|
|
(apply-closure (apply-closure make-addder 3) 2)
|
|
#+end_src
|
|
|
|
#+begin_src wat
|
|
(module
|
|
(type $heap-object (sub (struct (field $hash (mut i32)))))
|
|
(type $closure (sub $heap-object
|
|
(struct (field $hash (mut i32))
|
|
(field $code (ref $cont-type)))))
|
|
(type $closure1 (sub $closure
|
|
(struct (field $hash (mut i32))
|
|
(field $code (ref $cont-type))
|
|
(field $env0 (ref eq)))))
|
|
(global $arg0 (mut (ref null eq)) (ref.null eq))
|
|
(global $arg1 (mut (ref null eq)) (ref.null eq))
|
|
(global $arg2 (mut (ref null eq)) (ref.null eq))
|
|
(global $arg3 (mut (ref null eq)) (ref.null eq))
|
|
(global $arg4 (mut (ref null eq)) (ref.null eq))
|
|
(global $arg5 (mut (ref null eq)) (ref.null eq))
|
|
;; ⋮
|
|
;; (global $argn (mut (ref null eq)) (ref.null eq))
|
|
|
|
(global $current-closure (mut (ref null $closure)) (ref.null $closure))
|
|
|
|
(func $add-code (param $nargs i32)
|
|
(local $n (ref eq))
|
|
(local $x (ref eq))
|
|
(local.set $n (global.get $arg0))
|
|
(local.set $x (struct.get $closure1
|
|
(global.get $current-closure)
|
|
$env0))
|
|
(return (i32.add $n $x)))
|
|
|
|
(func $make-adder-code (param $nargs i32)
|
|
(local $n (ref eq))
|
|
(local.set $n (global.get $arg0))
|
|
(return (struct.new $closure1
|
|
0
|
|
$add-code)))
|
|
|
|
(func $main
|
|
(local.set $make-adder
|
|
(struct.new $closure
|
|
0
|
|
$make-adder-code))
|
|
(global.set $current-closure $make-adder)
|
|
(global.set $arg0 (i32.const 3))
|
|
(local.set $f (call (struct.get $closure
|
|
$make-adder
|
|
$code)
|
|
1))
|
|
(global.set $current-closure $f)
|
|
(global.set $arg0 (i32.const 2))
|
|
(return (call (struct.get $closure
|
|
$f
|
|
$code)
|
|
1))))
|
|
#+end_src
|