Merge branch 'trunk' into str-from-int

This commit is contained in:
Richard Feldman 2020-12-03 22:26:23 -05:00 committed by GitHub
commit 31fd70d0b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 103 deletions

View file

@ -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 { fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
if (arg1.is_empty()) { if (arg1.is_empty()) {
return cloneNonemptyStr(T, result_in_place, arg2); return cloneStr(T, result_in_place, arg2);
} else if (arg2.is_empty()) { } else if (arg2.is_empty()) {
return cloneNonemptyStr(T, result_in_place, arg1); return cloneStr(T, result_in_place, arg1);
} else { } else {
const combined_length = arg1.len() + arg2.len(); const combined_length = arg1.len() + arg2.len();
@ -775,7 +775,7 @@ const InPlace = packed enum(u8) {
Clone, 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()) { if (str.is_small_str() or str.is_empty()) {
// just return the bytes // just return the bytes
return str; return str;

View file

@ -3,7 +3,7 @@ use crate::llvm::build::{
}; };
use crate::llvm::compare::build_eq; use crate::llvm::compare::build_eq;
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type}; 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::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicTypeEnum, PointerType}; 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 /// List.map : List before, (before -> after) -> List after
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn list_map<'a, 'ctx, 'env>( pub fn list_map<'a, 'ctx, 'env>(
@ -1332,104 +1411,50 @@ pub fn list_map<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match (func, func_layout) { match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => { (BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
let non_empty_fn = |elem_layout: &Layout<'a>, list_map_help!(
len: IntValue<'ctx>, env,
list_wrapper: StructValue<'ctx>| { layout_ids,
let ctx = env.context; inplace,
let builder = env.builder; parent,
func,
let ret_list_ptr = allocate_list(env, inplace, ret_elem_layout, len); func_layout,
list,
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes); list_layout,
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); func_ptr,
ret_elem_layout,
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type); None
)
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")
} }
(BasicValueEnum::StructValue(ptr_and_data), Layout::Closure(_, _, ret_elem_layout)) => { (
let non_empty_fn = |elem_layout: &Layout<'a>, BasicValueEnum::StructValue(ptr_and_data),
len: IntValue<'ctx>, Layout::Closure(_, closure_layout, ret_elem_layout),
list_wrapper: StructValue<'ctx>| { ) => {
let ctx = env.context; let builder = env.builder;
let builder = env.builder;
let func_ptr = builder let func_ptr = builder
.build_extract_value(ptr_and_data, 0, "function_ptr") .build_extract_value(ptr_and_data, 0, "function_ptr")
.unwrap() .unwrap()
.into_pointer_value(); .into_pointer_value();
let closure_data = builder let closure_data = builder
.build_extract_value(ptr_and_data, 1, "closure_data") .build_extract_value(ptr_and_data, 1, "closure_data")
.unwrap(); .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); list_map_help!(
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); env,
layout_ids,
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type); inplace,
parent,
let list_loop = |index, before_elem| { func,
let call_site_value = builder.build_call( func_layout,
func_ptr, list,
env.arena.alloc([before_elem, closure_data]), list_layout,
"map_func", func_ptr,
); ret_elem_layout,
Some((&closure_data_layout, closure_data))
// 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")
} }
_ => { _ => {
unreachable!( unreachable!(

View file

@ -2,7 +2,7 @@ use crate::llvm::build::{
cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope, cast_basic_basic, cast_struct_struct, create_entry_block_alloca, set_name, Env, Scope,
FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, 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 crate::llvm::convert::{basic_type_from_layout, block_of_memory, ptr_int};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::context::Context; use inkwell::context::Context;
@ -367,7 +367,6 @@ fn decrement_refcount_builtin<'a, 'ctx, 'env>(
List(memory_mode, element_layout) => { List(memory_mode, element_layout) => {
let wrapper_struct = value.into_struct_value(); let wrapper_struct = value.into_struct_value();
if element_layout.contains_refcounted() { if element_layout.contains_refcounted() {
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
use inkwell::types::BasicType; use inkwell::types::BasicType;
let ptr_type = let ptr_type =
@ -451,7 +450,6 @@ fn increment_refcount_builtin<'a, 'ctx, 'env>(
List(memory_mode, element_layout) => { List(memory_mode, element_layout) => {
let wrapper_struct = value.into_struct_value(); let wrapper_struct = value.into_struct_value();
if element_layout.contains_refcounted() { if element_layout.contains_refcounted() {
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
use inkwell::types::BasicType; use inkwell::types::BasicType;
let ptr_type = let ptr_type =

View file

@ -460,7 +460,7 @@ impl<'a> Layout<'a> {
pub fn is_refcounted(&self) -> bool { pub fn is_refcounted(&self) -> bool {
match self { match self {
Layout::Builtin(Builtin::List(_, _)) => true, Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => true,
Layout::Builtin(Builtin::Str) => true, Layout::Builtin(Builtin::Str) => true,
Layout::RecursiveUnion(_) => true, Layout::RecursiveUnion(_) => true,
Layout::RecursivePointer => true, Layout::RecursivePointer => true,
@ -477,12 +477,12 @@ impl<'a> Layout<'a> {
match self { match self {
Builtin(builtin) => builtin.is_refcounted(), Builtin(builtin) => builtin.is_refcounted(),
PhantomEmptyStruct => false, PhantomEmptyStruct => false,
Struct(fields) => fields.iter().any(|f| f.is_refcounted()), Struct(fields) => fields.iter().any(|f| f.contains_refcounted()),
Union(fields) => fields Union(fields) => fields
.iter() .iter()
.map(|ls| ls.iter()) .map(|ls| ls.iter())
.flatten() .flatten()
.any(|f| f.is_refcounted()), .any(|f| f.contains_refcounted()),
RecursiveUnion(_) => true, RecursiveUnion(_) => true,
Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), Closure(_, closure_layout, _) => closure_layout.contains_refcounted(),
FunctionPointer(_, _) | RecursivePointer | Pointer(_) => false, FunctionPointer(_, _) | RecursivePointer | Pointer(_) => false,