call changes in mono

This commit is contained in:
Folkert 2021-01-01 16:11:31 +01:00
parent 6bc0cf33a5
commit 0893aa7369
5 changed files with 346 additions and 295 deletions

View file

@ -334,14 +334,21 @@ impl<'a> BorrowInfState<'a> {
} }
} }
FunctionCall { Call(crate::ir::Call {
call_type, call_type,
args, arguments,
arg_layouts, }) => {
.. use crate::ir::CallType::*;
match call_type {
ByName {
name, arg_layouts, ..
}
| ByPointer {
name, arg_layouts, ..
} => { } => {
// get the borrow signature of the applied function // get the borrow signature of the applied function
let ps = match self.param_map.get_symbol(call_type.get_inner()) { let ps = match self.param_map.get_symbol(*name) {
Some(slice) => slice, Some(slice) => slice,
None => Vec::from_iter_in( None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param { arg_layouts.iter().cloned().map(|layout| Param {
@ -358,19 +365,19 @@ impl<'a> BorrowInfState<'a> {
self.own_var(z); self.own_var(z);
// if the function exects an owned argument (ps), the argument must be owned (args) // if the function exects an owned argument (ps), the argument must be owned (args)
self.own_args_using_params(args, ps); self.own_args_using_params(arguments, ps);
} }
RunLowLevel(op, args) => { LowLevel { op } => {
// very unsure what demand RunLowLevel should place upon its arguments // very unsure what demand RunLowLevel should place upon its arguments
self.own_var(z); self.own_var(z);
let ps = lowlevel_borrow_signature(self.arena, *op); let ps = lowlevel_borrow_signature(self.arena, *op);
self.own_args_using_bools(args, ps); self.own_args_using_bools(arguments, ps);
} }
ForeignCall { arguments, .. } => { Foreign { .. } => {
// very unsure what demand ForeignCall should place upon its arguments // very unsure what demand ForeignCall should place upon its arguments
self.own_var(z); self.own_var(z);
@ -378,30 +385,41 @@ impl<'a> BorrowInfState<'a> {
self.own_args_using_bools(arguments, ps); self.own_args_using_bools(arguments, ps);
} }
}
}
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {} Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
} }
} }
fn preserve_tail_call(&mut self, x: Symbol, v: &Expr<'a>, b: &Stmt<'a>) { fn preserve_tail_call(&mut self, x: Symbol, v: &Expr<'a>, b: &Stmt<'a>) {
if let ( match (v, b) {
Expr::FunctionCall { (
call_type, Expr::Call(crate::ir::Call {
args: ys, call_type: crate::ir::CallType::ByName { name: g, .. },
arguments: ys,
.. ..
}, }),
Stmt::Ret(z), Stmt::Ret(z),
) = (v, b) )
{ | (
let g = call_type.get_inner(); Expr::Call(crate::ir::Call {
if self.current_proc == g && x == *z { call_type: crate::ir::CallType::ByPointer { name: g, .. },
arguments: ys,
..
}),
Stmt::Ret(z),
) => {
if self.current_proc == *g && x == *z {
// anonymous functions (for which the ps may not be known) // anonymous functions (for which the ps may not be known)
// can never be tail-recursive, so this is fine // can never be tail-recursive, so this is fine
if let Some(ps) = self.param_map.get_symbol(g) { if let Some(ps) = self.param_map.get_symbol(*g) {
self.own_params_using_args(ys, ps) self.own_params_using_args(ys, ps)
} }
} }
} }
_ => {}
}
} }
fn update_param_set(&mut self, ps: &[Param<'a>]) { fn update_param_set(&mut self, ps: &[Param<'a>]) {

View file

@ -1277,7 +1277,10 @@ fn compile_test<'a>(
ret_layout, ret_layout,
); );
let test = Expr::RunLowLevel(LowLevel::Eq, arena.alloc([lhs, rhs])); let test = Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
arguments: arena.alloc([lhs, rhs]),
});
// write to the test symbol // write to the test symbol
cond = Stmt::Let( cond = Stmt::Let(

View file

@ -88,10 +88,10 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
result.insert(*symbol); result.insert(*symbol);
} }
FunctionCall { args, .. } => { Call(crate::ir::Call { arguments, .. }) => {
// NOTE thouth the function name does occur, it is a static constant in the program // NOTE thouth the function name does occur, it is a static constant in the program
// for liveness, it should not be included here. // for liveness, it should not be included here.
result.extend(args.iter().copied()); result.extend(arguments.iter().copied());
} }
Tag { arguments, .. } Tag { arguments, .. }
@ -110,12 +110,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
Reset(x) => { Reset(x) => {
result.insert(*x); result.insert(*x);
} }
RunLowLevel(_, args) => {
result.extend(args.iter());
}
ForeignCall { arguments, .. } => {
result.extend(arguments.iter());
}
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {} EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
} }
@ -447,28 +441,36 @@ impl<'a> Context<'a> {
self.arena.alloc(Stmt::Let(z, v, l, b)) self.arena.alloc(Stmt::Let(z, v, l, b))
} }
RunLowLevel(op, args) => { Call(crate::ir::Call {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op); ref call_type,
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars); arguments,
}) => {
use crate::ir::CallType::*;
self.arena.alloc(Stmt::Let(z, v, l, b)) match &call_type {
} LowLevel { op } => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
ForeignCall { arguments, .. } => {
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 b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
self.arena.alloc(Stmt::Let(z, v, l, b)) &*self.arena.alloc(Stmt::Let(z, v, l, b))
} }
FunctionCall { Foreign { .. } => {
args: ys, let ps =
arg_layouts, crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
call_type, 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 // get the borrow signature
let ps = match self.param_map.get_symbol(call_type.get_inner()) { let ps = match self.param_map.get_symbol(*name) {
Some(slice) => slice, Some(slice) => slice,
None => Vec::from_iter_in( None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param { arg_layouts.iter().cloned().map(|layout| Param {
@ -481,10 +483,12 @@ impl<'a> Context<'a> {
.into_bump_slice(), .into_bump_slice(),
}; };
let b = self.add_dec_after_application(ys, ps, b, b_live_vars); let b = self.add_dec_after_application(arguments, ps, b, b_live_vars);
let b = self.arena.alloc(Stmt::Let(z, v, l, b)); let b = self.arena.alloc(Stmt::Let(z, v, l, b));
self.add_inc_before(ys, ps, b, b_live_vars) self.add_inc_before(arguments, ps, b, b_live_vars)
}
}
} }
EmptyArray EmptyArray
@ -510,7 +514,7 @@ impl<'a> Context<'a> {
// 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 {
Expr::FunctionCall { args, .. } => args.is_empty(), Expr::Call(crate::ir::Call { arguments, .. }) => arguments.is_empty(),
_ => false, _ => false,
}; };

View file

@ -785,20 +785,6 @@ pub enum Literal<'a> {
/// compile to bytes, e.g. [ Blue, Black, Red, Green, White ] /// compile to bytes, e.g. [ Blue, Black, Red, Green, White ]
Byte(u8), Byte(u8),
} }
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum CallType {
ByName(Symbol),
ByPointer(Symbol),
}
impl CallType {
pub fn get_inner(&self) -> Symbol {
match self {
CallType::ByName(s) => *s,
CallType::ByPointer(s) => *s,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Wrapped { pub enum Wrapped {
@ -838,25 +824,98 @@ impl Wrapped {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub struct Call<'a> {
pub call_type: CallType<'a>,
pub arguments: &'a [Symbol],
}
impl<'a> Call<'a> {
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
use CallType::*;
let arguments = self.arguments;
match self.call_type {
CallType::ByName { name, .. } => {
let it = std::iter::once(name)
.chain(arguments.iter().copied())
.map(|s| symbol_to_doc(alloc, s));
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
}
CallType::ByPointer { name, .. } => {
let it = std::iter::once(name)
.chain(arguments.iter().copied())
.map(|s| symbol_to_doc(alloc, s));
alloc
.text("CallByPointer ")
.append(alloc.intersperse(it, " "))
}
LowLevel { op: lowlevel } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
}
Foreign {
foreign_symbol: ref foreign_symbol,
..
} => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
.text(format!("foreign {:?} ", foreign_symbol.as_str()))
.append(alloc.intersperse(it, " "))
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum CallType<'a> {
ByName {
name: Symbol,
full_layout: Layout<'a>,
ret_layout: Layout<'a>,
arg_layouts: &'a [Layout<'a>],
},
ByPointer {
name: Symbol,
full_layout: Layout<'a>,
ret_layout: Layout<'a>,
arg_layouts: &'a [Layout<'a>],
},
Foreign {
foreign_symbol: ForeignSymbol,
ret_layout: Layout<'a>,
},
LowLevel {
op: LowLevel,
},
}
// x = f a b c; S
//
//
// invoke x = f a b c in S else Unreachable
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> { pub enum Expr<'a> {
Literal(Literal<'a>), Literal(Literal<'a>),
// Functions // Functions
FunctionPointer(Symbol, Layout<'a>), FunctionPointer(Symbol, Layout<'a>),
FunctionCall { Call(Call<'a>),
call_type: CallType,
full_layout: Layout<'a>,
ret_layout: Layout<'a>,
arg_layouts: &'a [Layout<'a>],
args: &'a [Symbol],
},
RunLowLevel(LowLevel, &'a [Symbol]),
ForeignCall {
foreign_symbol: ForeignSymbol,
arguments: &'a [Symbol],
ret_layout: Layout<'a>,
},
Tag { Tag {
tag_layout: Layout<'a>, tag_layout: Layout<'a>,
@ -957,44 +1016,8 @@ impl<'a> Expr<'a> {
.text("FunctionPointer ") .text("FunctionPointer ")
.append(symbol_to_doc(alloc, *symbol)), .append(symbol_to_doc(alloc, *symbol)),
FunctionCall { Call(call) => call.to_doc(alloc),
call_type, args, ..
} => match call_type {
CallType::ByName(name) => {
let it = std::iter::once(name)
.chain(args.iter())
.map(|s| symbol_to_doc(alloc, *s));
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
}
CallType::ByPointer(name) => {
let it = std::iter::once(name)
.chain(args.iter())
.map(|s| symbol_to_doc(alloc, *s));
alloc
.text("CallByPointer ")
.append(alloc.intersperse(it, " "))
}
},
RunLowLevel(lowlevel, args) => {
let it = args.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
}
ForeignCall {
foreign_symbol,
arguments,
..
} => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
.text(format!("foreign {:?} ", foreign_symbol.as_str()))
.append(alloc.intersperse(it, " "))
}
Tag { Tag {
tag_name, tag_name,
arguments, arguments,
@ -3509,13 +3532,15 @@ pub fn with_hole<'a>(
// build the call // build the call
result = Stmt::Let( result = Stmt::Let(
assigned, assigned,
Expr::FunctionCall { Expr::Call(self::Call {
call_type: CallType::ByPointer(closure_function_symbol), call_type: CallType::ByPointer {
name: closure_function_symbol,
full_layout: function_ptr_layout.clone(), full_layout: function_ptr_layout.clone(),
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
args: arg_symbols,
arg_layouts, arg_layouts,
}, },
arguments: arg_symbols,
}),
ret_layout, ret_layout,
arena.alloc(hole), arena.alloc(hole),
); );
@ -3556,13 +3581,15 @@ pub fn with_hole<'a>(
} else { } else {
result = Stmt::Let( result = Stmt::Let(
assigned, assigned,
Expr::FunctionCall { Expr::Call(self::Call {
call_type: CallType::ByPointer(function_symbol), call_type: CallType::ByPointer {
name: function_symbol,
full_layout, full_layout,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
args: arg_symbols,
arg_layouts, arg_layouts,
}, },
arguments: arg_symbols,
}),
ret_layout, ret_layout,
arena.alloc(hole), arena.alloc(hole),
); );
@ -3573,13 +3600,15 @@ pub fn with_hole<'a>(
result = Stmt::Let( result = Stmt::Let(
assigned, assigned,
Expr::FunctionCall { Expr::Call(self::Call {
call_type: CallType::ByPointer(function_symbol), call_type: CallType::ByPointer {
name: function_symbol,
full_layout, full_layout,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
args: arg_symbols,
arg_layouts, arg_layouts,
}, },
arguments: arg_symbols,
}),
ret_layout, ret_layout,
arena.alloc(hole), arena.alloc(hole),
); );
@ -3620,11 +3649,13 @@ pub fn with_hole<'a>(
let result = Stmt::Let( let result = Stmt::Let(
assigned, assigned,
Expr::ForeignCall { Expr::Call(self::Call {
call_type: CallType::Foreign {
foreign_symbol, foreign_symbol,
arguments: arg_symbols,
ret_layout: layout.clone(), ret_layout: layout.clone(),
}, },
arguments: arg_symbols,
}),
layout, layout,
hole, hole,
); );
@ -3666,7 +3697,15 @@ pub fn with_hole<'a>(
} }
}; };
let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole); let result = Stmt::Let(
assigned,
Expr::Call(self::Call {
call_type: CallType::LowLevel { op },
arguments: arg_symbols,
}),
layout,
hole,
);
let iter = args let iter = args
.into_iter() .into_iter()
@ -4455,72 +4494,37 @@ fn substitute_in_expr<'a>(
match expr { match expr {
Literal(_) | FunctionPointer(_, _) | EmptyArray | RuntimeErrorFunction(_) => None, Literal(_) | FunctionPointer(_, _) | EmptyArray | RuntimeErrorFunction(_) => None,
FunctionCall { Call(self::Call {
call_type, call_type,
args, arguments,
}) => {
let opt_call_type = match call_type {
CallType::ByName {
name,
arg_layouts, arg_layouts,
ret_layout, ret_layout,
full_layout, full_layout,
} => { } => substitute(subs, *name).map(|new| CallType::ByName {
let opt_call_type = match call_type { name: new,
CallType::ByName(s) => substitute(subs, *s).map(CallType::ByName), arg_layouts,
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,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
full_layout: full_layout.clone(), full_layout: full_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, CallType::ByPointer {
); name,
arg_layouts,
if did_change {
let args = new_args.into_bump_slice();
Some(RunLowLevel(*op, args))
} else {
None
}
}
ForeignCall {
foreign_symbol,
arguments,
ret_layout, 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 mut did_change = false;
let new_args = Vec::from_iter_in( let new_args = Vec::from_iter_in(
arguments.iter().map(|s| match substitute(subs, *s) { arguments.iter().map(|s| match substitute(subs, *s) {
@ -4533,14 +4537,15 @@ fn substitute_in_expr<'a>(
arena, arena,
); );
if did_change { if did_change || opt_call_type.is_some() {
let args = new_args.into_bump_slice(); let call_type = opt_call_type.unwrap_or_else(|| call_type.clone());
Some(ForeignCall { let arguments = new_args.into_bump_slice();
foreign_symbol: foreign_symbol.clone(),
arguments: args, Some(Expr::Call(self::Call {
ret_layout: ret_layout.clone(), call_type,
}) arguments,
}))
} else { } else {
None None
} }
@ -5211,13 +5216,15 @@ fn call_by_name<'a>(
"see call_by_name for background (scroll down a bit)" "see call_by_name for background (scroll down a bit)"
); );
let call = Expr::FunctionCall { let call = Expr::Call(self::Call {
call_type: CallType::ByName(proc_name), call_type: CallType::ByName {
name: proc_name,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
full_layout: full_layout.clone(), full_layout: full_layout.clone(),
arg_layouts, arg_layouts,
args: field_symbols, },
}; arguments: field_symbols,
});
let result = Stmt::Let(assigned, call, ret_layout.clone(), hole); let result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
@ -5259,13 +5266,16 @@ fn call_by_name<'a>(
field_symbols.len(), field_symbols.len(),
"see call_by_name for background (scroll down a bit)" "see call_by_name for background (scroll down a bit)"
); );
let call = Expr::FunctionCall {
call_type: CallType::ByName(proc_name), let call = Expr::Call(self::Call {
call_type: CallType::ByName {
name: proc_name,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
full_layout: full_layout.clone(), full_layout: full_layout.clone(),
arg_layouts, arg_layouts,
args: field_symbols, },
}; arguments: field_symbols,
});
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev()); let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
@ -5319,13 +5329,19 @@ fn call_by_name<'a>(
// and we have to fix it here. // and we have to fix it here.
match full_layout { match full_layout {
Layout::Closure(_, closure_layout, _) => { Layout::Closure(_, closure_layout, _) => {
let call = Expr::FunctionCall { let call = Expr::Call(self::Call {
call_type: CallType::ByName(proc_name), call_type: CallType::ByName {
ret_layout: function_layout.result.clone(), name: proc_name,
full_layout: function_layout.full.clone(), ret_layout: function_layout
.result
.clone(),
full_layout: function_layout
.full
.clone(),
arg_layouts: function_layout.arguments, arg_layouts: function_layout.arguments,
args: field_symbols, },
}; arguments: field_symbols,
});
// in the case of a closure specifically, we // in the case of a closure specifically, we
// have to create a custom layout, to make sure // have to create a custom layout, to make sure
@ -5346,13 +5362,19 @@ fn call_by_name<'a>(
) )
} }
_ => { _ => {
let call = Expr::FunctionCall { let call = Expr::Call(self::Call {
call_type: CallType::ByName(proc_name), call_type: CallType::ByName {
ret_layout: function_layout.result.clone(), name: proc_name,
full_layout: function_layout.full.clone(), ret_layout: function_layout
.result
.clone(),
full_layout: function_layout
.full
.clone(),
arg_layouts: function_layout.arguments, arg_layouts: function_layout.arguments,
args: field_symbols, },
}; arguments: field_symbols,
});
Stmt::Let( Stmt::Let(
assigned, assigned,
@ -5368,13 +5390,15 @@ fn call_by_name<'a>(
field_symbols.len(), field_symbols.len(),
"scroll up a bit for background" "scroll up a bit for background"
); );
let call = Expr::FunctionCall { let call = Expr::Call(self::Call {
call_type: CallType::ByName(proc_name), call_type: CallType::ByName {
name: proc_name,
ret_layout: function_layout.result.clone(), ret_layout: function_layout.result.clone(),
full_layout: function_layout.full, full_layout: function_layout.full.clone(),
arg_layouts: function_layout.arguments, arg_layouts: function_layout.arguments,
args: field_symbols, },
}; arguments: field_symbols,
});
let iter = loc_args let iter = loc_args
.into_iter() .into_iter()
@ -5420,13 +5444,15 @@ fn call_by_name<'a>(
"scroll up a bit for background" "scroll up a bit for background"
); );
let call = Expr::FunctionCall { let call = Expr::Call(self::Call {
call_type: CallType::ByName(proc_name), call_type: CallType::ByName {
name: proc_name,
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
full_layout: full_layout.clone(), full_layout: full_layout.clone(),
arg_layouts, arg_layouts,
args: field_symbols, },
}; arguments: field_symbols,
});
let iter = let iter =
loc_args.into_iter().rev().zip(field_symbols.iter().rev()); loc_args.into_iter().rev().zip(field_symbols.iter().rev());

View file

@ -75,17 +75,17 @@ fn insert_jumps<'a>(
match stmt { match stmt {
Let( Let(
symbol, symbol,
Expr::FunctionCall { Expr::Call(crate::ir::Call {
call_type: CallType::ByName(fsym), call_type: CallType::ByName { name: fsym, .. },
args, arguments,
.. ..
}, }),
_, _,
Stmt::Ret(rsym), Stmt::Ret(rsym),
) if needle == *fsym && symbol == rsym => { ) if needle == *fsym && symbol == rsym => {
// replace the call and return with a jump // replace the call and return with a jump
let jump = Stmt::Jump(goal_id, args); let jump = Stmt::Jump(goal_id, arguments);
Some(arena.alloc(jump)) Some(arena.alloc(jump))
} }