Files
gyehoek-hs/doc/closure-conversion.org
T
2026-08-20 01:05:16 -06:00

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