This commit is contained in:
Folkert 2021-10-17 16:08:41 +02:00
parent cdec69d8a8
commit 9763f9b51b
7 changed files with 912 additions and 1 deletions

View file

@ -786,6 +786,7 @@ where
CallType::ByName { .. } => {} CallType::ByName { .. } => {}
CallType::LowLevel { .. } => {} CallType::LowLevel { .. } => {}
CallType::HigherOrderLowLevel { .. } => {} CallType::HigherOrderLowLevel { .. } => {}
CallType::NewHigherOrderLowLevel { .. } => {}
CallType::Foreign { .. } => {} CallType::Foreign { .. } => {}
} }
} }

View file

@ -977,6 +977,36 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
) )
} }
CallType::NewHigherOrderLowLevel {
op,
function_owns_closure_data,
specialization_id,
function_name,
function_env,
arg_layouts,
ret_layout,
..
} => {
let bytes = specialization_id.to_bytes();
let callee_var = CalleeSpecVar(&bytes);
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
run_new_higher_order_low_level(
env,
layout_ids,
scope,
layout,
*op,
func_spec,
arg_layouts,
ret_layout,
*function_owns_closure_data,
*function_name,
function_env,
arguments,
)
}
CallType::Foreign { CallType::Foreign {
foreign_symbol, foreign_symbol,
ret_layout, ret_layout,
@ -4456,6 +4486,418 @@ fn roc_function_call<'a, 'ctx, 'env>(
} }
} }
#[allow(clippy::too_many_arguments)]
fn run_new_higher_order_low_level<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
scope: &Scope<'a, 'ctx>,
return_layout: &Layout<'a>,
op: roc_mono::low_level::HigherOrder,
func_spec: FuncSpec,
argument_layouts: &[Layout<'a>],
result_layout: &Layout<'a>,
function_owns_closure_data: bool,
function_name: Symbol,
function_env: &Symbol,
args: &[Symbol],
) -> BasicValueEnum<'ctx> {
use roc_mono::low_level::HigherOrder::*;
// macros because functions cause lifetime issues related to the `env` or `layout_ids`
macro_rules! function_details {
() => {{
let function = function_value_by_func_spec(
env,
func_spec,
function_name,
argument_layouts,
return_layout,
);
let (closure, closure_layout) = load_symbol_and_lambda_set(scope, function_env);
(function, closure, closure_layout)
}};
}
macro_rules! list_walk {
($variant:expr, $xs:expr, $state:expr) => {{
let (list, list_layout) = load_symbol_and_layout(scope, &$xs);
let (default, default_layout) = load_symbol_and_layout(scope, &$state);
let (function, closure, closure_layout) = function_details!();
match list_layout {
Layout::Builtin(Builtin::EmptyList) => default,
Layout::Builtin(Builtin::List(element_layout)) => {
let argument_layouts = &[*default_layout, **element_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
crate::llvm::build_list::list_walk_generic(
env,
layout_ids,
roc_function_call,
result_layout,
list,
element_layout,
default,
default_layout,
$variant,
)
}
_ => unreachable!("invalid list layout"),
}
}};
}
match op {
ListMap { xs } => {
// List.map : List before, (before -> after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match (list_layout, return_layout) {
(Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(element_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[**element_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_map(env, roc_function_call, list, element_layout, result_layout)
}
_ => unreachable!("invalid list layout"),
}
}
ListMap2 { xs, ys } => {
let (list1, list1_layout) = load_symbol_and_layout(scope, &xs);
let (list2, list2_layout) = load_symbol_and_layout(scope, &ys);
let (function, closure, closure_layout) = function_details!();
match (list1_layout, list2_layout, return_layout) {
(
Layout::Builtin(Builtin::List(element1_layout)),
Layout::Builtin(Builtin::List(element2_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[**element1_layout, **element2_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_map2(
env,
layout_ids,
roc_function_call,
list1,
list2,
element1_layout,
element2_layout,
result_layout,
)
}
(Layout::Builtin(Builtin::EmptyList), _, _)
| (_, Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMap3 { xs, ys, zs } => {
let (list1, list1_layout) = load_symbol_and_layout(scope, &xs);
let (list2, list2_layout) = load_symbol_and_layout(scope, &ys);
let (list3, list3_layout) = load_symbol_and_layout(scope, &zs);
let (function, closure, closure_layout) = function_details!();
match (list1_layout, list2_layout, list3_layout, return_layout) {
(
Layout::Builtin(Builtin::List(element1_layout)),
Layout::Builtin(Builtin::List(element2_layout)),
Layout::Builtin(Builtin::List(element3_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts =
&[**element1_layout, **element2_layout, **element3_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_map3(
env,
layout_ids,
roc_function_call,
list1,
list2,
list3,
element1_layout,
element2_layout,
element3_layout,
result_layout,
)
}
(Layout::Builtin(Builtin::EmptyList), _, _, _)
| (_, Layout::Builtin(Builtin::EmptyList), _, _)
| (_, _, Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMapWithIndex { xs } => {
// List.mapWithIndex : List before, (Nat, before -> after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match (list_layout, return_layout) {
(Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(element_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[Layout::Builtin(Builtin::Usize), **element_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_map_with_index(env, roc_function_call, list, element_layout, result_layout)
}
_ => unreachable!("invalid list layout"),
}
}
ListKeepIf { xs } => {
// List.keepIf : List elem, (elem -> Bool) -> List elem
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(element_layout)) => {
let argument_layouts = &[**element_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_keep_if(env, layout_ids, roc_function_call, list, element_layout)
}
_ => unreachable!("invalid list layout"),
}
}
ListKeepOks { xs } => {
// List.keepOks : List before, (before -> Result after *) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match (list_layout, return_layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(before_layout)),
Layout::Builtin(Builtin::List(after_layout)),
) => {
let argument_layouts = &[**before_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_keep_oks(
env,
layout_ids,
roc_function_call,
result_layout,
list,
before_layout,
after_layout,
)
}
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListKeepErrs { xs } => {
// List.keepErrs : List before, (before -> Result * after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match (list_layout, return_layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(before_layout)),
Layout::Builtin(Builtin::List(after_layout)),
) => {
let argument_layouts = &[**before_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_keep_errs(
env,
layout_ids,
roc_function_call,
result_layout,
list,
before_layout,
after_layout,
)
}
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListWalk { xs, state } => {
list_walk!(crate::llvm::build_list::ListWalk::Walk, xs, state)
}
ListWalkUntil { xs, state } => {
list_walk!(crate::llvm::build_list::ListWalk::WalkUntil, xs, state)
}
ListWalkBackwards { xs, state } => {
list_walk!(crate::llvm::build_list::ListWalk::WalkBackwards, xs, state)
}
ListSortWith { xs } => {
// List.sortWith : List a, (a, a -> Ordering) -> List a
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
let (function, closure, closure_layout) = function_details!();
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(element_layout)) => {
use crate::llvm::bitcode::build_compare_wrapper;
let argument_layouts = &[**element_layout, **element_layout];
let compare_wrapper =
build_compare_wrapper(env, function, closure_layout, element_layout)
.as_global_value()
.as_pointer_value();
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
list_sort_with(
env,
roc_function_call,
compare_wrapper,
list,
element_layout,
)
}
_ => unreachable!("invalid list layout"),
}
}
DictWalk { xs, state } => {
let (dict, dict_layout) = load_symbol_and_layout(scope, &xs);
let (default, default_layout) = load_symbol_and_layout(scope, &state);
let (function, closure, closure_layout) = function_details!();
match dict_layout {
Layout::Builtin(Builtin::EmptyDict) => {
// no elements, so `key` is not in here
panic!("key type unknown")
}
Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => {
let argument_layouts = &[*default_layout, **key_layout, **value_layout];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
);
dict_walk(
env,
roc_function_call,
dict,
default,
key_layout,
value_layout,
default_layout,
)
}
_ => unreachable!("invalid dict layout"),
}
}
_ => unreachable!(),
}
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn run_higher_order_low_level<'a, 'ctx, 'env>( fn run_higher_order_low_level<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,

View file

@ -605,6 +605,201 @@ fn call_spec(
*update_mode, *update_mode,
call.arguments, call.arguments,
), ),
NewHigherOrderLowLevel {
specialization_id,
closure_env_layout,
op,
arg_layouts,
ret_layout,
function_name,
function_env,
..
} => {
let array = specialization_id.to_bytes();
let spec_var = CalleeSpecVar(&array);
let it = arg_layouts.iter().copied();
let bytes = func_name_bytes_help(*function_name, it, *ret_layout);
let name = FuncName(&bytes);
let module = MOD_APP;
use crate::low_level::HigherOrder::*;
match op {
DictWalk { xs, state } => {
let dict = env.symbols[xs];
let state = env.symbols[state];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?;
let key = builder.add_get_tuple_field(block, first, 0)?;
let val = builder.add_get_tuple_field(block, first, 1)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[state, key, val])?
} else {
builder.add_make_tuple(block, &[state, key, val, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListWalk { xs, state }
| ListWalkBackwards { xs, state }
| ListWalkUntil { xs, state } => {
let list = env.symbols[xs];
let state = env.symbols[state];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[state, first])?
} else {
builder.add_make_tuple(block, &[state, first, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListMapWithIndex { xs } => {
let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?;
let index = builder.add_make_tuple(block, &[])?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[index, first])?
} else {
builder.add_make_tuple(block, &[index, first, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListMap { xs } => {
let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[elem1])?
} else {
builder.add_make_tuple(block, &[elem1, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListSortWith { xs } => {
let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[elem1, elem1])?
} else {
builder.add_make_tuple(block, &[elem1, elem1, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListMap2 { xs, ys } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?;
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
let elem2 = builder.add_bag_get(block, bag2)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[elem1, elem2])?
} else {
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListMap3 { xs, ys, zs } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let list3 = env.symbols[zs];
let closure_env = env.symbols[function_env];
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
let elem1 = builder.add_bag_get(block, bag1)?;
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
let elem2 = builder.add_bag_get(block, bag2)?;
let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?;
let elem3 = builder.add_bag_get(block, bag3)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[elem1, elem2, elem3])?
} else {
builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])?
};
builder.add_call(block, spec_var, module, name, argument)?;
}
ListKeepIf { xs } | ListKeepOks { xs } | ListKeepErrs { xs } => {
let list = env.symbols[xs];
let closure_env = env.symbols[function_env];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
// let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let first = builder.add_bag_get(block, bag)?;
let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[first])?
} else {
builder.add_make_tuple(block, &[first, closure_env])?
};
let result = builder.add_call(block, spec_var, module, name, argument)?;
let unit = builder.add_tuple_type(&[])?;
builder.add_unknown_with(block, &[result], unit)?;
}
}
// TODO overly pessimstic
// filter_map because one of the arguments is a function name, which
// is not defined in the env
let arguments: Vec<_> = call
.arguments
.iter()
.filter_map(|symbol| env.symbols.get(symbol))
.copied()
.collect();
let result_type = layout_spec(builder, layout)?;
builder.add_unknown_with(block, &arguments, result_type)
}
HigherOrderLowLevel { HigherOrderLowLevel {
specialization_id, specialization_id,
closure_env_layout, closure_env_layout,

View file

@ -593,6 +593,91 @@ impl<'a> BorrowInfState<'a> {
self.own_args_using_bools(arguments, ps); self.own_args_using_bools(arguments, ps);
} }
NewHigherOrderLowLevel {
op,
arg_layouts,
ret_layout,
function_name,
function_env,
..
} => {
use crate::low_level::HigherOrder::*;
let closure_layout = ProcLayout {
arguments: arg_layouts,
result: *ret_layout,
};
let function_ps = match param_map.get_symbol(*function_name, closure_layout) {
Some(function_ps) => function_ps,
None => unreachable!(),
};
match op {
ListMap { xs }
| ListKeepIf { xs }
| ListKeepOks { xs }
| ListKeepErrs { xs } => {
// own the list if the function wants to own the element
if !function_ps[0].borrow {
self.own_var(*xs);
}
}
ListMapWithIndex { xs } => {
// own the list if the function wants to own the element
if !function_ps[1].borrow {
self.own_var(*xs);
}
}
ListMap2 { xs, ys } => {
// own the lists if the function wants to own the element
if !function_ps[0].borrow {
self.own_var(*xs);
}
if !function_ps[1].borrow {
self.own_var(*ys);
}
}
ListMap3 { xs, ys, zs } => {
// own the lists if the function wants to own the element
if !function_ps[0].borrow {
self.own_var(*xs);
}
if !function_ps[1].borrow {
self.own_var(*ys);
}
if !function_ps[2].borrow {
self.own_var(*zs);
}
}
ListSortWith { xs } => {
// always own the input list
self.own_var(*xs);
}
ListWalk { xs, state }
| ListWalkUntil { xs, state }
| ListWalkBackwards { xs, state }
| DictWalk { xs, state } => {
// own the default value if the function wants to own it
if !function_ps[0].borrow {
self.own_var(*state);
}
// own the data structure if the function wants to own the element
if !function_ps[1].borrow {
self.own_var(*xs);
}
}
}
// own the closure environment if the function needs to own it
let function_env_position = op.function_arity();
if let Some(false) = function_ps.get(function_env_position).map(|p| p.borrow) {
self.own_var(*function_env);
}
}
HigherOrderLowLevel { HigherOrderLowLevel {
op, op,
arg_layouts, arg_layouts,
@ -952,7 +1037,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
use LowLevel::*; use LowLevel::*;
// TODO is true or false more efficient for non-refcounted layouts? // TODO is true or false more efficient for non-refcounted layouts?
let irrelevant = OWNED; let irrelevant = BORROWED;
let function = irrelevant; let function = irrelevant;
let closure_data = irrelevant; let closure_data = irrelevant;
let owned = OWNED; let owned = OWNED;
@ -1071,6 +1156,7 @@ fn call_info_call<'a>(call: &crate::ir::Call<'a>, info: &mut CallInfo<'a>) {
Foreign { .. } => {} Foreign { .. } => {}
LowLevel { .. } => {} LowLevel { .. } => {}
HigherOrderLowLevel { .. } => {} HigherOrderLowLevel { .. } => {}
NewHigherOrderLowLevel { .. } => {}
} }
} }

View file

@ -463,6 +463,163 @@ impl<'a> Context<'a> {
&*self.arena.alloc(Stmt::Let(z, v, l, b)) &*self.arena.alloc(Stmt::Let(z, v, l, b))
} }
NewHigherOrderLowLevel {
op,
closure_env_layout,
specialization_id,
arg_layouts,
ret_layout,
function_name,
function_env,
..
} => {
// setup
use crate::low_level::HigherOrder::*;
macro_rules! create_call {
($borrows:expr) => {
Expr::Call(crate::ir::Call {
call_type: if let Some(OWNED) = $borrows.map(|p| p.borrow) {
NewHigherOrderLowLevel {
op: *op,
closure_env_layout: *closure_env_layout,
function_owns_closure_data: true,
specialization_id: *specialization_id,
function_name: *function_name,
function_env: *function_env,
arg_layouts,
ret_layout: *ret_layout,
}
} else {
call_type
},
arguments,
})
};
}
macro_rules! decref_if_owned {
($borrows:expr, $argument:expr, $stmt:expr) => {
if !$borrows {
self.arena.alloc(Stmt::Refcounting(
ModifyRc::DecRef($argument),
self.arena.alloc($stmt),
))
} else {
$stmt
}
};
}
const FUNCTION: bool = BORROWED;
const CLOSURE_DATA: bool = BORROWED;
let function_layout = ProcLayout {
arguments: arg_layouts,
result: *ret_layout,
};
let function_ps = match self.param_map.get_symbol(*function_name, function_layout) {
Some(function_ps) => function_ps,
None => unreachable!(),
};
match op {
ListMap { xs }
| ListKeepIf { xs }
| ListKeepOks { xs }
| ListKeepErrs { xs } => {
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
// if the list is owned, then all elements have been consumed, but not the list itself
let b = decref_if_owned!(function_ps[0].borrow, *xs, b);
let v = create_call!(function_ps.get(1));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListMap2 { xs, ys } => {
let borrows = [
function_ps[0].borrow,
function_ps[1].borrow,
FUNCTION,
CLOSURE_DATA,
];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let b = decref_if_owned!(function_ps[0].borrow, *xs, b);
let b = decref_if_owned!(function_ps[1].borrow, *ys, b);
let v = create_call!(function_ps.get(2));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListMap3 { xs, ys, zs } => {
let borrows = [
function_ps[0].borrow,
function_ps[1].borrow,
function_ps[2].borrow,
FUNCTION,
CLOSURE_DATA,
];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let b = decref_if_owned!(function_ps[0].borrow, *xs, b);
let b = decref_if_owned!(function_ps[1].borrow, *ys, b);
let b = decref_if_owned!(function_ps[2].borrow, *zs, b);
let v = create_call!(function_ps.get(3));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListMapWithIndex { xs } => {
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let b = decref_if_owned!(function_ps[1].borrow, *xs, b);
let v = create_call!(function_ps.get(2));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListSortWith { xs: _ } => {
let borrows = [OWNED, FUNCTION, CLOSURE_DATA];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let v = create_call!(function_ps.get(2));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ListWalk { xs, state: _ }
| ListWalkUntil { xs, state: _ }
| ListWalkBackwards { xs, state: _ }
| DictWalk { xs, state: _ } => {
// borrow data structure based on first argument of the folded function
// borrow the default based on second argument of the folded function
let borrows = [
function_ps[1].borrow,
function_ps[0].borrow,
FUNCTION,
CLOSURE_DATA,
];
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
let b = decref_if_owned!(function_ps[1].borrow, *xs, b);
let v = create_call!(function_ps.get(2));
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
}
}
HigherOrderLowLevel { HigherOrderLowLevel {
op, op,
closure_env_layout, closure_env_layout,

View file

@ -1050,6 +1050,13 @@ impl<'a> Call<'a> {
.text(format!("lowlevel {:?} ", lowlevel)) .text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " ")) .append(alloc.intersperse(it, " "))
} }
NewHigherOrderLowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
}
Foreign { Foreign {
ref foreign_symbol, .. ref foreign_symbol, ..
} => { } => {
@ -1113,6 +1120,27 @@ pub enum CallType<'a> {
arg_layouts: &'a [Layout<'a>], arg_layouts: &'a [Layout<'a>],
ret_layout: Layout<'a>, ret_layout: Layout<'a>,
}, },
NewHigherOrderLowLevel {
op: crate::low_level::HigherOrder,
/// the layout of the closure argument, if any
closure_env_layout: Option<Layout<'a>>,
/// name of the top-level function that is passed as an argument
/// e.g. in `List.map xs Num.abs` this would be `Num.abs`
function_name: Symbol,
/// Symbol of the environment captured by the function argument
function_env: Symbol,
/// does the function argument need to own the closure data
function_owns_closure_data: bool,
/// specialization id of the function argument, used for name generation
specialization_id: CallSpecId,
/// function layout, used for name generation
arg_layouts: &'a [Layout<'a>],
ret_layout: Layout<'a>,
},
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -5447,6 +5475,7 @@ fn substitute_in_call<'a>(
CallType::Foreign { .. } => None, CallType::Foreign { .. } => None,
CallType::LowLevel { .. } => None, CallType::LowLevel { .. } => None,
CallType::HigherOrderLowLevel { .. } => None, CallType::HigherOrderLowLevel { .. } => None,
CallType::NewHigherOrderLowLevel { .. } => None,
}; };
let mut did_change = false; let mut did_change = false;

View file

@ -8,6 +8,7 @@ pub mod expand_rc;
pub mod inc_dec; pub mod inc_dec;
pub mod ir; pub mod ir;
pub mod layout; pub mod layout;
pub mod low_level;
pub mod reset_reuse; pub mod reset_reuse;
pub mod tail_recursion; pub mod tail_recursion;