-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement CPS & Closure Conversion on CPS
- Loading branch information
Showing
17 changed files
with
335 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
## Call Convention | ||
1. We use `ra` to store the continuation address, as it's otherwise unused in our language. We do need to push it onto stack to preserve it's value when doing a native call, though. | ||
2. We store `closure` pointer after any arguments, so we should be able to work with native functions just fine. This differs from what is being done in the book "Compiling with Continuations". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
struct CloEnv { | ||
// NOTE: fundef's type's arg's length should be one more than FuncDef.args, as | ||
// we use the last slot for closure. | ||
// and the types is recursive, BTW | ||
fnblocks : @hashmap.T[Var, FuncDef] | ||
counter : Ref[Int] | ||
bindings : @immut/hashmap.T[Var, Var] | ||
} | ||
|
||
fn CloEnv::add_rebind(self : CloEnv, name : Var, cb : Var) -> CloEnv { | ||
{ ..self, bindings: self.bindings.add(name, cb) } | ||
} | ||
|
||
fn CloEnv::new(counter : Int) -> CloEnv { | ||
let counter = { val: counter } | ||
{ fnblocks: @hashmap.new(), counter, bindings: @immut/hashmap.new() } | ||
} | ||
|
||
fn CloEnv::new_tmp(self : CloEnv, t : T) -> Var { | ||
self.counter.val = self.counter.val + 1 | ||
{ name: { val: None }, id: self.counter.val, ty: t } | ||
} | ||
|
||
// NOTE: no worry of repeated names generated as all vars are marked by an uid | ||
fn CloEnv::new_named(self : CloEnv, name : String, t : T) -> Var { | ||
self.counter.val = self.counter.val + 1 | ||
{ name: { val: Some(name) }, id: self.counter.val, ty: t } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// replace any function vars | ||
fn CloEnv::rebind_var(self : CloEnv, v : Var) -> Var { | ||
match self.bindings[v] { | ||
None => v | ||
Some(wrapped) => wrapped | ||
} | ||
} | ||
|
||
fn CloEnv::rebind_value(self : CloEnv, v : Value) -> Value { | ||
match v { | ||
Var(v) => Var(self.rebind_var(v)) | ||
v => v | ||
} | ||
} | ||
|
||
// collect all closures to top level and fix call convention | ||
fn CloEnv::collect_closure(self : CloEnv, s : S) -> S { | ||
fn rec(c : S) { | ||
self.collect_closure(c) | ||
} | ||
|
||
fn recrbva(v : Value) { | ||
self.rebind_value(v) | ||
} | ||
|
||
match s { | ||
Tuple(record, bind, rest) => | ||
Tuple( | ||
record.map(recrbva), | ||
bind, | ||
// NOTE: the reason for add all bindings is to shadow any closure rebind | ||
// so it doesn't accidentally rebind too muach than it should | ||
self.add_rebind(bind, bind).collect_closure(rest), | ||
) | ||
KthTuple(idx, v, bind, rest) => | ||
KthTuple(idx, v, bind, self.add_rebind(bind, bind).collect_closure(rest)) | ||
Switch(v, branches) => Switch(v, branches.map(rec)) | ||
Prim(op, args, bind, rest) => | ||
Prim(op, args, bind, self.add_rebind(bind, bind).collect_closure(rest)) | ||
Fix(f, args, body, rest) => { | ||
// Step 1. Calculate free variables of body | ||
let fvs = body.free_variables() | ||
fvs.remove(f) | ||
args.each(fn { a => fvs.remove(a) }) | ||
let free_vars = fvs.iter().collect() | ||
|
||
// Step 2. Calculate the free variable tuple we need to pass inside the | ||
// closure | ||
let fv_data_ty = match free_vars { | ||
[] => T::Unit | ||
_ => T::Tuple(free_vars.map(fn { v => v.ty })) | ||
} | ||
|
||
// this is the closure passed into the function | ||
let closure_ref = self.new_named( | ||
"closure_ref_\{f.to_string()}", | ||
T::Tuple([f.ty, fv_data_ty]), | ||
) | ||
|
||
// fix the type of f to accept an additional closure arg at the end | ||
guard let T::Fun(args_ty, _) = f.ty else { | ||
_ => @util.die("calling non function") | ||
// NOTE: the following alters f's type | ||
} | ||
args_ty.push(closure_ref.ty) // WARN: after this operation our ds is now self-recursive | ||
let body_fixed = match free_vars { | ||
[] => rec(body) | ||
_ => { | ||
let fn_ptr = self.new_named("fn_ptr", f.ty) | ||
let freevars = self.new_named("freevars", fv_data_ty) | ||
let body_to_wrap = self | ||
.add_rebind(f, closure_ref) | ||
.collect_closure(body) | ||
let body_with_freevars_bound = free_vars.foldi( | ||
init=body_to_wrap, | ||
fn(idx, acc, ele) { KthTuple(idx, Var(freevars), ele, acc) }, | ||
) | ||
KthTuple( | ||
0, | ||
Var(closure_ref), | ||
fn_ptr, | ||
KthTuple(1, Var(closure_ref), freevars, body_with_freevars_bound), | ||
) | ||
} | ||
} | ||
self.fnblocks[f] = { args, free_vars, body: body_fixed } | ||
let freevars_captured = self.new_named("freevars_captured", fv_data_ty) | ||
let closure_gen = self.new_named( | ||
"closure_\{f.to_string()}", | ||
T::Tuple([f.ty, fv_data_ty]), | ||
) | ||
let rest_fixed = self.add_rebind(f, closure_gen).collect_closure(rest) | ||
Tuple( | ||
free_vars.map(Value::Var), | ||
freevars_captured, | ||
Tuple([Label(f), Var(freevars_captured)], closure_gen, rest_fixed), | ||
) | ||
} | ||
App(f, args) => | ||
match f { | ||
Var(f_var) => | ||
// NOTE: always generate a call as if we're calling a closure. | ||
// Since there's no way for us to decide whether we're calling a | ||
// closure or not. | ||
match self.bindings[f_var] { | ||
Some(maybe_closure) => { | ||
args.push(Var(maybe_closure)) | ||
// we know the called function statically, so we're allowed to mark | ||
// it as a label | ||
App(Label(f_var), args) | ||
} | ||
None => { | ||
args.push(f) | ||
App(f, args) | ||
} | ||
} | ||
// NOTE: must be a native call | ||
// there's no guarantee we always use this case for all native calls | ||
Label(_) => App(f, args.map(recrbva)) | ||
_ => @util.die("Can't invoke call on \{f}") | ||
} | ||
Just(v) => Just(recrbva(v)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// we don't store the return value type as there's no return in CPS | ||
pub struct FuncDef { | ||
args : Array[Var] | ||
free_vars : Array[Var] | ||
// closure is a tuple of function pointer | ||
// and a tuple of free variables | ||
body : S | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
pub struct ClosurePS { | ||
fnblocks : @hashmap.T[Var, FuncDef] | ||
root : S | ||
counter : Ref[Int] | ||
} | ||
|
||
pub fn cps2clops(cnt : Int, s : S) -> ClosurePS { | ||
let env = CloEnv::new(cnt) | ||
let root = env.collect_closure(s) | ||
let counter = env.counter | ||
let fnblocks = env.fnblocks | ||
{ fnblocks, root, counter } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"import": [ | ||
{ | ||
"path": "moonbitlang/minimbt", | ||
"alias": "top" | ||
}, | ||
"moonbitlang/minimbt/util", | ||
"moonbitlang/minimbt/precps", | ||
"moonbitlang/minimbt/cps" | ||
], | ||
"test-import": [ | ||
"moonbitlang/minimbt/parser", | ||
"moonbitlang/minimbt/lex", | ||
"moonbitlang/minimbt/typing" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
impl Show for ClosurePS with output(self, logger) { | ||
logger.write_string(self.to_string()) | ||
} | ||
|
||
pub fn ClosurePS::to_string(self : ClosurePS) -> String { | ||
let mut output = "" | ||
for item in self.fnblocks.iter() { | ||
let (name, def) = item | ||
output += "[\{name}], args: \{def.args}, freevars: \{def.free_vars}\n" | ||
output += "\{def.body}\n\n" | ||
} | ||
output += "[root]\n\{self.root}\n" | ||
output | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
typealias T = @top.Type | ||
|
||
typealias PrimOp = @precps.PrimOp | ||
|
||
typealias S = @cps.Cps | ||
|
||
typealias Var = @cps.Var | ||
|
||
typealias Value = @cps.Value | ||
|
||
enum Either[L, R] { | ||
Left(L) | ||
Right(R) | ||
} |
Oops, something went wrong.