add HigherOrderLowLevel

This commit is contained in:
Folkert 2021-05-16 22:45:16 +02:00
parent e675bac893
commit 2b72f9e733
7 changed files with 394 additions and 324 deletions

View File

@ -881,17 +881,23 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
} }
CallType::LowLevel { CallType::LowLevel { op } => {
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments)
}
CallType::HigherOrderLowLevel {
op, op,
opt_closure_layout, closure_layout,
} => run_low_level( function_owns_closure_data,
} => run_higher_order_low_level(
env, env,
layout_ids, layout_ids,
scope, scope,
parent, parent,
layout, layout,
*op, *op,
*opt_closure_layout, *closure_layout,
*function_owns_closure_data,
arguments, arguments,
), ),
@ -2157,6 +2163,10 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
CallType::LowLevel { .. } => { CallType::LowLevel { .. } => {
unreachable!("lowlevel itself never throws exceptions") unreachable!("lowlevel itself never throws exceptions")
} }
CallType::HigherOrderLowLevel { .. } => {
unreachable!("lowlevel itself never throws exceptions")
}
}, },
Rethrow => { Rethrow => {
@ -3577,22 +3587,24 @@ pub static FAST_CALL_CONV: u32 = 8;
pub static COLD_CALL_CONV: u32 = 9; pub static COLD_CALL_CONV: u32 = 9;
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn run_low_level<'a, 'ctx, 'env>( fn run_higher_order_low_level<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
scope: &Scope<'a, 'ctx>, scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
layout: &Layout<'a>, layout: &Layout<'a>,
op: LowLevel, op: LowLevel,
opt_closure_layout: Option<Layout<'a>>, function_layout: Layout<'a>,
function_owns_closure_data: bool,
args: &[Symbol], args: &[Symbol],
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use LowLevel::*; use LowLevel::*;
debug_assert!(op.is_higher_order());
// macros because functions cause lifetime issues related to the `env` or `layout_ids` // macros because functions cause lifetime issues related to the `env` or `layout_ids`
macro_rules! passed_function_at_index { macro_rules! passed_function_at_index {
($index:expr) => {{ ($index:expr) => {{
let function_layout = opt_closure_layout.unwrap();
let function_symbol = args[$index]; let function_symbol = args[$index];
let fn_name = layout_ids let fn_name = layout_ids
@ -3616,7 +3628,6 @@ fn run_low_level<'a, 'ctx, 'env>(
let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); let (default, default_layout) = load_symbol_and_layout(scope, &args[1]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(2); let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]);
@ -3643,6 +3654,285 @@ fn run_low_level<'a, 'ctx, 'env>(
} }
}}; }};
} }
match op {
ListMap => {
// List.map : List before, (before -> after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_map(
env,
function,
function_layout,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListMap2 => {
debug_assert_eq!(args.len(), 4);
let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]);
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]);
match (list1_layout, list2_layout) {
(
Layout::Builtin(Builtin::List(_, element1_layout)),
Layout::Builtin(Builtin::List(_, element2_layout)),
) => list_map2(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list1,
list2,
element1_layout,
element2_layout,
),
(Layout::Builtin(Builtin::EmptyList), _)
| (_, Layout::Builtin(Builtin::EmptyList)) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMap3 => {
debug_assert_eq!(args.len(), 5);
let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]);
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]);
let function = passed_function_at_index!(3);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[4]);
match (list1_layout, list2_layout, list3_layout) {
(
Layout::Builtin(Builtin::List(_, element1_layout)),
Layout::Builtin(Builtin::List(_, element2_layout)),
Layout::Builtin(Builtin::List(_, element3_layout)),
) => list_map3(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list1,
list2,
list3,
element1_layout,
element2_layout,
element3_layout,
),
(Layout::Builtin(Builtin::EmptyList), _, _)
| (_, Layout::Builtin(Builtin::EmptyList), _)
| (_, _, Layout::Builtin(Builtin::EmptyList)) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMapWithIndex => {
// List.mapWithIndex : List before, (Nat, before -> after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_map_with_index(
env,
function,
function_layout,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListKeepIf => {
// List.keepIf : List elem, (elem -> Bool) -> List elem
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_keep_if(
env,
layout_ids,
function,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListKeepOks => {
// List.keepOks : List before, (before -> Result after *) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match (list_layout, layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(_, before_layout)),
Layout::Builtin(Builtin::List(_, after_layout)),
) => list_keep_oks(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list,
before_layout,
after_layout,
),
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListKeepErrs => {
// List.keepErrs : List before, (before -> Result * after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match (list_layout, layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(_, before_layout)),
Layout::Builtin(Builtin::List(_, after_layout)),
) => list_keep_errs(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list,
before_layout,
after_layout,
),
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListWalk => {
list_walk!(crate::llvm::build_list::ListWalk::Walk)
}
ListWalkUntil => {
list_walk!(crate::llvm::build_list::ListWalk::WalkUntil)
}
ListWalkBackwards => {
list_walk!(crate::llvm::build_list::ListWalk::WalkBackwards)
}
ListSortWith => {
// List.sortWith : List a, (a, a -> Ordering) -> List a
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_sort_with(
env,
function,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
DictWalk => {
debug_assert_eq!(args.len(), 4);
let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]);
let (default, default_layout) = load_symbol_and_layout(scope, &args[1]);
let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]);
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)) => dict_walk(
env,
layout_ids,
dict,
function,
closure,
*closure_layout,
default,
key_layout,
value_layout,
default_layout,
),
_ => unreachable!("invalid dict layout"),
}
}
_ => unreachable!(),
}
}
#[allow(clippy::too_many_arguments)]
fn run_low_level<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
layout: &Layout<'a>,
op: LowLevel,
args: &[Symbol],
) -> BasicValueEnum<'ctx> {
use LowLevel::*;
debug_assert!(!op.is_higher_order());
match op { match op {
StrConcat => { StrConcat => {
@ -3784,213 +4074,6 @@ fn run_low_level<'a, 'ctx, 'env>(
list_concat(env, inplace, parent, first_list, second_list, list_layout) list_concat(env, inplace, parent, first_list, second_list, list_layout)
} }
ListMap => {
// List.map : List before, (before -> after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_map(
env,
function,
function_layout,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListMap2 => {
debug_assert_eq!(args.len(), 4);
let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]);
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]);
match (list1_layout, list2_layout) {
(
Layout::Builtin(Builtin::List(_, element1_layout)),
Layout::Builtin(Builtin::List(_, element2_layout)),
) => list_map2(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list1,
list2,
element1_layout,
element2_layout,
),
(Layout::Builtin(Builtin::EmptyList), _)
| (_, Layout::Builtin(Builtin::EmptyList)) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMap3 => {
debug_assert_eq!(args.len(), 5);
let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]);
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(3);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[4]);
match (list1_layout, list2_layout, list3_layout) {
(
Layout::Builtin(Builtin::List(_, element1_layout)),
Layout::Builtin(Builtin::List(_, element2_layout)),
Layout::Builtin(Builtin::List(_, element3_layout)),
) => list_map3(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list1,
list2,
list3,
element1_layout,
element2_layout,
element3_layout,
),
(Layout::Builtin(Builtin::EmptyList), _, _)
| (_, Layout::Builtin(Builtin::EmptyList), _)
| (_, _, Layout::Builtin(Builtin::EmptyList)) => empty_list(env),
_ => unreachable!("invalid list layout"),
}
}
ListMapWithIndex => {
// List.mapWithIndex : List before, (Nat, before -> after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_map_with_index(
env,
function,
function_layout,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListKeepIf => {
// List.keepIf : List elem, (elem -> Bool) -> List elem
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_keep_if(
env,
layout_ids,
function,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
ListKeepOks => {
// List.keepOks : List before, (before -> Result after *) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match (list_layout, layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(_, before_layout)),
Layout::Builtin(Builtin::List(_, after_layout)),
) => list_keep_oks(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list,
before_layout,
after_layout,
),
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListKeepErrs => {
// List.keepErrs : List before, (before -> Result * after) -> List after
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function_layout = opt_closure_layout.unwrap();
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match (list_layout, layout) {
(_, Layout::Builtin(Builtin::EmptyList))
| (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
(
Layout::Builtin(Builtin::List(_, before_layout)),
Layout::Builtin(Builtin::List(_, after_layout)),
) => list_keep_errs(
env,
layout_ids,
function,
function_layout,
closure,
*closure_layout,
list,
before_layout,
after_layout,
),
(other1, other2) => {
unreachable!("invalid list layouts:\n{:?}\n{:?}", other1, other2)
}
}
}
ListContains => { ListContains => {
// List.contains : List elem, elem -> Bool // List.contains : List elem, elem -> Bool
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
@ -4015,15 +4098,6 @@ fn run_low_level<'a, 'ctx, 'env>(
list_range(env, *builtin, low.into_int_value(), high.into_int_value()) list_range(env, *builtin, low.into_int_value(), high.into_int_value())
} }
ListWalk => {
list_walk!(crate::llvm::build_list::ListWalk::Walk)
}
ListWalkUntil => {
list_walk!(crate::llvm::build_list::ListWalk::WalkUntil)
}
ListWalkBackwards => {
list_walk!(crate::llvm::build_list::ListWalk::WalkBackwards)
}
ListAppend => { ListAppend => {
// List.append : List elem, elem -> List elem // List.append : List elem, elem -> List elem
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
@ -4056,29 +4130,6 @@ fn run_low_level<'a, 'ctx, 'env>(
list_join(env, inplace, parent, list, outer_list_layout) list_join(env, inplace, parent, list, outer_list_layout)
} }
ListSortWith => {
// List.sortWith : List a, (a, a -> Ordering) -> List a
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => list_sort_with(
env,
function,
closure,
*closure_layout,
list,
element_layout,
),
_ => unreachable!("invalid list layout"),
}
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => { | NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
@ -4512,34 +4563,6 @@ fn run_low_level<'a, 'ctx, 'env>(
_ => unreachable!("invalid dict layout"), _ => unreachable!("invalid dict layout"),
} }
} }
DictWalk => {
debug_assert_eq!(args.len(), 4);
let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]);
let (default, default_layout) = load_symbol_and_layout(scope, &args[1]);
let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]);
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)) => dict_walk(
env,
layout_ids,
dict,
function,
closure,
*closure_layout,
default,
key_layout,
value_layout,
default_layout,
),
_ => unreachable!("invalid dict layout"),
}
}
SetFromList => { SetFromList => {
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
@ -4581,6 +4604,10 @@ fn run_low_level<'a, 'ctx, 'env>(
cond cond
} }
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
} }
} }

View File

@ -211,10 +211,9 @@ where
} }
} }
CallType::LowLevel { CallType::LowLevel { op: lowlevel } => {
op: lowlevel, self.build_run_low_level(sym, lowlevel, arguments, layout)
opt_closure_layout: _, }
} => self.build_run_low_level(sym, lowlevel, arguments, layout),
x => Err(format!("the call type, {:?}, is not yet implemented", x)), x => Err(format!("the call type, {:?}, is not yet implemented", x)),
} }
} }
@ -524,6 +523,7 @@ where
self.set_last_seen(*sym, stmt); self.set_last_seen(*sym, stmt);
} }
CallType::LowLevel { .. } => {} CallType::LowLevel { .. } => {}
CallType::HigherOrderLowLevel { .. } => {}
CallType::Foreign { .. } => {} CallType::Foreign { .. } => {}
} }
} }

View File

@ -102,7 +102,9 @@ pub enum LowLevel {
} }
impl LowLevel { impl LowLevel {
pub fn is_higher_order_function(&self) -> bool { /// is one of the arguments always a function?
/// An example is List.map.
pub fn is_higher_order(&self) -> bool {
use LowLevel::*; use LowLevel::*;
match self { match self {

View File

@ -407,29 +407,36 @@ impl<'a> BorrowInfState<'a> {
self.own_args(arguments); self.own_args(arguments);
} }
LowLevel { LowLevel { op } => {
op, debug_assert!(!op.is_higher_order());
opt_closure_layout,
self.own_var(z);
let ps = lowlevel_borrow_signature(self.arena, *op);
self.own_args_using_bools(arguments, ps);
}
HigherOrderLowLevel {
op, closure_layout, ..
} => { } => {
use roc_module::low_level::LowLevel::*; use roc_module::low_level::LowLevel::*;
match op {
ListMap => {
match self
.param_map
.get_symbol(arguments[1], opt_closure_layout.unwrap())
{
Some(ps) => {
if !ps[0].borrow {
self.own_var(arguments[0]);
}
if ps.len() > 1 && !ps[1].borrow { debug_assert!(op.is_higher_order());
self.own_var(arguments[2]);
} match op {
ListMap => match self.param_map.get_symbol(arguments[1], *closure_layout) {
Some(ps) => {
if !ps[0].borrow {
self.own_var(arguments[0]);
}
if ps.len() > 1 && !ps[1].borrow {
self.own_var(arguments[2]);
} }
None => unreachable!(),
} }
} None => unreachable!(),
},
_ => { _ => {
// 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);

View File

@ -1405,10 +1405,7 @@ fn compile_test_help<'a>(
}; };
let test = Expr::Call(crate::ir::Call { let test = Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel { call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
op: LowLevel::Eq,
opt_closure_layout: None,
},
arguments: arena.alloc([lhs, rhs]), arguments: arena.alloc([lhs, rhs]),
}); });

View File

@ -441,15 +441,23 @@ impl<'a> Context<'a> {
use crate::ir::CallType::*; use crate::ir::CallType::*;
match &call_type { match &call_type {
LowLevel { LowLevel { op } => {
op, let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
opt_closure_layout, 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))
}
HigherOrderLowLevel {
op, closure_layout, ..
} => match op { } => match op {
roc_module::low_level::LowLevel::ListMap => { roc_module::low_level::LowLevel::ListMap => {
match self match self.param_map.get_symbol(arguments[1], *closure_layout) {
.param_map
.get_symbol(arguments[1], opt_closure_layout.unwrap())
{
Some(ps) => { Some(ps) => {
let b = if ps[0].borrow { let b = if ps[0].borrow {
let ps = [BORROWED, BORROWED, BORROWED]; let ps = [BORROWED, BORROWED, BORROWED];
@ -463,6 +471,18 @@ impl<'a> Context<'a> {
)) ))
}; };
let call_type = {
if ps[1].borrow {
call_type
} else {
HigherOrderLowLevel {
op: *op,
closure_layout: *closure_layout,
function_owns_closure_data: true,
}
}
};
let v = Expr::Call(crate::ir::Call { let v = Expr::Call(crate::ir::Call {
call_type, call_type,
arguments, arguments,
@ -790,14 +810,15 @@ impl<'a> Context<'a> {
use crate::ir::CallType; use crate::ir::CallType;
let stmt = match &call.call_type { let stmt = match &call.call_type {
CallType::LowLevel { CallType::LowLevel { op } => {
op,
opt_closure_layout: _,
} => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars) self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
} }
CallType::HigherOrderLowLevel { .. } => {
todo!("copy the code for normal calls over to here");
}
CallType::Foreign { .. } => { CallType::Foreign { .. } => {
let ps = crate::borrow::foreign_borrow_signature( let ps = crate::borrow::foreign_borrow_signature(
self.arena, self.arena,

View File

@ -1047,10 +1047,14 @@ impl<'a> Call<'a> {
.text("CallByPointer ") .text("CallByPointer ")
.append(alloc.intersperse(it, " ")) .append(alloc.intersperse(it, " "))
} }
LowLevel { LowLevel { op: lowlevel } => {
op: lowlevel, let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
opt_closure_layout: _,
} => { alloc
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
}
HigherOrderLowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
alloc alloc
@ -1092,8 +1096,13 @@ pub enum CallType<'a> {
}, },
LowLevel { LowLevel {
op: LowLevel, op: LowLevel,
},
HigherOrderLowLevel {
op: LowLevel,
/// the layout of the closure argument, if any /// the layout of the closure argument, if any
opt_closure_layout: Option<Layout<'a>>, closure_layout: Layout<'a>,
/// does the function need to own the closure data
function_owns_closure_data: bool,
}, },
} }
@ -2654,7 +2663,11 @@ macro_rules! match_on_closure_argument {
lambda_set, lambda_set,
$closure_data_symbol, $closure_data_symbol,
|top_level_function, closure_data, function_layout| self::Call { |top_level_function, closure_data, function_layout| self::Call {
call_type: CallType::LowLevel { op: $op, opt_closure_layout: Some(function_layout) }, call_type: CallType::HigherOrderLowLevel {
op: $op,
closure_layout: function_layout,
function_owns_closure_data: false
},
arguments: arena.alloc([$($x,)* top_level_function, closure_data]), arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
}, },
arena.alloc(top_level).full(), arena.alloc(top_level).full(),
@ -4345,10 +4358,7 @@ pub fn with_hole<'a>(
} }
_ => { _ => {
let call = self::Call { let call = self::Call {
call_type: CallType::LowLevel { call_type: CallType::LowLevel { op },
op,
opt_closure_layout: None,
},
arguments: arg_symbols, arguments: arg_symbols,
}; };
@ -4568,7 +4578,6 @@ pub fn from_can<'a>(
let call_type = CallType::LowLevel { let call_type = CallType::LowLevel {
op: LowLevel::ExpectTrue, op: LowLevel::ExpectTrue,
opt_closure_layout: None,
}; };
let arguments = env.arena.alloc([cond_symbol]); let arguments = env.arena.alloc([cond_symbol]);
let call = self::Call { let call = self::Call {
@ -5339,6 +5348,7 @@ fn substitute_in_call<'a>(
}), }),
CallType::Foreign { .. } => None, CallType::Foreign { .. } => None,
CallType::LowLevel { .. } => None, CallType::LowLevel { .. } => None,
CallType::HigherOrderLowLevel { .. } => None,
}; };
let mut did_change = false; let mut did_change = false;
@ -6052,6 +6062,12 @@ fn can_throw_exception(call: &Call) -> bool {
CallType::LowLevel { .. } => { CallType::LowLevel { .. } => {
// lowlevel operations themselves don't throw // lowlevel operations themselves don't throw
// TODO except for on allocation?
false
}
CallType::HigherOrderLowLevel { .. } => {
// TODO throwing is based on whether the HOF can throw
// or if there is (potentially) allocation in the lowlevel
false false
} }
} }