#+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