mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
perform copy propagation
This commit is contained in:
parent
078c6df677
commit
f8143e3e53
4 changed files with 581 additions and 121 deletions
|
@ -256,7 +256,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Literal(literal) => build_exp_literal(env, literal),
|
Literal(literal) => build_exp_literal(env, literal),
|
||||||
Alias(symbol) => load_symbol(env, scope, symbol),
|
|
||||||
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, *op, symbols),
|
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, *op, symbols),
|
||||||
|
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
|
|
|
@ -93,7 +93,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
FunctionPointer(symbol, _)
|
FunctionPointer(symbol, _)
|
||||||
| Alias(symbol)
|
|
||||||
| AccessAtIndex {
|
| AccessAtIndex {
|
||||||
structure: symbol, ..
|
structure: symbol, ..
|
||||||
} => {
|
} => {
|
||||||
|
@ -239,6 +238,11 @@ impl<'a> Context<'a> {
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this symbol is never a reference, don't emit
|
||||||
|
if !info.reference {
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
self.arena.alloc(Stmt::Inc(symbol, stmt))
|
self.arena.alloc(Stmt::Inc(symbol, stmt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +254,11 @@ impl<'a> Context<'a> {
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this symbol is never a reference, don't emit
|
||||||
|
if !info.reference {
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
self.arena.alloc(Stmt::Dec(symbol, stmt))
|
self.arena.alloc(Stmt::Dec(symbol, stmt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,13 +433,8 @@ impl<'a> Context<'a> {
|
||||||
),
|
),
|
||||||
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);
|
||||||
// NOTE deviation from Lean. I think lean assumes all structure elements live on
|
|
||||||
// the heap. Therefore any access to a Tag/Struct element must increment its
|
|
||||||
// refcount. But in roc, structure elements can be unboxed.
|
|
||||||
let info_x = self.get_var_info(x);
|
let info_x = self.get_var_info(x);
|
||||||
// let info_z = self.get_var_info(z);
|
|
||||||
let b = if info_x.consume {
|
let b = if info_x.consume {
|
||||||
println!("inc on {}", z);
|
|
||||||
self.add_inc(z, b)
|
self.add_inc(z, b)
|
||||||
} else {
|
} else {
|
||||||
b
|
b
|
||||||
|
@ -489,7 +493,6 @@ impl<'a> Context<'a> {
|
||||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(_) => unreachable!("well, it should be unreachable!"),
|
|
||||||
|
|
||||||
EmptyArray | FunctionPointer(_, _) | Literal(_) | RuntimeErrorFunction(_) => {
|
EmptyArray | FunctionPointer(_, _) | Literal(_) | RuntimeErrorFunction(_) => {
|
||||||
// EmptyArray is always stack-allocated
|
// EmptyArray is always stack-allocated
|
||||||
|
@ -734,7 +737,8 @@ impl<'a> Context<'a> {
|
||||||
(switch, case_live_vars)
|
(switch, case_live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => todo!(),
|
RuntimeError(_) | Inc(_,_) | Dec(_,_) => (stmt, MutSet::default()),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,10 +404,6 @@ pub enum CallType {
|
||||||
pub enum Expr<'a> {
|
pub enum Expr<'a> {
|
||||||
Literal(Literal<'a>),
|
Literal(Literal<'a>),
|
||||||
|
|
||||||
/// A symbol will alias this symbol
|
|
||||||
/// in the long term we should get rid of this using copy propagation
|
|
||||||
Alias(Symbol),
|
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
FunctionPointer(Symbol, Layout<'a>),
|
FunctionPointer(Symbol, Layout<'a>),
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
|
@ -490,7 +486,6 @@ impl<'a> Expr<'a> {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Literal(lit) => lit.to_doc(alloc),
|
Literal(lit) => lit.to_doc(alloc),
|
||||||
Alias(symbol) => alloc.text("alias ").append(symbol_to_doc(alloc, *symbol)),
|
|
||||||
|
|
||||||
FunctionPointer(symbol, _) => symbol_to_doc(alloc, *symbol),
|
FunctionPointer(symbol, _) => symbol_to_doc(alloc, *symbol),
|
||||||
|
|
||||||
|
@ -1049,7 +1044,12 @@ pub fn with_hole<'a>(
|
||||||
LetNonRec(def, cont, _, _) => {
|
LetNonRec(def, cont, _, _) => {
|
||||||
// WRONG! this is introduces new control flow, and should call `from_can` again
|
// WRONG! this is introduces new control flow, and should call `from_can` again
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
||||||
let stmt = with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
let mut stmt = with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
||||||
|
|
||||||
|
// this is an alias of a variable
|
||||||
|
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
||||||
|
substitute_in_exprs(env.arena, &mut stmt, symbol, original);
|
||||||
|
}
|
||||||
|
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -1060,7 +1060,51 @@ pub fn with_hole<'a>(
|
||||||
env.arena.alloc(stmt),
|
env.arena.alloc(stmt),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
// this may be a destructure pattern
|
||||||
|
let mono_pattern = from_can_pattern(env, layout_cache, &def.loc_pattern.value);
|
||||||
|
|
||||||
|
if let Pattern::Identifier(symbol) = mono_pattern {
|
||||||
|
let hole = env
|
||||||
|
.arena
|
||||||
|
.alloc(from_can(env, cont.value, procs, layout_cache));
|
||||||
|
with_hole(env, def.loc_expr.value, procs, layout_cache, symbol, hole)
|
||||||
|
} else {
|
||||||
|
let context = crate::exhaustive::Context::BadDestruct;
|
||||||
|
match crate::exhaustive::check(
|
||||||
|
def.loc_pattern.region,
|
||||||
|
&[(
|
||||||
|
Located::at(def.loc_pattern.region, mono_pattern.clone()),
|
||||||
|
crate::exhaustive::Guard::NoGuard,
|
||||||
|
)],
|
||||||
|
context,
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(errors) => {
|
||||||
|
for error in errors {
|
||||||
|
env.problems.push(MonoProblem::PatternProblem(error))
|
||||||
|
}
|
||||||
|
} // TODO make all variables bound in the pattern evaluate to a runtime error
|
||||||
|
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the continuation
|
||||||
|
let mut stmt = from_can(env, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
|
let outer_symbol = env.unique_symbol();
|
||||||
|
stmt =
|
||||||
|
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// convert the def body, store in outer_symbol
|
||||||
|
with_hole(
|
||||||
|
env,
|
||||||
|
def.loc_expr.value,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
outer_symbol,
|
||||||
|
env.arena.alloc(stmt),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Var(symbol) => {
|
Var(symbol) => {
|
||||||
|
@ -1840,23 +1884,11 @@ pub fn from_can<'a>(
|
||||||
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
|
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout = layout_cache
|
|
||||||
.from_var(env.arena, def.expr_var, env.subs)
|
|
||||||
.expect("invalid layout");
|
|
||||||
|
|
||||||
// convert the continuation
|
// convert the continuation
|
||||||
let mut stmt = from_can(env, cont.value, procs, layout_cache);
|
let mut stmt = from_can(env, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
let outer_symbol = env.unique_symbol();
|
let outer_symbol = env.unique_symbol();
|
||||||
stmt = store_pattern(
|
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&mono_pattern,
|
|
||||||
outer_symbol,
|
|
||||||
layout,
|
|
||||||
stmt,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// convert the def body, store in outer_symbol
|
// convert the def body, store in outer_symbol
|
||||||
|
@ -2012,15 +2044,7 @@ fn from_can_when<'a>(
|
||||||
|
|
||||||
let guard_stmt = with_hole(env, loc_expr.value, procs, layout_cache, symbol, jump);
|
let guard_stmt = with_hole(env, loc_expr.value, procs, layout_cache, symbol, jump);
|
||||||
|
|
||||||
match store_pattern(
|
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, guard_stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&pattern,
|
|
||||||
cond_symbol,
|
|
||||||
cond_layout.clone(),
|
|
||||||
guard_stmt,
|
|
||||||
) {
|
|
||||||
Ok(new_guard_stmt) => (
|
Ok(new_guard_stmt) => (
|
||||||
pattern,
|
pattern,
|
||||||
Guard::Guard {
|
Guard::Guard {
|
||||||
|
@ -2037,15 +2061,7 @@ fn from_can_when<'a>(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match store_pattern(
|
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, branch_stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&pattern,
|
|
||||||
cond_symbol,
|
|
||||||
cond_layout.clone(),
|
|
||||||
branch_stmt,
|
|
||||||
) {
|
|
||||||
Ok(new_branch_stmt) => (pattern, Guard::NoGuard, new_branch_stmt),
|
Ok(new_branch_stmt) => (pattern, Guard::NoGuard, new_branch_stmt),
|
||||||
Err(msg) => (
|
Err(msg) => (
|
||||||
Pattern::Underscore,
|
Pattern::Underscore,
|
||||||
|
@ -2068,6 +2084,363 @@ fn from_can_when<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn substitute(substitutions: &MutMap<Symbol, Symbol>, s: Symbol) -> Option<Symbol> {
|
||||||
|
match substitutions.get(&s) {
|
||||||
|
Some(new) => {
|
||||||
|
debug_assert!(!substitutions.contains_key(new));
|
||||||
|
Some(*new)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, to: Symbol) {
|
||||||
|
let mut subs = MutMap::default();
|
||||||
|
subs.insert(from, to);
|
||||||
|
|
||||||
|
// TODO clean this up
|
||||||
|
let ref_stmt = arena.alloc(stmt.clone());
|
||||||
|
if let Some(new) = substitute_in_stmt_help(arena, ref_stmt, &subs) {
|
||||||
|
*stmt = new.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute_in_stmt_help<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
stmt: &'a Stmt<'a>,
|
||||||
|
subs: &MutMap<Symbol, Symbol>,
|
||||||
|
) -> Option<&'a Stmt<'a>> {
|
||||||
|
use Stmt::*;
|
||||||
|
|
||||||
|
match stmt {
|
||||||
|
Let(symbol, expr, layout, cont) => {
|
||||||
|
let opt_cont = substitute_in_stmt_help(arena, cont, subs);
|
||||||
|
let opt_expr = substitute_in_expr(arena, expr, subs);
|
||||||
|
|
||||||
|
if opt_expr.is_some() || opt_cont.is_some() {
|
||||||
|
let cont = opt_cont.unwrap_or(cont);
|
||||||
|
let expr = opt_expr.unwrap_or_else(|| expr.clone());
|
||||||
|
|
||||||
|
Some(arena.alloc(Let(*symbol, expr, layout.clone(), cont)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Join {
|
||||||
|
id,
|
||||||
|
parameters,
|
||||||
|
remainder,
|
||||||
|
continuation,
|
||||||
|
} => {
|
||||||
|
let opt_remainder = substitute_in_stmt_help(arena, remainder, subs);
|
||||||
|
let opt_continuation = substitute_in_stmt_help(arena, continuation, subs);
|
||||||
|
|
||||||
|
if opt_remainder.is_some() || opt_continuation.is_some() {
|
||||||
|
let remainder = opt_remainder.unwrap_or(remainder);
|
||||||
|
let continuation = opt_continuation.unwrap_or_else(|| *continuation);
|
||||||
|
|
||||||
|
Some(arena.alloc(Join {
|
||||||
|
id: *id,
|
||||||
|
parameters,
|
||||||
|
remainder,
|
||||||
|
continuation,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cond {
|
||||||
|
cond_symbol,
|
||||||
|
cond_layout,
|
||||||
|
branching_symbol,
|
||||||
|
branching_layout,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
let opt_pass = substitute_in_stmt_help(arena, pass, subs);
|
||||||
|
let opt_fail = substitute_in_stmt_help(arena, fail, subs);
|
||||||
|
|
||||||
|
if opt_pass.is_some() || opt_fail.is_some() {
|
||||||
|
let pass = opt_pass.unwrap_or(pass);
|
||||||
|
let fail = opt_fail.unwrap_or_else(|| *fail);
|
||||||
|
|
||||||
|
Some(arena.alloc(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(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Switch {
|
||||||
|
cond_symbol,
|
||||||
|
cond_layout,
|
||||||
|
branches,
|
||||||
|
default_branch,
|
||||||
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
let opt_default = substitute_in_stmt_help(arena, default_branch, subs);
|
||||||
|
|
||||||
|
let mut did_change = false;
|
||||||
|
|
||||||
|
let opt_branches = Vec::from_iter_in(
|
||||||
|
branches.iter().map(|(label, branch)| {
|
||||||
|
match substitute_in_stmt_help(arena, branch, subs) {
|
||||||
|
None => None,
|
||||||
|
Some(branch) => {
|
||||||
|
did_change = true;
|
||||||
|
Some((*label, branch.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if opt_default.is_some() || did_change {
|
||||||
|
let default_branch = opt_default.unwrap_or(default_branch);
|
||||||
|
|
||||||
|
let branches = if did_change {
|
||||||
|
let new = Vec::from_iter_in(
|
||||||
|
opt_branches.into_iter().zip(branches.iter()).map(
|
||||||
|
|(opt_branch, branch)| match opt_branch {
|
||||||
|
None => branch.clone(),
|
||||||
|
Some(new_branch) => new_branch,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
new.into_bump_slice()
|
||||||
|
} else {
|
||||||
|
branches
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(arena.alloc(Switch {
|
||||||
|
cond_symbol: *cond_symbol,
|
||||||
|
cond_layout: cond_layout.clone(),
|
||||||
|
default_branch,
|
||||||
|
branches,
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ret(s) => match substitute(subs, *s) {
|
||||||
|
Some(s) => Some(arena.alloc(Ret(s))),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
Inc(symbol, cont) => match substitute_in_stmt_help(arena, cont, subs) {
|
||||||
|
Some(cont) => Some(arena.alloc(Inc(*symbol, cont))),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
Dec(symbol, cont) => match substitute_in_stmt_help(arena, cont, subs) {
|
||||||
|
Some(cont) => Some(arena.alloc(Dec(*symbol, cont))),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
|
||||||
|
Jump(id, args) => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(arena.alloc(Jump(*id, args)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeError(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute_in_expr<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
expr: &'a Expr<'a>,
|
||||||
|
subs: &MutMap<Symbol, Symbol>,
|
||||||
|
) -> Option<Expr<'a>> {
|
||||||
|
use Expr::*;
|
||||||
|
|
||||||
|
match expr {
|
||||||
|
Literal(_) | FunctionPointer(_, _) | EmptyArray | RuntimeErrorFunction(_) => None,
|
||||||
|
|
||||||
|
FunctionCall {
|
||||||
|
call_type,
|
||||||
|
args,
|
||||||
|
arg_layouts,
|
||||||
|
layout,
|
||||||
|
} => {
|
||||||
|
let opt_call_type = match call_type {
|
||||||
|
CallType::ByName(s) => substitute(subs, *s).map(CallType::ByName),
|
||||||
|
CallType::ByPointer(s) => substitute(subs, *s).map(CallType::ByPointer),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change || opt_call_type.is_some() {
|
||||||
|
let call_type = opt_call_type.unwrap_or(*call_type);
|
||||||
|
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(FunctionCall {
|
||||||
|
call_type,
|
||||||
|
args,
|
||||||
|
arg_layouts: *arg_layouts,
|
||||||
|
layout: layout.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RunLowLevel(op, args) => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(RunLowLevel(*op, args))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag {
|
||||||
|
tag_layout,
|
||||||
|
tag_name,
|
||||||
|
tag_id,
|
||||||
|
union_size,
|
||||||
|
arguments: args,
|
||||||
|
} => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let arguments = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(Tag {
|
||||||
|
tag_layout: tag_layout.clone(),
|
||||||
|
tag_name: tag_name.clone(),
|
||||||
|
tag_id: *tag_id,
|
||||||
|
union_size: *union_size,
|
||||||
|
arguments,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Struct(args) => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(Struct(args))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array {
|
||||||
|
elems: args,
|
||||||
|
elem_layout,
|
||||||
|
} => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
args.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(Array {
|
||||||
|
elem_layout: elem_layout.clone(),
|
||||||
|
elems: args,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessAtIndex {
|
||||||
|
index,
|
||||||
|
structure,
|
||||||
|
field_layouts,
|
||||||
|
is_unwrapped,
|
||||||
|
} => match substitute(subs, *structure) {
|
||||||
|
Some(structure) => Some(AccessAtIndex {
|
||||||
|
index: *index,
|
||||||
|
field_layouts: *field_layouts,
|
||||||
|
is_unwrapped: *is_unwrapped,
|
||||||
|
structure,
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn store_pattern<'a>(
|
fn store_pattern<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -2075,15 +2448,13 @@ fn store_pattern<'a>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
can_pat: &Pattern<'a>,
|
can_pat: &Pattern<'a>,
|
||||||
outer_symbol: Symbol,
|
outer_symbol: Symbol,
|
||||||
layout: Layout<'a>,
|
|
||||||
mut stmt: Stmt<'a>,
|
mut stmt: Stmt<'a>,
|
||||||
) -> Result<Stmt<'a>, &'a str> {
|
) -> Result<Stmt<'a>, &'a str> {
|
||||||
use Pattern::*;
|
use Pattern::*;
|
||||||
|
|
||||||
match can_pat {
|
match can_pat {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
let expr = Expr::Alias(outer_symbol);
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
||||||
stmt = Stmt::Let(*symbol, expr, layout, env.arena.alloc(stmt));
|
|
||||||
}
|
}
|
||||||
Underscore => {
|
Underscore => {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -2134,15 +2505,7 @@ fn store_pattern<'a>(
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
// first recurse, continuing to unpack symbol
|
// first recurse, continuing to unpack symbol
|
||||||
stmt = store_pattern(
|
stmt = store_pattern(env, procs, layout_cache, argument, symbol, stmt)?;
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
argument,
|
|
||||||
symbol,
|
|
||||||
arg_layout.clone(),
|
|
||||||
stmt,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// then store the symbol
|
// then store the symbol
|
||||||
stmt = Stmt::Let(symbol, load, arg_layout.clone(), env.arena.alloc(stmt));
|
stmt = Stmt::Let(symbol, load, arg_layout.clone(), env.arena.alloc(stmt));
|
||||||
|
@ -2244,15 +2607,7 @@ fn store_record_destruct<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
stmt = store_pattern(
|
stmt = store_pattern(env, procs, layout_cache, guard_pattern, symbol, stmt)?;
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
guard_pattern,
|
|
||||||
symbol,
|
|
||||||
destruct.layout.clone(),
|
|
||||||
stmt,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
stmt = Stmt::Let(symbol, load, destruct.layout.clone(), env.arena.alloc(stmt));
|
stmt = Stmt::Let(symbol, load, destruct.layout.clone(), env.arena.alloc(stmt));
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,14 +125,14 @@ mod test_mono {
|
||||||
"#,
|
"#,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
let Test.2 = true;
|
let Test.3 = true;
|
||||||
if Test.2 then
|
if Test.3 then
|
||||||
let Test.0 = 1i64;
|
let Test.1 = 1i64;
|
||||||
jump Test.1 Test.0;
|
jump Test.2 Test.1;
|
||||||
else
|
else
|
||||||
let Test.0 = 2i64;
|
let Test.1 = 2i64;
|
||||||
jump Test.1 Test.0;
|
jump Test.2 Test.1;
|
||||||
joinpoint Test.1 Test.0:
|
joinpoint Test.2 Test.0:
|
||||||
ret Test.0;
|
ret Test.0;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -310,19 +310,19 @@ mod test_mono {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure Num.32 (#Attr.2, #Attr.3):
|
procedure Num.32 (#Attr.2, #Attr.3):
|
||||||
let Test.20 = 0i64;
|
let Test.21 = 0i64;
|
||||||
let Test.17 = lowlevel NotEq #Attr.3 Test.20;
|
let Test.18 = lowlevel NotEq #Attr.3 Test.21;
|
||||||
if Test.17 then
|
if Test.18 then
|
||||||
let Test.18 = 1i64;
|
let Test.19 = 1i64;
|
||||||
let Test.19 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
let Test.20 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||||
let Test.13 = Ok Test.18 Test.19;
|
let Test.14 = Ok Test.19 Test.20;
|
||||||
jump Test.14 Test.13;
|
jump Test.15 Test.14;
|
||||||
else
|
else
|
||||||
let Test.15 = 0i64;
|
let Test.16 = 0i64;
|
||||||
let Test.16 = Struct {};
|
let Test.17 = Struct {};
|
||||||
let Test.13 = Err Test.15 Test.16;
|
let Test.14 = Err Test.16 Test.17;
|
||||||
jump Test.14 Test.13;
|
jump Test.15 Test.14;
|
||||||
joinpoint Test.14 Test.13:
|
joinpoint Test.15 Test.13:
|
||||||
ret Test.13;
|
ret Test.13;
|
||||||
|
|
||||||
let Test.11 = 1000i64;
|
let Test.11 = 1000i64;
|
||||||
|
@ -440,14 +440,14 @@ mod test_mono {
|
||||||
"#,
|
"#,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
let Test.3 = true;
|
let Test.4 = true;
|
||||||
if Test.3 then
|
if Test.4 then
|
||||||
let Test.0 = 1i64;
|
let Test.2 = 1i64;
|
||||||
jump Test.2 Test.0;
|
jump Test.3 Test.2;
|
||||||
else
|
else
|
||||||
let Test.0 = 2i64;
|
let Test.2 = 2i64;
|
||||||
jump Test.2 Test.0;
|
jump Test.3 Test.2;
|
||||||
joinpoint Test.2 Test.0:
|
joinpoint Test.3 Test.0:
|
||||||
ret Test.0;
|
ret Test.0;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -603,21 +603,52 @@ mod test_mono {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_push() {
|
fn list_append_closure() {
|
||||||
compiles_to_ir(
|
compiles_to_ir(
|
||||||
r#"
|
r#"
|
||||||
List.push [1] 2
|
myFunction = \l -> List.append l 42
|
||||||
|
|
||||||
|
myFunction [ 1, 2 ]
|
||||||
|
"#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
procedure Test.0 (Test.2):
|
||||||
|
let Test.6 = 42i64;
|
||||||
|
let Test.5 = CallByName List.5 Test.2 Test.6;
|
||||||
|
ret Test.5;
|
||||||
|
|
||||||
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
|
let Test.7 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||||
|
ret Test.7;
|
||||||
|
|
||||||
|
let Test.8 = 1i64;
|
||||||
|
let Test.9 = 2i64;
|
||||||
|
let Test.4 = Array [Test.8, Test.9];
|
||||||
|
let Test.3 = CallByName Test.0 Test.4;
|
||||||
|
dec Test.4;
|
||||||
|
ret Test.3;
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_append() {
|
||||||
|
compiles_to_ir(
|
||||||
|
r#"
|
||||||
|
List.append [1] 2
|
||||||
"#,
|
"#,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
let Test.3 = lowlevel ListPush #Attr.2 #Attr.3;
|
let Test.3 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
|
|
||||||
let Test.4 = 1i64;
|
let Test.4 = 1i64;
|
||||||
let Test.1 = Array [Test.4];
|
let Test.1 = Array [Test.4];
|
||||||
let Test.2 = 2i64;
|
let Test.2 = 2i64;
|
||||||
let Test.0 = CallByName List.5 Test.1 Test.2;
|
let Test.0 = CallByName List.5 Test.1 Test.2;
|
||||||
|
dec Test.1;
|
||||||
ret Test.0;
|
ret Test.0;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -635,15 +666,26 @@ mod test_mono {
|
||||||
"#,
|
"#,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure Num.14 (#Attr.2, #Attr.3):
|
||||||
let Test.3 = lowlevel ListPush #Attr.2 #Attr.3;
|
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.3;
|
ret Test.5;
|
||||||
|
|
||||||
let Test.4 = 1i64;
|
procedure List.7 (#Attr.2):
|
||||||
let Test.1 = Array [Test.4];
|
let Test.6 = lowlevel ListLen #Attr.2;
|
||||||
let Test.2 = 2i64;
|
ret Test.6;
|
||||||
let Test.0 = CallByName List.5 Test.1 Test.2;
|
|
||||||
ret Test.0;
|
let Test.10 = 1f64;
|
||||||
|
let Test.1 = Array [Test.10];
|
||||||
|
let Test.7 = 1i64;
|
||||||
|
let Test.8 = 2i64;
|
||||||
|
let Test.9 = 3i64;
|
||||||
|
let Test.0 = Array [Test.7, Test.8, Test.9];
|
||||||
|
let Test.3 = CallByName List.7 Test.0;
|
||||||
|
dec Test.0;
|
||||||
|
let Test.4 = CallByName List.7 Test.1;
|
||||||
|
dec Test.1;
|
||||||
|
let Test.2 = CallByName Num.14 Test.3 Test.4;
|
||||||
|
ret Test.2;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -806,8 +848,30 @@ mod test_mono {
|
||||||
let Test.5 = 3.14f64;
|
let Test.5 = 3.14f64;
|
||||||
let Test.3 = Struct {Test.4, Test.5};
|
let Test.3 = Struct {Test.4, Test.5};
|
||||||
let Test.0 = Index 0 Test.3;
|
let Test.0 = Index 0 Test.3;
|
||||||
|
ret Test.0;
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_with_record_pattern_list() {
|
||||||
|
compiles_to_ir(
|
||||||
|
r#"
|
||||||
|
{ x } = { x: [ 1, 3, 4 ], y: 3.14 }
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
let Test.6 = 1i64;
|
||||||
|
let Test.7 = 3i64;
|
||||||
|
let Test.8 = 4i64;
|
||||||
|
let Test.4 = Array [Test.6, Test.7, Test.8];
|
||||||
|
let Test.5 = 3.14f64;
|
||||||
|
let Test.3 = Struct {Test.4, Test.5};
|
||||||
|
let Test.0 = Index 0 Test.3;
|
||||||
inc Test.0;
|
inc Test.0;
|
||||||
dec Test.3;
|
|
||||||
ret Test.0;
|
ret Test.0;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -832,9 +896,8 @@ mod test_mono {
|
||||||
|
|
||||||
let Test.2 = 10i64;
|
let Test.2 = 10i64;
|
||||||
let Test.11 = true;
|
let Test.11 = true;
|
||||||
let Test.0 = alias Test.2;
|
|
||||||
let Test.7 = 5i64;
|
let Test.7 = 5i64;
|
||||||
let Test.6 = CallByName Bool.5 Test.0 Test.7;
|
let Test.6 = CallByName Bool.5 Test.2 Test.7;
|
||||||
jump Test.5 Test.6;
|
jump Test.5 Test.6;
|
||||||
joinpoint Test.5 Test.12:
|
joinpoint Test.5 Test.12:
|
||||||
let Test.10 = lowlevel And Test.12 Test.11;
|
let Test.10 = lowlevel And Test.12 Test.11;
|
||||||
|
@ -852,26 +915,65 @@ mod test_mono {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn beans_example_1() {
|
fn alias_variable() {
|
||||||
compiles_to_ir(
|
compiles_to_ir(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
y = 10
|
x = 5
|
||||||
|
y = x
|
||||||
|
|
||||||
z = Num.add y y
|
3
|
||||||
|
|
||||||
z
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure Num.14 (#Attr.2, #Attr.3):
|
let Test.0 = 5i64;
|
||||||
let Test.3 = lowlevel NumAdd #Attr.2 #Attr.3;
|
ret Test.0;
|
||||||
ret Test.3;
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let Test.0 = 10i64;
|
compiles_to_ir(
|
||||||
inc Test.0;
|
indoc!(
|
||||||
let Test.1 = CallByName Num.14 Test.0 Test.0;
|
r#"
|
||||||
|
x = 5
|
||||||
|
y = x
|
||||||
|
|
||||||
|
y
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
let Test.0 = 5i64;
|
||||||
|
ret Test.0;
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_store_variable() {
|
||||||
|
compiles_to_ir(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 0 is
|
||||||
|
1 -> 12
|
||||||
|
a -> a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
let Test.2 = 0i64;
|
||||||
|
let Test.7 = true;
|
||||||
|
let Test.8 = 1i64;
|
||||||
|
let Test.9 = lowlevel Eq Test.8 Test.2;
|
||||||
|
let Test.6 = lowlevel And Test.9 Test.7;
|
||||||
|
if Test.6 then
|
||||||
|
let Test.4 = 12i64;
|
||||||
|
jump Test.3 Test.4;
|
||||||
|
else
|
||||||
|
jump Test.3 Test.2;
|
||||||
|
joinpoint Test.3 Test.1:
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue