mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge branch 'trunk' into str-from-int
This commit is contained in:
commit
31fd70d0b1
4 changed files with 126 additions and 103 deletions
|
@ -706,9 +706,9 @@ pub fn strConcat(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: Ro
|
|||
|
||||
fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
||||
if (arg1.is_empty()) {
|
||||
return cloneNonemptyStr(T, result_in_place, arg2);
|
||||
return cloneStr(T, result_in_place, arg2);
|
||||
} else if (arg2.is_empty()) {
|
||||
return cloneNonemptyStr(T, result_in_place, arg1);
|
||||
return cloneStr(T, result_in_place, arg1);
|
||||
} else {
|
||||
const combined_length = arg1.len() + arg2.len();
|
||||
|
||||
|
@ -775,7 +775,7 @@ const InPlace = packed enum(u8) {
|
|||
Clone,
|
||||
};
|
||||
|
||||
fn cloneNonemptyStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr {
|
||||
fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr {
|
||||
if (str.is_small_str() or str.is_empty()) {
|
||||
// just return the bytes
|
||||
return str;
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::llvm::build::{
|
|||
};
|
||||
use crate::llvm::compare::build_eq;
|
||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type};
|
||||
use crate::llvm::refcounting::decrement_refcount_layout;
|
||||
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicTypeEnum, PointerType};
|
||||
|
@ -1318,6 +1318,85 @@ pub fn list_keep_if_help<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
/// List.map : List before, (before -> after) -> List after
|
||||
macro_rules! list_map_help {
|
||||
($env:expr, $layout_ids:expr, $inplace:expr, $parent:expr, $func:expr, $func_layout:expr, $list:expr, $list_layout:expr, $function_ptr:expr, $function_return_layout: expr, $closure_info:expr) => {{
|
||||
let layout_ids = $layout_ids;
|
||||
let inplace = $inplace;
|
||||
let parent = $parent;
|
||||
let func = $func;
|
||||
let func_layout = $func_layout;
|
||||
let list = $list;
|
||||
let list_layout = $list_layout;
|
||||
let function_ptr = $function_ptr;
|
||||
let function_return_layout = $function_return_layout;
|
||||
let closure_info : Option<(&Layout, BasicValueEnum)> = $closure_info;
|
||||
|
||||
|
||||
let non_empty_fn = |elem_layout: &Layout<'a>,
|
||||
len: IntValue<'ctx>,
|
||||
list_wrapper: StructValue<'ctx>| {
|
||||
let ctx = $env.context;
|
||||
let builder = $env.builder;
|
||||
|
||||
let ret_list_ptr = allocate_list($env, inplace, function_return_layout, len);
|
||||
|
||||
let elem_type = basic_type_from_layout($env.arena, ctx, elem_layout, $env.ptr_bytes);
|
||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||
|
||||
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
|
||||
|
||||
let list_loop = |index, before_elem| {
|
||||
increment_refcount_layout($env, parent, layout_ids, before_elem, elem_layout);
|
||||
|
||||
let arguments = match closure_info {
|
||||
Some((closure_data_layout, closure_data)) => {
|
||||
increment_refcount_layout( $env, parent, layout_ids, closure_data, closure_data_layout);
|
||||
|
||||
bumpalo::vec![in $env.arena; before_elem, closure_data]
|
||||
}
|
||||
None => bumpalo::vec![in $env.arena; before_elem],
|
||||
};
|
||||
|
||||
|
||||
let call_site_value = builder.build_call(function_ptr, &arguments, "map_func");
|
||||
|
||||
// set the calling convention explicitly for this call
|
||||
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
|
||||
|
||||
let after_elem = call_site_value
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||
|
||||
// The pointer to the element in the mapped-over list
|
||||
let after_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
|
||||
};
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(after_elem_ptr, after_elem);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(builder, ctx, parent, list_ptr, len, "#index", list_loop);
|
||||
|
||||
let result = store_list($env, ret_list_ptr, len);
|
||||
|
||||
// decrement the input list and function (if it's a closure)
|
||||
decrement_refcount_layout($env, parent, layout_ids, list, list_layout);
|
||||
decrement_refcount_layout($env, parent, layout_ids, func, func_layout);
|
||||
|
||||
if let Some((closure_data_layout, closure_data)) = closure_info {
|
||||
decrement_refcount_layout( $env, parent, layout_ids, closure_data, closure_data_layout);
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
if_list_is_not_empty($env, parent, non_empty_fn, list, list_layout, "List.map")
|
||||
}};
|
||||
}
|
||||
|
||||
/// List.map : List before, (before -> after) -> List after
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn list_map<'a, 'ctx, 'env>(
|
||||
|
@ -1332,56 +1411,24 @@ pub fn list_map<'a, 'ctx, 'env>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
match (func, func_layout) {
|
||||
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
|
||||
let non_empty_fn = |elem_layout: &Layout<'a>,
|
||||
len: IntValue<'ctx>,
|
||||
list_wrapper: StructValue<'ctx>| {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let ret_list_ptr = allocate_list(env, inplace, ret_elem_layout, len);
|
||||
|
||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||
|
||||
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
|
||||
|
||||
let list_loop = |index, before_elem| {
|
||||
let call_site_value =
|
||||
builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func");
|
||||
|
||||
// set the calling convention explicitly for this call
|
||||
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
|
||||
|
||||
let after_elem = call_site_value
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||
|
||||
// The pointer to the element in the mapped-over list
|
||||
let after_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
|
||||
};
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(after_elem_ptr, after_elem);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(builder, ctx, parent, list_ptr, len, "#index", list_loop);
|
||||
|
||||
let result = store_list(env, ret_list_ptr, len);
|
||||
|
||||
decrement_refcount_layout(env, parent, layout_ids, list, list_layout);
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.map")
|
||||
list_map_help!(
|
||||
env,
|
||||
layout_ids,
|
||||
inplace,
|
||||
parent,
|
||||
func,
|
||||
func_layout,
|
||||
list,
|
||||
list_layout,
|
||||
func_ptr,
|
||||
ret_elem_layout,
|
||||
None
|
||||
)
|
||||
}
|
||||
(BasicValueEnum::StructValue(ptr_and_data), Layout::Closure(_, _, ret_elem_layout)) => {
|
||||
let non_empty_fn = |elem_layout: &Layout<'a>,
|
||||
len: IntValue<'ctx>,
|
||||
list_wrapper: StructValue<'ctx>| {
|
||||
let ctx = env.context;
|
||||
(
|
||||
BasicValueEnum::StructValue(ptr_and_data),
|
||||
Layout::Closure(_, closure_layout, ret_elem_layout),
|
||||
) => {
|
||||
let builder = env.builder;
|
||||
|
||||
let func_ptr = builder
|
||||
|
@ -1393,43 +1440,21 @@ pub fn list_map<'a, 'ctx, 'env>(
|
|||
.build_extract_value(ptr_and_data, 1, "closure_data")
|
||||
.unwrap();
|
||||
|
||||
let ret_list_ptr = allocate_list(env, inplace, ret_elem_layout, len);
|
||||
let closure_data_layout = closure_layout.as_block_of_memory_layout();
|
||||
|
||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||
|
||||
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
|
||||
|
||||
let list_loop = |index, before_elem| {
|
||||
let call_site_value = builder.build_call(
|
||||
list_map_help!(
|
||||
env,
|
||||
layout_ids,
|
||||
inplace,
|
||||
parent,
|
||||
func,
|
||||
func_layout,
|
||||
list,
|
||||
list_layout,
|
||||
func_ptr,
|
||||
env.arena.alloc([before_elem, closure_data]),
|
||||
"map_func",
|
||||
);
|
||||
|
||||
// set the calling convention explicitly for this call
|
||||
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
|
||||
|
||||
let after_elem = call_site_value
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||
|
||||
// The pointer to the element in the mapped-over list
|
||||
let after_elem_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
|
||||
};
|
||||
|
||||
// Mutate the new array in-place to change the element.
|
||||
builder.build_store(after_elem_ptr, after_elem);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(builder, ctx, parent, list_ptr, len, "#index", list_loop);
|
||||
|
||||
store_list(env, ret_list_ptr, len)
|
||||
};
|
||||
|
||||
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.map")
|
||||
ret_elem_layout,
|
||||
Some((&closure_data_layout, closure_data))
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!(
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::llvm::build::{
|
|||
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
|
||||
FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64,
|
||||
};
|
||||
use crate::llvm::build_list::list_len;
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::{basic_type_from_layout, block_of_memory, ptr_int};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::context::Context;
|
||||
|
@ -367,7 +367,6 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
|||
List(memory_mode, element_layout) => {
|
||||
let wrapper_struct = value.into_struct_value();
|
||||
if element_layout.contains_refcounted() {
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let ptr_type =
|
||||
|
@ -451,7 +450,6 @@ fn increment_refcount_builtin<'a, 'ctx, 'env>(
|
|||
List(memory_mode, element_layout) => {
|
||||
let wrapper_struct = value.into_struct_value();
|
||||
if element_layout.contains_refcounted() {
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let ptr_type =
|
||||
|
|
|
@ -460,7 +460,7 @@ impl<'a> Layout<'a> {
|
|||
|
||||
pub fn is_refcounted(&self) -> bool {
|
||||
match self {
|
||||
Layout::Builtin(Builtin::List(_, _)) => true,
|
||||
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => true,
|
||||
Layout::Builtin(Builtin::Str) => true,
|
||||
Layout::RecursiveUnion(_) => true,
|
||||
Layout::RecursivePointer => true,
|
||||
|
@ -477,12 +477,12 @@ impl<'a> Layout<'a> {
|
|||
match self {
|
||||
Builtin(builtin) => builtin.is_refcounted(),
|
||||
PhantomEmptyStruct => false,
|
||||
Struct(fields) => fields.iter().any(|f| f.is_refcounted()),
|
||||
Struct(fields) => fields.iter().any(|f| f.contains_refcounted()),
|
||||
Union(fields) => fields
|
||||
.iter()
|
||||
.map(|ls| ls.iter())
|
||||
.flatten()
|
||||
.any(|f| f.is_refcounted()),
|
||||
.any(|f| f.contains_refcounted()),
|
||||
RecursiveUnion(_) => true,
|
||||
Closure(_, closure_layout, _) => closure_layout.contains_refcounted(),
|
||||
FunctionPointer(_, _) | RecursivePointer | Pointer(_) => false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue