mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Merge pull request #3369 from rtfeldman/pure-roc-list-walk
List.walk and friends in pure Roc
This commit is contained in:
commit
2a82d24847
39 changed files with 344 additions and 1630 deletions
|
@ -8,10 +8,10 @@ use crate::llvm::build_dict::{
|
|||
};
|
||||
use crate::llvm::build_hash::generic_hash;
|
||||
use crate::llvm::build_list::{
|
||||
self, allocate_list, empty_polymorphic_list, list_all, list_any, list_append, list_concat,
|
||||
list_drop_at, list_find_unsafe, list_get_unsafe, list_len, list_map, list_map2, list_map3,
|
||||
list_map4, list_map_with_index, list_prepend, list_replace_unsafe, list_sort_with,
|
||||
list_sublist, list_swap, list_symbol_to_c_abi, list_to_c_abi, list_with_capacity,
|
||||
self, allocate_list, empty_polymorphic_list, list_append, list_concat, list_drop_at,
|
||||
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_map_with_index,
|
||||
list_prepend, list_replace_unsafe, list_sort_with, list_sublist, list_swap,
|
||||
list_symbol_to_c_abi, list_to_c_abi, list_with_capacity,
|
||||
};
|
||||
use crate::llvm::build_str::{
|
||||
str_from_float, str_from_int, str_from_utf8, str_from_utf8_range, str_split,
|
||||
|
@ -4881,44 +4881,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
}};
|
||||
}
|
||||
|
||||
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::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,
|
||||
result_layout,
|
||||
);
|
||||
|
||||
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
|
||||
|
@ -5119,15 +5081,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
@ -5167,78 +5120,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListAny { xs } => {
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match list_layout {
|
||||
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,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
);
|
||||
|
||||
list_any(env, roc_function_call, list, element_layout)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListAll { xs } => {
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match list_layout {
|
||||
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,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
);
|
||||
|
||||
list_all(env, roc_function_call, list, element_layout)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListFindUnsafe { xs } => {
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
||||
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match list_layout {
|
||||
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,
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
);
|
||||
|
||||
list_find_unsafe(env, layout_ids, roc_function_call, 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);
|
||||
|
@ -6064,8 +5945,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
set
|
||||
}
|
||||
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListWalk | ListWalkUntil
|
||||
| ListWalkBackwards | ListSortWith | ListFindUnsafe | DictWalk => {
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListSortWith | DictWalk => {
|
||||
unreachable!("these are higher order, and are handled elsewhere")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use crate::llvm::bitcode::{
|
||||
build_dec_wrapper, build_has_tag_id, build_inc_n_wrapper, build_inc_wrapper, call_bitcode_fn,
|
||||
call_list_bitcode_fn, call_void_bitcode_fn,
|
||||
};
|
||||
use crate::llvm::bitcode::{build_dec_wrapper, call_list_bitcode_fn};
|
||||
use crate::llvm::build::{
|
||||
allocate_with_refcount_help, cast_basic_basic, Env, RocFunctionCall, Scope,
|
||||
};
|
||||
|
@ -122,29 +119,6 @@ pub fn list_with_capacity<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
/// List.repeat : elem, Nat -> List elem
|
||||
pub fn list_repeat<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
list_len: IntValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_n_wrapper(env, layout_ids, element_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_len.into(),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_REPEAT,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -370,133 +344,6 @@ pub fn list_len<'ctx>(
|
|||
.into_int_value()
|
||||
}
|
||||
|
||||
pub enum ListWalk {
|
||||
Walk,
|
||||
WalkBackwards,
|
||||
WalkUntil,
|
||||
WalkBackwardsUntil,
|
||||
}
|
||||
|
||||
pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
function_call_return_layout: &Layout<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
default: BasicValueEnum<'ctx>,
|
||||
default_layout: &Layout<'a>,
|
||||
variant: ListWalk,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_function = match variant {
|
||||
ListWalk::Walk => bitcode::LIST_WALK,
|
||||
ListWalk::WalkBackwards => bitcode::LIST_WALK_BACKWARDS,
|
||||
ListWalk::WalkUntil => bitcode::LIST_WALK_UNTIL,
|
||||
ListWalk::WalkBackwardsUntil => todo!(),
|
||||
};
|
||||
|
||||
let default_ptr = if default_layout.is_passed_by_reference(env.target_info) {
|
||||
debug_assert!(default.is_pointer_value());
|
||||
default.into_pointer_value()
|
||||
} else {
|
||||
let default_ptr = builder.build_alloca(default.get_type(), "default_ptr");
|
||||
env.builder.build_store(default_ptr, default);
|
||||
default_ptr
|
||||
};
|
||||
|
||||
let result_ptr = {
|
||||
let basic_type = basic_type_from_layout(env, default_layout);
|
||||
env.builder.build_alloca(basic_type, "result")
|
||||
};
|
||||
|
||||
match variant {
|
||||
ListWalk::Walk | ListWalk::WalkBackwards => {
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_to_c_abi(env, list).into(),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
roc_function_call.data_is_owned.into(),
|
||||
pass_as_opaque(env, default_ptr),
|
||||
env.alignment_intvalue(element_layout),
|
||||
layout_width(env, element_layout),
|
||||
layout_width(env, default_layout),
|
||||
pass_as_opaque(env, result_ptr),
|
||||
],
|
||||
zig_function,
|
||||
);
|
||||
}
|
||||
ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => {
|
||||
let function = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
|
||||
let has_tag_id = match function_call_return_layout {
|
||||
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_to_c_abi(env, list).into(),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
roc_function_call.data_is_owned.into(),
|
||||
pass_as_opaque(env, default_ptr),
|
||||
env.alignment_intvalue(element_layout),
|
||||
layout_width(env, element_layout),
|
||||
layout_width(env, function_call_return_layout),
|
||||
layout_width(env, default_layout),
|
||||
has_tag_id_helper(env, has_tag_id).into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_as_opaque(env, result_ptr),
|
||||
],
|
||||
zig_function,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if default_layout.is_passed_by_reference(env.target_info) {
|
||||
result_ptr.into()
|
||||
} else {
|
||||
env.builder.build_load(result_ptr, "load_result")
|
||||
}
|
||||
}
|
||||
|
||||
fn has_tag_id_helper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
has_tag_id: FunctionValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let u8_t = env.context.i8_type();
|
||||
let u16_t = env.context.i16_type();
|
||||
|
||||
let u8_ptr_t = u8_t.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let struct_t = env
|
||||
.context
|
||||
.struct_type(&[u8_t.into(), env.ptr_int().into()], false);
|
||||
|
||||
let has_tag_id_type = struct_t
|
||||
.fn_type(&[u16_t.into(), u8_ptr_t.into()], false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
|
||||
env.builder.build_pointer_cast(
|
||||
has_tag_id.as_global_value().as_pointer_value(),
|
||||
has_tag_id_type,
|
||||
"has_tag_id_cast",
|
||||
)
|
||||
}
|
||||
|
||||
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
pub fn list_sort_with<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -704,144 +551,6 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
/// List.any : List elem, \(elem -> Bool) -> Bool
|
||||
pub fn list_any<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_to_c_abi(env, list).into(),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
roc_function_call.data_is_owned.into(),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_ANY,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.all : List elem, \(elem -> Bool) -> Bool
|
||||
pub fn list_all<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_to_c_abi(env, list).into(),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
roc_function_call.data_is_owned.into(),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_ALL,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.findUnsafe : List elem, (elem -> Bool) -> { value: elem, found: bool }
|
||||
pub fn list_find_unsafe<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||
|
||||
// { value: *const u8, found: bool }
|
||||
let result = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_to_c_abi(env, list).into(),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
roc_function_call.data_is_owned.into(),
|
||||
layout_width(env, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_FIND_UNSAFE,
|
||||
)
|
||||
.into_struct_value();
|
||||
|
||||
// We promised the caller we'd give them back a struct containing the element
|
||||
// loaded on the stack, so we do that now. The element can't be loaded directly
|
||||
// in the Zig definition called above, because we don't know the size of the
|
||||
// element until user compile time, which is later than the compile time of bitcode defs.
|
||||
|
||||
let value_u8_ptr_int = env
|
||||
.builder
|
||||
.build_extract_value(result, 0, "get_value_ptr_int")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let found_u8 = env
|
||||
.builder
|
||||
.build_extract_value(result, 1, "get_found")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let found = env
|
||||
.builder
|
||||
.build_int_cast(found_u8, env.context.bool_type(), "found_as_bool");
|
||||
|
||||
let start_block = env.builder.get_insert_block().unwrap();
|
||||
let parent = start_block.get_parent().unwrap();
|
||||
|
||||
let if_not_null = env.context.append_basic_block(parent, "if_not_null");
|
||||
let done_block = env.context.append_basic_block(parent, "done");
|
||||
|
||||
let value_bt = basic_type_from_layout(env, element_layout);
|
||||
let default = value_bt.const_zero();
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(found, if_not_null, done_block);
|
||||
|
||||
env.builder.position_at_end(if_not_null);
|
||||
|
||||
let value_ptr = env.builder.build_int_to_ptr(
|
||||
value_u8_ptr_int,
|
||||
value_bt.ptr_type(AddressSpace::Generic),
|
||||
"get_value_ptr",
|
||||
);
|
||||
|
||||
let loaded = env.builder.build_load(value_ptr, "load_value");
|
||||
env.builder.build_unconditional_branch(done_block);
|
||||
|
||||
env.builder.position_at_end(done_block);
|
||||
let result_phi = env.builder.build_phi(value_bt, "result");
|
||||
|
||||
result_phi.add_incoming(&[(&default, start_block), (&loaded, if_not_null)]);
|
||||
|
||||
let value = result_phi.as_basic_value();
|
||||
|
||||
let result = env
|
||||
.context
|
||||
.struct_type(&[value_bt, env.context.bool_type().into()], false)
|
||||
.const_zero();
|
||||
|
||||
let result = env
|
||||
.builder
|
||||
.build_insert_value(result, value, 0, "insert_value")
|
||||
.unwrap();
|
||||
|
||||
env.builder
|
||||
.build_insert_value(result, found, 1, "insert_found")
|
||||
.unwrap()
|
||||
.into_struct_value()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn decrementing_elem_loop<'ctx, LoopFn>(
|
||||
builder: &Builder<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue