mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
invoke in mono
This commit is contained in:
parent
ca24f1cd38
commit
0ef70f55ea
4 changed files with 421 additions and 167 deletions
|
@ -156,6 +156,10 @@ impl<'a> ParamMap<'a> {
|
||||||
Let(_, _, _, cont) => {
|
Let(_, _, _, cont) => {
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
|
Invoke { pass, fail, .. } => {
|
||||||
|
stack.push(pass);
|
||||||
|
stack.push(fail);
|
||||||
|
}
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
|
@ -295,6 +299,62 @@ impl<'a> BorrowInfState<'a> {
|
||||||
///
|
///
|
||||||
/// and determines whether z and which of the symbols used in e
|
/// and determines whether z and which of the symbols used in e
|
||||||
/// must be taken as owned paramters
|
/// must be taken as owned paramters
|
||||||
|
fn collect_call(&mut self, z: Symbol, e: &crate::ir::Call<'a>) {
|
||||||
|
use crate::ir::CallType::*;
|
||||||
|
|
||||||
|
let crate::ir::Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
} = e;
|
||||||
|
|
||||||
|
match call_type {
|
||||||
|
ByName {
|
||||||
|
name, arg_layouts, ..
|
||||||
|
}
|
||||||
|
| ByPointer {
|
||||||
|
name, arg_layouts, ..
|
||||||
|
} => {
|
||||||
|
// get the borrow signature of the applied function
|
||||||
|
let ps = match self.param_map.get_symbol(*name) {
|
||||||
|
Some(slice) => slice,
|
||||||
|
None => Vec::from_iter_in(
|
||||||
|
arg_layouts.iter().cloned().map(|layout| Param {
|
||||||
|
symbol: Symbol::UNDERSCORE,
|
||||||
|
borrow: false,
|
||||||
|
layout,
|
||||||
|
}),
|
||||||
|
self.arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// the return value will be owned
|
||||||
|
self.own_var(z);
|
||||||
|
|
||||||
|
// if the function exects an owned argument (ps), the argument must be owned (args)
|
||||||
|
self.own_args_using_params(arguments, ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
LowLevel { op } => {
|
||||||
|
// very unsure what demand RunLowLevel should place upon its arguments
|
||||||
|
self.own_var(z);
|
||||||
|
|
||||||
|
let ps = lowlevel_borrow_signature(self.arena, *op);
|
||||||
|
|
||||||
|
self.own_args_using_bools(arguments, ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
Foreign { .. } => {
|
||||||
|
// very unsure what demand ForeignCall should place upon its arguments
|
||||||
|
self.own_var(z);
|
||||||
|
|
||||||
|
let ps = foreign_borrow_signature(self.arena, arguments.len());
|
||||||
|
|
||||||
|
self.own_args_using_bools(arguments, ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_expr(&mut self, z: Symbol, e: &Expr<'a>) {
|
fn collect_expr(&mut self, z: Symbol, e: &Expr<'a>) {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
|
|
||||||
|
@ -334,59 +394,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(crate::ir::Call {
|
Call(call) => self.collect_call(z, call),
|
||||||
call_type,
|
|
||||||
arguments,
|
|
||||||
}) => {
|
|
||||||
use crate::ir::CallType::*;
|
|
||||||
|
|
||||||
match call_type {
|
|
||||||
ByName {
|
|
||||||
name, arg_layouts, ..
|
|
||||||
}
|
|
||||||
| ByPointer {
|
|
||||||
name, arg_layouts, ..
|
|
||||||
} => {
|
|
||||||
// get the borrow signature of the applied function
|
|
||||||
let ps = match self.param_map.get_symbol(*name) {
|
|
||||||
Some(slice) => slice,
|
|
||||||
None => Vec::from_iter_in(
|
|
||||||
arg_layouts.iter().cloned().map(|layout| Param {
|
|
||||||
symbol: Symbol::UNDERSCORE,
|
|
||||||
borrow: false,
|
|
||||||
layout,
|
|
||||||
}),
|
|
||||||
self.arena,
|
|
||||||
)
|
|
||||||
.into_bump_slice(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// the return value will be owned
|
|
||||||
self.own_var(z);
|
|
||||||
|
|
||||||
// if the function exects an owned argument (ps), the argument must be owned (args)
|
|
||||||
self.own_args_using_params(arguments, ps);
|
|
||||||
}
|
|
||||||
|
|
||||||
LowLevel { op } => {
|
|
||||||
// very unsure what demand RunLowLevel should place upon its arguments
|
|
||||||
self.own_var(z);
|
|
||||||
|
|
||||||
let ps = lowlevel_borrow_signature(self.arena, *op);
|
|
||||||
|
|
||||||
self.own_args_using_bools(arguments, ps);
|
|
||||||
}
|
|
||||||
|
|
||||||
Foreign { .. } => {
|
|
||||||
// very unsure what demand ForeignCall should place upon its arguments
|
|
||||||
self.own_var(z);
|
|
||||||
|
|
||||||
let ps = foreign_borrow_signature(self.arena, arguments.len());
|
|
||||||
|
|
||||||
self.own_args_using_bools(arguments, ps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
|
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -462,11 +470,29 @@ impl<'a> BorrowInfState<'a> {
|
||||||
self.collect_stmt(b);
|
self.collect_stmt(b);
|
||||||
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, layout.clone()), b);
|
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, layout.clone()), b);
|
||||||
}
|
}
|
||||||
|
|
||||||
Let(x, v, _, b) => {
|
Let(x, v, _, b) => {
|
||||||
self.collect_stmt(b);
|
self.collect_stmt(b);
|
||||||
self.collect_expr(*x, v);
|
self.collect_expr(*x, v);
|
||||||
self.preserve_tail_call(*x, v, b);
|
self.preserve_tail_call(*x, v, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
layout,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
} => {
|
||||||
|
self.collect_stmt(pass);
|
||||||
|
self.collect_stmt(fail);
|
||||||
|
|
||||||
|
self.collect_call(*symbol, call);
|
||||||
|
|
||||||
|
// TODO how to preserve the tail call of an invoke?
|
||||||
|
// self.preserve_tail_call(*x, v, b);
|
||||||
|
}
|
||||||
|
|
||||||
Jump(j, ys) => {
|
Jump(j, ys) => {
|
||||||
let ps = self.param_map.get_join_point(*j);
|
let ps = self.param_map.get_join_point(*j);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,21 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
|
||||||
bound_variables.insert(*symbol);
|
bound_variables.insert(*symbol);
|
||||||
stack.push(cont);
|
stack.push(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
occuring_variables_call(call, &mut result);
|
||||||
|
result.insert(*symbol);
|
||||||
|
bound_variables.insert(*symbol);
|
||||||
|
stack.push(pass);
|
||||||
|
stack.push(fail);
|
||||||
|
}
|
||||||
|
|
||||||
Ret(symbol) => {
|
Ret(symbol) => {
|
||||||
result.insert(*symbol);
|
result.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +92,12 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
|
||||||
(result, bound_variables)
|
(result, bound_variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn occuring_variables_call(call: &crate::ir::Call<'_>, result: &mut MutSet<Symbol>) {
|
||||||
|
// NOTE though the function name does occur, it is a static constant in the program
|
||||||
|
// for liveness, it should not be included here.
|
||||||
|
result.extend(call.arguments.iter().copied());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
|
|
||||||
|
@ -88,11 +109,7 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
result.insert(*symbol);
|
result.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(crate::ir::Call { arguments, .. }) => {
|
Call(call) => occuring_variables_call(call, result),
|
||||||
// NOTE thouth the function name does occur, it is a static constant in the program
|
|
||||||
// for liveness, it should not be included here.
|
|
||||||
result.extend(arguments.iter().copied());
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag { arguments, .. }
|
Tag { arguments, .. }
|
||||||
| Struct(arguments)
|
| Struct(arguments)
|
||||||
|
@ -204,6 +221,11 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn consume_call(m: &VarMap, e: &crate::ir::Call<'_>) -> bool {
|
||||||
|
// variables bound by a call (or invoke) must always be consumed
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
||||||
let mut vars = MutMap::default();
|
let mut vars = MutMap::default();
|
||||||
|
@ -406,6 +428,75 @@ impl<'a> Context<'a> {
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_call(
|
||||||
|
&self,
|
||||||
|
z: Symbol,
|
||||||
|
call_type: crate::ir::CallType<'a>,
|
||||||
|
arguments: &'a [Symbol],
|
||||||
|
l: Layout<'a>,
|
||||||
|
b: &'a Stmt<'a>,
|
||||||
|
b_live_vars: &LiveVarSet,
|
||||||
|
) -> &'a Stmt<'a> {
|
||||||
|
use crate::ir::CallType::*;
|
||||||
|
|
||||||
|
match &call_type {
|
||||||
|
LowLevel { op } => {
|
||||||
|
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||||
|
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||||
|
|
||||||
|
let v = Expr::Call(crate::ir::Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
});
|
||||||
|
|
||||||
|
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
Foreign { .. } => {
|
||||||
|
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
|
||||||
|
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||||
|
|
||||||
|
let v = Expr::Call(crate::ir::Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
});
|
||||||
|
|
||||||
|
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
ByName {
|
||||||
|
name, arg_layouts, ..
|
||||||
|
}
|
||||||
|
| ByPointer {
|
||||||
|
name, arg_layouts, ..
|
||||||
|
} => {
|
||||||
|
// get the borrow signature
|
||||||
|
let ps = match self.param_map.get_symbol(*name) {
|
||||||
|
Some(slice) => slice,
|
||||||
|
None => Vec::from_iter_in(
|
||||||
|
arg_layouts.iter().cloned().map(|layout| Param {
|
||||||
|
symbol: Symbol::UNDERSCORE,
|
||||||
|
borrow: false,
|
||||||
|
layout,
|
||||||
|
}),
|
||||||
|
self.arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = Expr::Call(crate::ir::Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
});
|
||||||
|
|
||||||
|
let b = self.add_dec_after_application(arguments, ps, b, b_live_vars);
|
||||||
|
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
|
||||||
|
|
||||||
|
self.add_inc_before(arguments, ps, b, b_live_vars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn visit_variable_declaration(
|
fn visit_variable_declaration(
|
||||||
&self,
|
&self,
|
||||||
|
@ -442,54 +533,9 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(crate::ir::Call {
|
Call(crate::ir::Call {
|
||||||
ref call_type,
|
call_type,
|
||||||
arguments,
|
arguments,
|
||||||
}) => {
|
}) => self.visit_call(z, call_type, arguments, l, b, b_live_vars),
|
||||||
use crate::ir::CallType::*;
|
|
||||||
|
|
||||||
match &call_type {
|
|
||||||
LowLevel { op } => {
|
|
||||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
|
||||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
|
||||||
|
|
||||||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
Foreign { .. } => {
|
|
||||||
let ps =
|
|
||||||
crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
|
|
||||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
|
||||||
|
|
||||||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
|
||||||
}
|
|
||||||
|
|
||||||
ByName {
|
|
||||||
name, arg_layouts, ..
|
|
||||||
}
|
|
||||||
| ByPointer {
|
|
||||||
name, arg_layouts, ..
|
|
||||||
} => {
|
|
||||||
// get the borrow signature
|
|
||||||
let ps = match self.param_map.get_symbol(*name) {
|
|
||||||
Some(slice) => slice,
|
|
||||||
None => Vec::from_iter_in(
|
|
||||||
arg_layouts.iter().cloned().map(|layout| Param {
|
|
||||||
symbol: Symbol::UNDERSCORE,
|
|
||||||
borrow: false,
|
|
||||||
layout,
|
|
||||||
}),
|
|
||||||
self.arena,
|
|
||||||
)
|
|
||||||
.into_bump_slice(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = self.add_dec_after_application(arguments, ps, b, b_live_vars);
|
|
||||||
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
|
|
||||||
|
|
||||||
self.add_inc_before(arguments, ps, b, b_live_vars)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EmptyArray
|
EmptyArray
|
||||||
| FunctionPointer(_, _)
|
| FunctionPointer(_, _)
|
||||||
|
@ -505,12 +551,23 @@ impl<'a> Context<'a> {
|
||||||
(new_b, live_vars)
|
(new_b, live_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_var_info_invoke(
|
||||||
|
&self,
|
||||||
|
symbol: Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
call: &crate::ir::Call<'a>,
|
||||||
|
) -> Self {
|
||||||
|
// is this value a constant?
|
||||||
|
// TODO do function pointers also fall into this category?
|
||||||
|
let persistent = call.arguments.is_empty();
|
||||||
|
|
||||||
|
// must this value be consumed?
|
||||||
|
let consume = consume_call(&self.vars, call);
|
||||||
|
|
||||||
|
self.update_var_info_help(symbol, layout, persistent, consume)
|
||||||
|
}
|
||||||
|
|
||||||
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
|
||||||
let mut ctx = self.clone();
|
|
||||||
|
|
||||||
// can this type be reference-counted at runtime?
|
|
||||||
let reference = layout.contains_refcounted();
|
|
||||||
|
|
||||||
// is this value a constant?
|
// is this value a constant?
|
||||||
// TODO do function pointers also fall into this category?
|
// TODO do function pointers also fall into this category?
|
||||||
let persistent = match expr {
|
let persistent = match expr {
|
||||||
|
@ -519,7 +576,20 @@ impl<'a> Context<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// must this value be consumed?
|
// must this value be consumed?
|
||||||
let consume = consume_expr(&ctx.vars, expr);
|
let consume = consume_expr(&self.vars, expr);
|
||||||
|
|
||||||
|
self.update_var_info_help(symbol, layout, persistent, consume)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_var_info_help(
|
||||||
|
&self,
|
||||||
|
symbol: Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
persistent: bool,
|
||||||
|
consume: bool,
|
||||||
|
) -> Self {
|
||||||
|
// can this type be reference-counted at runtime?
|
||||||
|
let reference = layout.contains_refcounted();
|
||||||
|
|
||||||
let info = VarInfo {
|
let info = VarInfo {
|
||||||
reference,
|
reference,
|
||||||
|
@ -527,6 +597,8 @@ impl<'a> Context<'a> {
|
||||||
consume,
|
consume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut ctx = self.clone();
|
||||||
|
|
||||||
ctx.vars.insert(symbol, info);
|
ctx.vars.insert(symbol, info);
|
||||||
|
|
||||||
ctx
|
ctx
|
||||||
|
@ -634,6 +706,46 @@ impl<'a> Context<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
layout,
|
||||||
|
} => {
|
||||||
|
// TODO this combines parts of Let and Switch. Did this happen correctly?
|
||||||
|
let mut case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||||
|
|
||||||
|
case_live_vars.remove(symbol);
|
||||||
|
|
||||||
|
let fail = {
|
||||||
|
// TODO should we use ctor info like Lean?
|
||||||
|
let ctx = self.clone();
|
||||||
|
let (b, alt_live_vars) = ctx.visit_stmt(fail);
|
||||||
|
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||||
|
};
|
||||||
|
|
||||||
|
case_live_vars.insert(*symbol);
|
||||||
|
|
||||||
|
let pass = {
|
||||||
|
// TODO should we use ctor info like Lean?
|
||||||
|
let ctx = self.clone();
|
||||||
|
let ctx = ctx.update_var_info_invoke(*symbol, layout, call);
|
||||||
|
let (b, alt_live_vars) = ctx.visit_stmt(pass);
|
||||||
|
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||||
|
};
|
||||||
|
|
||||||
|
let invoke = Invoke {
|
||||||
|
symbol: *symbol,
|
||||||
|
call: call.clone(),
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
layout: layout.clone(),
|
||||||
|
};
|
||||||
|
let stmt = self.arena.alloc(invoke);
|
||||||
|
|
||||||
|
(stmt, case_live_vars)
|
||||||
|
}
|
||||||
Join {
|
Join {
|
||||||
id: j,
|
id: j,
|
||||||
parameters: _,
|
parameters: _,
|
||||||
|
@ -765,6 +877,25 @@ pub fn collect_stmt(
|
||||||
|
|
||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
vars = collect_stmt(pass, jp_live_vars, vars);
|
||||||
|
vars = collect_stmt(fail, jp_live_vars, vars);
|
||||||
|
|
||||||
|
vars.remove(symbol);
|
||||||
|
|
||||||
|
let mut result = MutSet::default();
|
||||||
|
occuring_variables_call(call, &mut result);
|
||||||
|
|
||||||
|
vars.extend(result);
|
||||||
|
|
||||||
|
vars
|
||||||
|
}
|
||||||
Ret(symbol) => {
|
Ret(symbol) => {
|
||||||
vars.insert(*symbol);
|
vars.insert(*symbol);
|
||||||
vars
|
vars
|
||||||
|
|
|
@ -741,6 +741,13 @@ pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Stmt<'a> {
|
pub enum Stmt<'a> {
|
||||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||||
|
Invoke {
|
||||||
|
symbol: Symbol,
|
||||||
|
call: Call<'a>,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
pass: &'a Stmt<'a>,
|
||||||
|
fail: &'a Stmt<'a>,
|
||||||
|
},
|
||||||
Switch {
|
Switch {
|
||||||
/// This *must* stand for an integer, because Switch potentially compiles to a jump table.
|
/// This *must* stand for an integer, because Switch potentially compiles to a jump table.
|
||||||
cond_symbol: Symbol,
|
cond_symbol: Symbol,
|
||||||
|
@ -1121,6 +1128,11 @@ impl<'a> Stmt<'a> {
|
||||||
.append(alloc.hardline())
|
.append(alloc.hardline())
|
||||||
.append(cont.to_doc(alloc)),
|
.append(cont.to_doc(alloc)),
|
||||||
|
|
||||||
|
Invoke { symbol, .. } => alloc
|
||||||
|
.text("invoke ")
|
||||||
|
.append(symbol_to_doc(alloc, *symbol))
|
||||||
|
.append(" = ?"),
|
||||||
|
|
||||||
Ret(symbol) => alloc
|
Ret(symbol) => alloc
|
||||||
.text("ret ")
|
.text("ret ")
|
||||||
.append(symbol_to_doc(alloc, *symbol))
|
.append(symbol_to_doc(alloc, *symbol))
|
||||||
|
@ -4365,6 +4377,33 @@ fn substitute_in_stmt_help<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
layout,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
} => {
|
||||||
|
let opt_call = substitute_in_call(arena, call, subs);
|
||||||
|
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);
|
||||||
|
let call = opt_call.unwrap_or_else(|| call.clone());
|
||||||
|
|
||||||
|
Some(arena.alloc(Invoke {
|
||||||
|
symbol: *symbol,
|
||||||
|
call,
|
||||||
|
layout: layout.clone(),
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -4483,6 +4522,69 @@ fn substitute_in_stmt_help<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn substitute_in_call<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
call: &'a Call<'a>,
|
||||||
|
subs: &MutMap<Symbol, Symbol>,
|
||||||
|
) -> Option<Call<'a>> {
|
||||||
|
let Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
} = call;
|
||||||
|
|
||||||
|
let opt_call_type = match call_type {
|
||||||
|
CallType::ByName {
|
||||||
|
name,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
full_layout,
|
||||||
|
} => substitute(subs, *name).map(|new| CallType::ByName {
|
||||||
|
name: new,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
full_layout: full_layout.clone(),
|
||||||
|
}),
|
||||||
|
CallType::ByPointer {
|
||||||
|
name,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
full_layout,
|
||||||
|
} => substitute(subs, *name).map(|new| CallType::ByPointer {
|
||||||
|
name: new,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
full_layout: full_layout.clone(),
|
||||||
|
}),
|
||||||
|
CallType::Foreign { .. } => None,
|
||||||
|
CallType::LowLevel { .. } => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
arguments.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_else(|| call_type.clone());
|
||||||
|
|
||||||
|
let arguments = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(self::Call {
|
||||||
|
call_type,
|
||||||
|
arguments,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn substitute_in_expr<'a>(
|
fn substitute_in_expr<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
expr: &'a Expr<'a>,
|
expr: &'a Expr<'a>,
|
||||||
|
@ -4493,62 +4595,7 @@ fn substitute_in_expr<'a>(
|
||||||
match expr {
|
match expr {
|
||||||
Literal(_) | FunctionPointer(_, _) | EmptyArray | RuntimeErrorFunction(_) => None,
|
Literal(_) | FunctionPointer(_, _) | EmptyArray | RuntimeErrorFunction(_) => None,
|
||||||
|
|
||||||
Call(self::Call {
|
Call(call) => substitute_in_call(arena, call, subs).map(|new| Expr::Call(new)),
|
||||||
call_type,
|
|
||||||
arguments,
|
|
||||||
}) => {
|
|
||||||
let opt_call_type = match call_type {
|
|
||||||
CallType::ByName {
|
|
||||||
name,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout,
|
|
||||||
full_layout,
|
|
||||||
} => substitute(subs, *name).map(|new| CallType::ByName {
|
|
||||||
name: new,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout: ret_layout.clone(),
|
|
||||||
full_layout: full_layout.clone(),
|
|
||||||
}),
|
|
||||||
CallType::ByPointer {
|
|
||||||
name,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout,
|
|
||||||
full_layout,
|
|
||||||
} => substitute(subs, *name).map(|new| CallType::ByPointer {
|
|
||||||
name: new,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout: ret_layout.clone(),
|
|
||||||
full_layout: full_layout.clone(),
|
|
||||||
}),
|
|
||||||
CallType::Foreign { .. } => None,
|
|
||||||
CallType::LowLevel { .. } => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut did_change = false;
|
|
||||||
let new_args = Vec::from_iter_in(
|
|
||||||
arguments.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_else(|| call_type.clone());
|
|
||||||
|
|
||||||
let arguments = new_args.into_bump_slice();
|
|
||||||
|
|
||||||
Some(Expr::Call(self::Call {
|
|
||||||
call_type,
|
|
||||||
arguments,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout,
|
tag_layout,
|
||||||
|
|
|
@ -90,6 +90,27 @@ fn insert_jumps<'a>(
|
||||||
Some(arena.alloc(jump))
|
Some(arena.alloc(jump))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call:
|
||||||
|
crate::ir::Call {
|
||||||
|
call_type: CallType::ByName { name: fsym, .. },
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
fail,
|
||||||
|
pass: Stmt::Ret(rsym),
|
||||||
|
..
|
||||||
|
} if needle == *fsym && symbol == rsym => {
|
||||||
|
debug_assert_eq!(fail, &&Stmt::Unreachable);
|
||||||
|
|
||||||
|
// replace the call and return with a jump
|
||||||
|
|
||||||
|
let jump = Stmt::Jump(goal_id, arguments);
|
||||||
|
|
||||||
|
Some(arena.alloc(jump))
|
||||||
|
}
|
||||||
|
|
||||||
Let(symbol, expr, layout, cont) => {
|
Let(symbol, expr, layout, cont) => {
|
||||||
let opt_cont = insert_jumps(arena, cont, goal_id, needle);
|
let opt_cont = insert_jumps(arena, cont, goal_id, needle);
|
||||||
|
|
||||||
|
@ -101,6 +122,35 @@ fn insert_jumps<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoke {
|
||||||
|
symbol,
|
||||||
|
call,
|
||||||
|
fail,
|
||||||
|
pass,
|
||||||
|
layout,
|
||||||
|
} => {
|
||||||
|
let opt_pass = insert_jumps(arena, pass, goal_id, needle);
|
||||||
|
let opt_fail = insert_jumps(arena, fail, goal_id, needle);
|
||||||
|
|
||||||
|
if opt_pass.is_some() || opt_fail.is_some() {
|
||||||
|
let pass = opt_pass.unwrap_or(pass);
|
||||||
|
let fail = opt_fail.unwrap_or(fail);
|
||||||
|
|
||||||
|
let stmt = Invoke {
|
||||||
|
symbol: *symbol,
|
||||||
|
call: call.clone(),
|
||||||
|
layout: layout.clone(),
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(arena.alloc(stmt))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue