mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
reset reuse stuff
This commit is contained in:
parent
f02d907f17
commit
b849a3019f
5 changed files with 265 additions and 599 deletions
|
@ -292,6 +292,19 @@ impl<'a> BorrowInfState<'a> {
|
||||||
// the function must take it as an owned parameter
|
// the function must take it as an owned parameter
|
||||||
self.own_args_if_param(xs);
|
self.own_args_if_param(xs);
|
||||||
}
|
}
|
||||||
|
Reset(x) => {
|
||||||
|
self.own_var(z);
|
||||||
|
self.own_var(*x);
|
||||||
|
}
|
||||||
|
Reuse {
|
||||||
|
symbol: x,
|
||||||
|
arguments: ys,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.own_var(z);
|
||||||
|
self.own_var(*x);
|
||||||
|
self.own_args_if_param(ys);
|
||||||
|
}
|
||||||
EmptyArray => {
|
EmptyArray => {
|
||||||
self.own_var(z);
|
self.own_var(z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,15 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
} => {
|
} => {
|
||||||
result.extend(arguments.iter().copied());
|
result.extend(arguments.iter().copied());
|
||||||
}
|
}
|
||||||
|
Reuse {
|
||||||
|
symbol, arguments, ..
|
||||||
|
} => {
|
||||||
|
result.extend(arguments.iter().copied());
|
||||||
|
result.insert(*symbol);
|
||||||
|
}
|
||||||
|
Reset(x) => {
|
||||||
|
result.insert(*x);
|
||||||
|
}
|
||||||
RunLowLevel(_, args) => {
|
RunLowLevel(_, args) => {
|
||||||
result.extend(args.iter());
|
result.extend(args.iter());
|
||||||
}
|
}
|
||||||
|
@ -413,12 +421,14 @@ impl<'a> Context<'a> {
|
||||||
live_vars.remove(&z);
|
live_vars.remove(&z);
|
||||||
|
|
||||||
let new_b = match v {
|
let new_b = match v {
|
||||||
Tag { arguments: ys, .. } | Struct(ys) | Array { elems: ys, .. } => self
|
Reuse { arguments: ys, .. }
|
||||||
.add_inc_before_consume_all(
|
| Tag { arguments: ys, .. }
|
||||||
ys,
|
| Struct(ys)
|
||||||
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
| Array { elems: ys, .. } => self.add_inc_before_consume_all(
|
||||||
&b_live_vars,
|
ys,
|
||||||
),
|
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
||||||
|
&b_live_vars,
|
||||||
|
),
|
||||||
AccessAtIndex { structure: x, .. } => {
|
AccessAtIndex { structure: x, .. } => {
|
||||||
let b = self.add_dec_if_needed(x, b, b_live_vars);
|
let b = self.add_dec_if_needed(x, b, b_live_vars);
|
||||||
let info_x = self.get_var_info(x);
|
let info_x = self.get_var_info(x);
|
||||||
|
@ -464,7 +474,11 @@ impl<'a> Context<'a> {
|
||||||
self.add_inc_before(ys, ps, b, b_live_vars)
|
self.add_inc_before(ys, ps, b, b_live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyArray | FunctionPointer(_, _) | Literal(_) | RuntimeErrorFunction(_) => {
|
EmptyArray
|
||||||
|
| FunctionPointer(_, _)
|
||||||
|
| Literal(_)
|
||||||
|
| Reset(_)
|
||||||
|
| RuntimeErrorFunction(_) => {
|
||||||
// EmptyArray is always stack-allocated
|
// EmptyArray is always stack-allocated
|
||||||
// function pointers are persistent
|
// function pointers are persistent
|
||||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
|
@ -749,7 +763,7 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct LocalContext<'a> {
|
pub struct LocalContext<'a> {
|
||||||
join_points: MutMap<JoinPointId, (&'a [Param<'a>], &'a Stmt<'a>)>,
|
join_points: MutMap<JoinPointId, (&'a [Param<'a>], &'a Stmt<'a>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -569,6 +569,14 @@ pub enum Expr<'a> {
|
||||||
},
|
},
|
||||||
EmptyArray,
|
EmptyArray,
|
||||||
|
|
||||||
|
Reuse {
|
||||||
|
symbol: Symbol,
|
||||||
|
tag_name: TagName,
|
||||||
|
tag_id: u8,
|
||||||
|
arguments: &'a [Symbol],
|
||||||
|
},
|
||||||
|
Reset(Symbol),
|
||||||
|
|
||||||
RuntimeErrorFunction(&'a str),
|
RuntimeErrorFunction(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,6 +674,28 @@ impl<'a> Expr<'a> {
|
||||||
.append(alloc.space())
|
.append(alloc.space())
|
||||||
.append(alloc.intersperse(it, " "))
|
.append(alloc.intersperse(it, " "))
|
||||||
}
|
}
|
||||||
|
Reuse {
|
||||||
|
symbol,
|
||||||
|
tag_name,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let doc_tag = match tag_name {
|
||||||
|
TagName::Global(s) => alloc.text(s.as_str()),
|
||||||
|
TagName::Private(s) => alloc.text(format!("{}", s)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
|
||||||
|
|
||||||
|
alloc
|
||||||
|
.text("Reuse ")
|
||||||
|
.append(symbol_to_doc(alloc, *symbol))
|
||||||
|
.append(doc_tag)
|
||||||
|
.append(alloc.space())
|
||||||
|
.append(alloc.intersperse(it, " "))
|
||||||
|
}
|
||||||
|
Reset(symbol) => alloc.text("Reuse ").append(symbol_to_doc(alloc, *symbol)),
|
||||||
|
|
||||||
Struct(args) => {
|
Struct(args) => {
|
||||||
let it = args.iter().map(|s| symbol_to_doc(alloc, *s));
|
let it = args.iter().map(|s| symbol_to_doc(alloc, *s));
|
||||||
|
|
||||||
|
@ -2728,6 +2758,9 @@ fn substitute_in_expr<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reuse { .. } | Reset(_) => unreachable!("reset/reuse have not been introduced yet"),
|
||||||
|
|
||||||
Struct(args) => {
|
Struct(args) => {
|
||||||
let mut did_change = false;
|
let mut did_change = false;
|
||||||
let new_args = Vec::from_iter_in(
|
let new_args = Vec::from_iter_in(
|
||||||
|
|
|
@ -15,6 +15,8 @@ pub mod borrow;
|
||||||
pub mod inc_dec;
|
pub mod inc_dec;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub mod live_vars;
|
||||||
|
pub mod reset_reuse;
|
||||||
pub mod tail_recursion;
|
pub mod tail_recursion;
|
||||||
|
|
||||||
// Temporary, while we can build up test cases and optimize the exhaustiveness checking.
|
// Temporary, while we can build up test cases and optimize the exhaustiveness checking.
|
||||||
|
|
|
@ -1,220 +1,188 @@
|
||||||
use crate::expr::Env;
|
use crate::inc_dec::LocalContext;
|
||||||
use crate::expr::Expr;
|
use crate::ir::{Expr, Literal, Stmt};
|
||||||
|
use crate::layout::{Builtin, Layout};
|
||||||
|
use crate::live_vars;
|
||||||
|
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
pub fn function_r<'a>(env: &mut Env<'a, '_>, body: &'a Expr<'a>) -> Expr<'a> {
|
struct Env<'a, 'b> {
|
||||||
use Expr::*;
|
env: crate::ir::Env<'a, 'b>,
|
||||||
|
ctx: LocalContext<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
match body {
|
fn may_reuse<'a>(x: Layout<'a>, y: Layout<'a>) -> bool {
|
||||||
Switch {
|
// a heuristic; we really only want the same "type" to be reused.
|
||||||
cond_symbol,
|
// we could also compare actual stack size.
|
||||||
branches,
|
x == y
|
||||||
cond,
|
}
|
||||||
cond_layout,
|
|
||||||
default_branch,
|
|
||||||
ret_layout,
|
|
||||||
} => {
|
|
||||||
let stack_size = cond_layout.stack_size(env.pointer_size);
|
|
||||||
let mut new_branches = Vec::with_capacity_in(branches.len(), env.arena);
|
|
||||||
|
|
||||||
for (tag, stores, branch) in branches.iter() {
|
fn try_function_s<'a>(
|
||||||
let new_branch = function_d(env, *cond_symbol, stack_size as _, branch);
|
env: &mut Env<'a, '_>,
|
||||||
|
x: Symbol,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
stmt: &'a Stmt<'a>,
|
||||||
|
) -> &'a Stmt<'a> {
|
||||||
|
let arena = env.env.arena;
|
||||||
|
|
||||||
new_branches.push((*tag, *stores, new_branch));
|
let w = env.env.unique_symbol();
|
||||||
}
|
|
||||||
|
|
||||||
let new_default_branch = (
|
match function_s(env, w, stmt) {
|
||||||
default_branch.0,
|
None => stmt,
|
||||||
&*env.arena.alloc(function_d(
|
Some(new) => arena.alloc(Stmt::Let(w, Expr::Reset(x), layout, new)),
|
||||||
env,
|
}
|
||||||
*cond_symbol,
|
}
|
||||||
stack_size as _,
|
|
||||||
default_branch.1,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
fn function_s<'a>(env: &mut Env<'a, '_>, w: Symbol, stmt: &'a Stmt<'a>) -> Option<&'a Stmt<'a>> {
|
||||||
|
use Stmt::*;
|
||||||
|
|
||||||
|
let arena = env.env.arena;
|
||||||
|
|
||||||
|
match stmt {
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn function_d_main<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
x: Symbol,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
stmt: &'a Stmt<'a>,
|
||||||
|
) -> (Option<&'a Stmt<'a>>, bool) {
|
||||||
|
/*
|
||||||
|
let c = layout;
|
||||||
|
|
||||||
|
use Stmt::*;
|
||||||
|
|
||||||
|
let arena = env.env.arena;
|
||||||
|
|
||||||
|
match stmt {
|
||||||
Switch {
|
Switch {
|
||||||
cond_symbol: *cond_symbol,
|
cond_symbol,
|
||||||
branches: new_branches.into_bump_slice(),
|
cond_layout,
|
||||||
default_branch: new_default_branch,
|
branches,
|
||||||
ret_layout: ret_layout.clone(),
|
default_branch,
|
||||||
cond: *cond,
|
ret_layout,
|
||||||
cond_layout: cond_layout.clone(),
|
} => {
|
||||||
|
// TODO only conditionally re-build the expression
|
||||||
|
let live = live_vars::collect_stmt(stmt, env.ctx)
|
||||||
|
|
||||||
|
let branches = Vec::from_iter_in(
|
||||||
|
branches.iter().map(|(label, branch)| {
|
||||||
|
let branch = function_r(env, branch).unwrap_or(branch);
|
||||||
|
let branch = function_d(env, *cond_symbol, cond_layout.clone(), branch)
|
||||||
|
.unwrap_or(branch);
|
||||||
|
|
||||||
|
(*label, branch.clone())
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice();
|
||||||
|
|
||||||
|
let default_branch = function_r(env, default_branch).unwrap_or(default_branch);
|
||||||
|
let default_branch = function_d(env, *cond_symbol, cond_layout.clone(), default_branch)
|
||||||
|
.unwrap_or(default_branch);
|
||||||
|
|
||||||
|
let switch = Switch {
|
||||||
|
cond_symbol: *cond_symbol,
|
||||||
|
branches,
|
||||||
|
default_branch,
|
||||||
|
cond_layout: cond_layout.clone(),
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(arena.alloc(switch))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Cond {
|
|
||||||
cond_symbol,
|
|
||||||
cond_layout,
|
|
||||||
branching_symbol,
|
|
||||||
branching_layout,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
ret_layout,
|
|
||||||
} => {
|
|
||||||
let stack_size = cond_layout.stack_size(env.pointer_size);
|
|
||||||
|
|
||||||
let new_pass = (
|
Join { .. } => todo!(),
|
||||||
pass.0,
|
|
||||||
&*env
|
|
||||||
.arena
|
|
||||||
.alloc(function_d(env, *cond_symbol, stack_size as _, pass.1)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_fail = (
|
Ret(_) | Jump(_, _) | RuntimeError(_) => None,
|
||||||
fail.0,
|
|
||||||
&*env
|
Inc(x, b) => match function_r(env, b) {
|
||||||
.arena
|
None => None,
|
||||||
.alloc(function_d(env, *cond_symbol, stack_size as _, fail.1)),
|
Some(new_b) => Some(arena.alloc(Inc(*x, new_b))),
|
||||||
);
|
},
|
||||||
|
|
||||||
|
Dec(x, b) => match function_r(env, b) {
|
||||||
|
None => None,
|
||||||
|
Some(new_b) => Some(arena.alloc(Dec(*x, new_b))),
|
||||||
|
},
|
||||||
|
|
||||||
|
Let(x, v, l, b) => match function_r(env, b) {
|
||||||
|
None => None,
|
||||||
|
Some(new_b) => Some(arena.alloc(Let(*x, v.clone(), l.clone(), new_b))),
|
||||||
|
},
|
||||||
|
|
||||||
Cond {
|
Cond {
|
||||||
cond_symbol: *cond_symbol,
|
cond_symbol,
|
||||||
cond_layout: cond_layout.clone(),
|
cond_layout,
|
||||||
branching_symbol: *branching_symbol,
|
branching_symbol,
|
||||||
branching_layout: branching_layout.clone(),
|
branching_layout,
|
||||||
ret_layout: ret_layout.clone(),
|
pass,
|
||||||
pass: new_pass,
|
fail,
|
||||||
fail: new_fail,
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
// TODO only conditionally re-build the expression
|
||||||
|
|
||||||
|
let pass = function_r(env, pass).unwrap_or(pass);
|
||||||
|
let pass = function_d(env, *cond_symbol, cond_layout.clone(), pass).unwrap_or(pass);
|
||||||
|
|
||||||
|
let fail = function_r(env, fail).unwrap_or(fail);
|
||||||
|
let fail = function_d(env, *cond_symbol, cond_layout.clone(), fail).unwrap_or(fail);
|
||||||
|
|
||||||
|
let stmt = Cond {
|
||||||
|
cond_symbol: *cond_symbol,
|
||||||
|
cond_layout: cond_layout.clone(),
|
||||||
|
branching_symbol: *branching_symbol,
|
||||||
|
branching_layout: branching_layout.clone(),
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(arena.alloc(stmt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
Store(stores, body) => {
|
panic!();
|
||||||
let new_body = function_r(env, body);
|
|
||||||
|
|
||||||
Store(stores, env.arena.alloc(new_body))
|
|
||||||
}
|
|
||||||
|
|
||||||
DecAfter(symbol, body) => {
|
|
||||||
let new_body = function_r(env, body);
|
|
||||||
|
|
||||||
DecAfter(*symbol, env.arena.alloc(new_body))
|
|
||||||
}
|
|
||||||
|
|
||||||
CallByName { .. }
|
|
||||||
| CallByPointer(_, _, _)
|
|
||||||
| RunLowLevel(_, _)
|
|
||||||
| Tag { .. }
|
|
||||||
| Struct(_)
|
|
||||||
| Array { .. }
|
|
||||||
| AccessAtIndex { .. } => {
|
|
||||||
// TODO
|
|
||||||
// how often are `when` expressions in one of the above?
|
|
||||||
body.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
Int(_)
|
|
||||||
| Float(_)
|
|
||||||
| Str(_)
|
|
||||||
| Bool(_)
|
|
||||||
| Byte(_)
|
|
||||||
| Load(_)
|
|
||||||
| EmptyArray
|
|
||||||
| Inc(_, _)
|
|
||||||
| FunctionPointer(_, _)
|
|
||||||
| RuntimeError(_)
|
|
||||||
| RuntimeErrorFunction(_) => body.clone(),
|
|
||||||
|
|
||||||
Reset(_, _) | Reuse(_, _) => unreachable!("reset/reuse should not have been inserted yet!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_d<'a>(
|
fn function_d<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
z: Symbol,
|
x: Symbol,
|
||||||
stack_size: usize,
|
layout: Layout<'a>,
|
||||||
body: &'a Expr<'a>,
|
stmt: &'a Stmt<'a>,
|
||||||
) -> Expr<'a> {
|
) -> Option<&'a Stmt<'a>> {
|
||||||
let symbols = symbols_in_expr(body);
|
let c = layout;
|
||||||
if symbols.contains(&z) {
|
todo!();
|
||||||
return body.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(reused) = function_s(env, z, stack_size, body) {
|
|
||||||
Expr::Reset(z, env.arena.alloc(reused))
|
|
||||||
} else {
|
|
||||||
body.clone()
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
match body {
|
|
||||||
Expr::Tag { .. } => Some(env.arena.alloc(Expr::Reuse(w, body))),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_s<'a>(
|
fn function_r<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> Option<&'a Stmt<'a>> {
|
||||||
env: &mut Env<'a, '_>,
|
use Stmt::*;
|
||||||
w: Symbol,
|
|
||||||
stack_size: usize,
|
|
||||||
body: &'a Expr<'a>,
|
|
||||||
) -> Result<&'a Expr<'a>, &'a Expr<'a>> {
|
|
||||||
use Expr::*;
|
|
||||||
|
|
||||||
match body {
|
let arena = env.env.arena;
|
||||||
Tag { tag_layout, .. } => {
|
|
||||||
if tag_layout.stack_size(env.pointer_size) as usize <= stack_size {
|
|
||||||
Ok(env.arena.alloc(Expr::Reuse(w, body)))
|
|
||||||
} else {
|
|
||||||
Err(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Array { .. } | Struct(_) => {
|
match stmt {
|
||||||
// TODO
|
Join { .. } => todo!(),
|
||||||
Err(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
Ret(_) | Jump(_, _) | RuntimeError(_) => None,
|
||||||
cond_symbol,
|
|
||||||
branches,
|
|
||||||
cond,
|
|
||||||
cond_layout,
|
|
||||||
default_branch,
|
|
||||||
ret_layout,
|
|
||||||
} => {
|
|
||||||
// we can re-use `w` in each branch
|
|
||||||
let mut has_been_reused = false;
|
|
||||||
let mut new_branches = Vec::with_capacity_in(branches.len(), env.arena);
|
|
||||||
for (tag, stores, branch) in branches.iter() {
|
|
||||||
match function_s(env, *cond_symbol, stack_size as _, branch) {
|
|
||||||
Ok(new_branch) => {
|
|
||||||
has_been_reused = true;
|
|
||||||
new_branches.push((*tag, *stores, new_branch.clone()));
|
|
||||||
}
|
|
||||||
Err(new_branch) => {
|
|
||||||
new_branches.push((*tag, *stores, new_branch.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_default_branch = (
|
Inc(x, b) => match function_r(env, b) {
|
||||||
default_branch.0,
|
None => None,
|
||||||
match function_s(env, *cond_symbol, stack_size, default_branch.1) {
|
Some(new_b) => Some(arena.alloc(Inc(*x, new_b))),
|
||||||
Ok(new) => {
|
},
|
||||||
has_been_reused = true;
|
|
||||||
new
|
|
||||||
}
|
|
||||||
Err(new) => new,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let result = env.arena.alloc(Switch {
|
|
||||||
cond_symbol: *cond_symbol,
|
|
||||||
branches: new_branches.into_bump_slice(),
|
|
||||||
default_branch: new_default_branch,
|
|
||||||
ret_layout: ret_layout.clone(),
|
|
||||||
cond: *cond,
|
|
||||||
cond_layout: cond_layout.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if has_been_reused {
|
Dec(x, b) => match function_r(env, b) {
|
||||||
Ok(result)
|
None => None,
|
||||||
} else {
|
Some(new_b) => Some(arena.alloc(Dec(*x, new_b))),
|
||||||
Err(result)
|
},
|
||||||
}
|
|
||||||
}
|
Let(x, v, l, b) => match function_r(env, b) {
|
||||||
|
None => None,
|
||||||
|
Some(new_b) => Some(arena.alloc(Let(*x, v.clone(), l.clone(), new_b))),
|
||||||
|
},
|
||||||
|
|
||||||
Cond {
|
Cond {
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
|
@ -225,424 +193,60 @@ fn function_s<'a>(
|
||||||
fail,
|
fail,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
} => {
|
} => {
|
||||||
let mut has_been_reused = false;
|
// TODO only conditionally re-build the expression
|
||||||
let new_pass = (
|
|
||||||
pass.0,
|
|
||||||
match function_s(env, *cond_symbol, stack_size, pass.1) {
|
|
||||||
Ok(new) => {
|
|
||||||
has_been_reused = true;
|
|
||||||
new
|
|
||||||
}
|
|
||||||
Err(new) => new,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_fail = (
|
let pass = function_r(env, pass).unwrap_or(pass);
|
||||||
fail.0,
|
let pass = function_d(env, *cond_symbol, cond_layout.clone(), pass).unwrap_or(pass);
|
||||||
match function_s(env, *cond_symbol, stack_size, fail.1) {
|
|
||||||
Ok(new) => {
|
|
||||||
has_been_reused = true;
|
|
||||||
new
|
|
||||||
}
|
|
||||||
Err(new) => new,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = env.arena.alloc(Cond {
|
let fail = function_r(env, fail).unwrap_or(fail);
|
||||||
|
let fail = function_d(env, *cond_symbol, cond_layout.clone(), fail).unwrap_or(fail);
|
||||||
|
|
||||||
|
let stmt = Cond {
|
||||||
cond_symbol: *cond_symbol,
|
cond_symbol: *cond_symbol,
|
||||||
cond_layout: cond_layout.clone(),
|
cond_layout: cond_layout.clone(),
|
||||||
branching_symbol: *branching_symbol,
|
branching_symbol: *branching_symbol,
|
||||||
branching_layout: branching_layout.clone(),
|
branching_layout: branching_layout.clone(),
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
ret_layout: ret_layout.clone(),
|
ret_layout: ret_layout.clone(),
|
||||||
pass: new_pass,
|
};
|
||||||
fail: new_fail,
|
|
||||||
});
|
|
||||||
|
|
||||||
if has_been_reused {
|
Some(arena.alloc(stmt))
|
||||||
Ok(result)
|
|
||||||
} else {
|
|
||||||
Err(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Switch {
|
||||||
|
cond_symbol,
|
||||||
|
cond_layout,
|
||||||
|
branches,
|
||||||
|
default_branch,
|
||||||
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
// TODO only conditionally re-build the expression
|
||||||
|
|
||||||
Store(stores, expr) => {
|
let branches = Vec::from_iter_in(
|
||||||
let new_expr = function_s(env, w, stack_size, expr)?;
|
branches.iter().map(|(label, branch)| {
|
||||||
|
let branch = function_r(env, branch).unwrap_or(branch);
|
||||||
|
let branch = function_d(env, *cond_symbol, cond_layout.clone(), branch)
|
||||||
|
.unwrap_or(branch);
|
||||||
|
|
||||||
Ok(env.arena.alloc(Store(*stores, new_expr)))
|
(*label, branch.clone())
|
||||||
}
|
}),
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice();
|
||||||
|
|
||||||
DecAfter(symbol, expr) => {
|
let default_branch = function_r(env, default_branch).unwrap_or(default_branch);
|
||||||
let new_expr = function_s(env, w, stack_size, expr)?;
|
let default_branch = function_d(env, *cond_symbol, cond_layout.clone(), default_branch)
|
||||||
|
.unwrap_or(default_branch);
|
||||||
|
|
||||||
Ok(env.arena.alloc(DecAfter(*symbol, new_expr)))
|
let switch = Switch {
|
||||||
}
|
cond_symbol: *cond_symbol,
|
||||||
|
|
||||||
CallByName { .. } | CallByPointer(_, _, _) | RunLowLevel(_, _) | AccessAtIndex { .. } => {
|
|
||||||
// TODO
|
|
||||||
// how often are `Tag` expressions in one of the above?
|
|
||||||
Err(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
Int(_)
|
|
||||||
| Float(_)
|
|
||||||
| Str(_)
|
|
||||||
| Bool(_)
|
|
||||||
| Byte(_)
|
|
||||||
| Load(_)
|
|
||||||
| EmptyArray
|
|
||||||
| Inc(_, _)
|
|
||||||
| FunctionPointer(_, _)
|
|
||||||
| RuntimeError(_)
|
|
||||||
| RuntimeErrorFunction(_) => Err(body),
|
|
||||||
|
|
||||||
Reset(_, _) | Reuse(_, _) => {
|
|
||||||
unreachable!("reset/reuse should not have been introduced yet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn free_variables<'a>(initial: &Expr<'a>) -> MutSet<Symbol> {
|
|
||||||
use Expr::*;
|
|
||||||
let mut seen = MutSet::default();
|
|
||||||
let mut bound = MutSet::default();
|
|
||||||
let mut stack = vec![initial];
|
|
||||||
|
|
||||||
// in other words, variables that are referenced, but not stored
|
|
||||||
|
|
||||||
while let Some(expr) = stack.pop() {
|
|
||||||
match expr {
|
|
||||||
FunctionPointer(symbol, _) | Load(symbol) => {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
}
|
|
||||||
Reset(symbol, expr) | Reuse(symbol, expr) => {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
Cond {
|
|
||||||
cond_symbol,
|
|
||||||
branching_symbol,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
seen.insert(*cond_symbol);
|
|
||||||
seen.insert(*branching_symbol);
|
|
||||||
|
|
||||||
for (symbol, _, expr) in pass.0.iter() {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (symbol, _, expr) in fail.0.iter() {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
cond,
|
|
||||||
cond_symbol,
|
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
..
|
cond_layout: cond_layout.clone(),
|
||||||
} => {
|
ret_layout: ret_layout.clone(),
|
||||||
stack.push(cond);
|
};
|
||||||
seen.insert(*cond_symbol);
|
|
||||||
|
|
||||||
for (_, stores, expr) in branches.iter() {
|
Some(arena.alloc(switch))
|
||||||
stack.push(expr);
|
|
||||||
|
|
||||||
for (symbol, _, expr) in stores.iter() {
|
|
||||||
bound.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(default_branch.1);
|
|
||||||
for (symbol, _, expr) in default_branch.0.iter() {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Store(stores, body) => {
|
|
||||||
for (symbol, _, expr) in stores.iter() {
|
|
||||||
bound.insert(*symbol);
|
|
||||||
stack.push(&expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
DecAfter(symbol, body) | Inc(symbol, body) => {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
stack.push(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
CallByName { name, args, .. } => {
|
|
||||||
seen.insert(*name);
|
|
||||||
for (expr, _) in args.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CallByPointer(function, args, _) => {
|
|
||||||
stack.push(function);
|
|
||||||
stack.extend(args.iter());
|
|
||||||
}
|
|
||||||
|
|
||||||
RunLowLevel(_, args) => {
|
|
||||||
for (expr, _) in args.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag { arguments, .. } => {
|
|
||||||
for (symbol, _) in arguments.iter() {
|
|
||||||
seen.insert(*symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Struct(arguments) => {
|
|
||||||
for (expr, _) in arguments.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Array { elems, .. } => {
|
|
||||||
for expr in elems.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AccessAtIndex { expr, .. } => {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Int(_)
|
|
||||||
| Float(_)
|
|
||||||
| Str(_)
|
|
||||||
| Bool(_)
|
|
||||||
| Byte(_)
|
|
||||||
| EmptyArray
|
|
||||||
| RuntimeError(_)
|
|
||||||
| RuntimeErrorFunction(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for symbol in bound.iter() {
|
|
||||||
seen.remove(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
seen
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbols_in_expr<'a>(initial: &Expr<'a>) -> MutSet<Symbol> {
|
|
||||||
use Expr::*;
|
|
||||||
let mut result = MutSet::default();
|
|
||||||
let mut stack = vec![initial];
|
|
||||||
|
|
||||||
while let Some(expr) = stack.pop() {
|
|
||||||
match expr {
|
|
||||||
FunctionPointer(symbol, _) | Load(symbol) => {
|
|
||||||
result.insert(*symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reset(symbol, expr) | Reuse(symbol, expr) => {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
Cond {
|
|
||||||
cond_symbol,
|
|
||||||
branching_symbol,
|
|
||||||
pass,
|
|
||||||
fail,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
result.insert(*cond_symbol);
|
|
||||||
result.insert(*branching_symbol);
|
|
||||||
|
|
||||||
for (symbol, _, expr) in pass.0.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (symbol, _, expr) in fail.0.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
cond,
|
|
||||||
cond_symbol,
|
|
||||||
branches,
|
|
||||||
default_branch,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
stack.push(cond);
|
|
||||||
result.insert(*cond_symbol);
|
|
||||||
|
|
||||||
for (_, stores, expr) in branches.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
|
|
||||||
for (symbol, _, expr) in stores.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(default_branch.1);
|
|
||||||
for (symbol, _, expr) in default_branch.0.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Store(stores, body) => {
|
|
||||||
for (symbol, _, expr) in stores.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(&expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
DecAfter(symbol, body) | Inc(symbol, body) => {
|
|
||||||
result.insert(*symbol);
|
|
||||||
stack.push(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
CallByName { name, args, .. } => {
|
|
||||||
result.insert(*name);
|
|
||||||
for (expr, _) in args.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CallByPointer(function, args, _) => {
|
|
||||||
stack.push(function);
|
|
||||||
stack.extend(args.iter());
|
|
||||||
}
|
|
||||||
|
|
||||||
RunLowLevel(_, args) => {
|
|
||||||
for (expr, _) in args.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag { arguments, .. } => {
|
|
||||||
for (symbol, _) in arguments.iter() {
|
|
||||||
result.insert(*symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Struct(arguments) => {
|
|
||||||
for (expr, _) in arguments.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Array { elems, .. } => {
|
|
||||||
for expr in elems.iter() {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AccessAtIndex { expr, .. } => {
|
|
||||||
stack.push(expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Int(_)
|
|
||||||
| Float(_)
|
|
||||||
| Str(_)
|
|
||||||
| Bool(_)
|
|
||||||
| Byte(_)
|
|
||||||
| EmptyArray
|
|
||||||
| RuntimeError(_)
|
|
||||||
| RuntimeErrorFunction(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn function_c<'a>(env: &mut Env<'a, '_>, body: Expr<'a>) -> Expr<'a> {
|
|
||||||
let fv = free_variables(&body);
|
|
||||||
|
|
||||||
function_c_help(env, body, fv)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn function_c_help<'a>(env: &mut Env<'a, '_>, body: Expr<'a>, fv: MutSet<Symbol>) -> Expr<'a> {
|
|
||||||
use Expr::*;
|
|
||||||
|
|
||||||
match body {
|
|
||||||
Tag { arguments, .. } => {
|
|
||||||
let symbols = arguments
|
|
||||||
.iter()
|
|
||||||
.map(|(x, _)| x)
|
|
||||||
.copied()
|
|
||||||
.collect::<std::vec::Vec<_>>();
|
|
||||||
|
|
||||||
function_c_app(env, &symbols, &fv, body)
|
|
||||||
}
|
|
||||||
_ => body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_c_app<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
arguments: &[Symbol],
|
|
||||||
orig_fv: &MutSet<Symbol>,
|
|
||||||
mut application: Expr<'a>,
|
|
||||||
) -> Expr<'a> {
|
|
||||||
// in the future, this will need to be a check
|
|
||||||
let is_owned = true;
|
|
||||||
|
|
||||||
for (i, y) in arguments.iter().rev().enumerate() {
|
|
||||||
if is_owned {
|
|
||||||
let mut fv = orig_fv.clone();
|
|
||||||
fv.extend(arguments[i..].iter().copied());
|
|
||||||
|
|
||||||
application = insert_increment(env, *y, fv, application)
|
|
||||||
} else {
|
|
||||||
unimplemented!("owned references are not implemented yet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
application
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_increment<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
symbol: Symbol,
|
|
||||||
live_variables: MutSet<Symbol>,
|
|
||||||
body: Expr<'a>,
|
|
||||||
) -> Expr<'a> {
|
|
||||||
// in the future, this will need to be a check
|
|
||||||
let is_owned = true;
|
|
||||||
|
|
||||||
if is_owned && !live_variables.contains(&symbol) {
|
|
||||||
body
|
|
||||||
} else {
|
|
||||||
Expr::Inc(symbol, env.arena.alloc(body))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_decrement<'a>(env: &mut Env<'a, '_>, symbols: &[Symbol], mut body: Expr<'a>) -> Expr<'a> {
|
|
||||||
// in the future, this will need to be a check
|
|
||||||
let is_owned = true;
|
|
||||||
let fv = free_variables(&body);
|
|
||||||
|
|
||||||
for symbol in symbols.iter() {
|
|
||||||
let is_dead = !fv.contains(&symbol);
|
|
||||||
|
|
||||||
if is_owned && is_dead {
|
|
||||||
body = Expr::DecAfter(*symbol, env.arena.alloc(body));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue