mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
add HigherOrderLowLevel
This commit is contained in:
parent
e675bac893
commit
2b72f9e733
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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]),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user