mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Resolving conflicts with remote trunk
This commit is contained in:
commit
385d0fda9f
209 changed files with 29256 additions and 8806 deletions
|
@ -1,51 +0,0 @@
|
|||
use roc_collections::all::{default_hasher, MutMap};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::layout::Layout;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct LayoutId(u32);
|
||||
|
||||
impl LayoutId {
|
||||
// Returns something like "foo#1" when given a symbol that interns to "foo"
|
||||
// and a LayoutId of 1.
|
||||
pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String {
|
||||
format!("{}_{}", symbol.ident_string(interns), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
struct IdsByLayout<'a> {
|
||||
by_id: MutMap<Layout<'a>, u32>,
|
||||
next_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LayoutIds<'a> {
|
||||
by_symbol: MutMap<Symbol, IdsByLayout<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> LayoutIds<'a> {
|
||||
/// Returns a LayoutId which is unique for the given symbol and layout.
|
||||
/// If given the same symbol and same layout, returns the same LayoutId.
|
||||
pub fn get(&mut self, symbol: Symbol, layout: &Layout<'a>) -> LayoutId {
|
||||
// Note: this function does some weird stuff to satisfy the borrow checker.
|
||||
// There's probably a nicer way to write it that still works.
|
||||
let ids = self.by_symbol.entry(symbol).or_insert_with(|| IdsByLayout {
|
||||
by_id: HashMap::with_capacity_and_hasher(1, default_hasher()),
|
||||
next_id: 1,
|
||||
});
|
||||
|
||||
// Get the id associated with this layout, or default to next_id.
|
||||
let answer = ids.by_id.get(layout).copied().unwrap_or(ids.next_id);
|
||||
|
||||
// If we had to default to next_id, it must not have been found;
|
||||
// store the ID we're going to return and increment next_id.
|
||||
if answer == ids.next_id {
|
||||
ids.by_id.insert(layout.clone(), ids.next_id);
|
||||
|
||||
ids.next_id += 1;
|
||||
}
|
||||
|
||||
LayoutId(answer)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod layout_id;
|
||||
pub mod llvm;
|
||||
|
||||
pub mod run_roc;
|
||||
|
|
21
compiler/gen/src/llvm/bitcode.rs
Normal file
21
compiler/gen/src/llvm/bitcode.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use inkwell::types::BasicTypeEnum;
|
||||
use roc_module::low_level::LowLevel;
|
||||
|
||||
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||
op: LowLevel,
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let fn_val = env
|
||||
.module
|
||||
.get_function(fn_name)
|
||||
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
|
||||
let call = env.builder.build_call(fn_val, args, "call_builtin");
|
||||
|
||||
call.set_call_convention(fn_val.get_call_conventions());
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call for low-level op {:?}", op))
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,8 @@
|
|||
use crate::llvm::build::{Env, InPlace};
|
||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
||||
use crate::llvm::build::{
|
||||
allocate_with_refcount_help, build_num_binop, cast_basic_basic, Env, InPlace,
|
||||
};
|
||||
use crate::llvm::compare::build_eq;
|
||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicTypeEnum, PointerType};
|
||||
|
@ -184,7 +187,9 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
|||
// one we just malloc'd.
|
||||
//
|
||||
// TODO how do we decide when to do the small memcpy vs the normal one?
|
||||
builder.build_memcpy(index_1_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size);
|
||||
builder
|
||||
.build_memcpy(index_1_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||
}
|
||||
|
@ -625,7 +630,9 @@ pub fn list_append<'a, 'ctx, 'env>(
|
|||
// one we just malloc'd.
|
||||
//
|
||||
// TODO how do we decide when to do the small memcpy vs the normal one?
|
||||
builder.build_memcpy(clone_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size);
|
||||
builder
|
||||
.build_memcpy(clone_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||
}
|
||||
|
@ -727,6 +734,81 @@ pub fn list_len<'ctx>(
|
|||
.into_int_value()
|
||||
}
|
||||
|
||||
/// List.sum : List (Num a) -> Num a
|
||||
pub fn list_sum<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
default_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let list_wrapper = list.into_struct_value();
|
||||
let len = list_len(env.builder, list_wrapper);
|
||||
|
||||
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
|
||||
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
|
||||
|
||||
let default: BasicValueEnum = match accum_type {
|
||||
BasicTypeEnum::IntType(int_type) => int_type.const_zero().into(),
|
||||
BasicTypeEnum::FloatType(float_type) => float_type.const_zero().into(),
|
||||
_ => unreachable!(""),
|
||||
};
|
||||
|
||||
builder.build_store(accum_alloca, default);
|
||||
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
let cont_block = ctx.append_basic_block(parent, "branchcont");
|
||||
|
||||
let condition = builder.build_int_compare(
|
||||
IntPredicate::UGT,
|
||||
len,
|
||||
ctx.i64_type().const_zero(),
|
||||
"list_non_empty",
|
||||
);
|
||||
|
||||
builder.build_conditional_branch(condition, then_block, cont_block);
|
||||
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
let elem_ptr_type = get_ptr_type(&accum_type, AddressSpace::Generic);
|
||||
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
|
||||
|
||||
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
|
||||
// load current accumulator
|
||||
let current = builder.build_load(accum_alloca, "retrieve_accum");
|
||||
|
||||
let new_current = build_num_binop(
|
||||
env,
|
||||
parent,
|
||||
current,
|
||||
default_layout,
|
||||
elem,
|
||||
default_layout,
|
||||
roc_module::low_level::LowLevel::NumAdd,
|
||||
);
|
||||
|
||||
builder.build_store(accum_alloca, new_current);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(
|
||||
builder,
|
||||
ctx,
|
||||
parent,
|
||||
list_ptr,
|
||||
len,
|
||||
"#index",
|
||||
walk_right_loop,
|
||||
);
|
||||
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
|
||||
builder.position_at_end(cont_block);
|
||||
|
||||
builder.build_load(accum_alloca, "load_final_acum")
|
||||
}
|
||||
|
||||
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn list_walk_right<'a, 'ctx, 'env>(
|
||||
|
@ -788,8 +870,7 @@ pub fn list_walk_right<'a, 'ctx, 'env>(
|
|||
let new_current = call_site_value
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
.into_int_value();
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||
|
||||
builder.build_store(accum_alloca, new_current);
|
||||
};
|
||||
|
@ -820,6 +901,116 @@ pub fn list_walk_right<'a, 'ctx, 'env>(
|
|||
builder.build_load(accum_alloca, "load_final_acum")
|
||||
}
|
||||
|
||||
/// List.contains : List elem, elem -> Bool
|
||||
pub fn list_contains<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
elem: BasicValueEnum<'ctx>,
|
||||
elem_layout: &Layout<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
list_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let builder = env.builder;
|
||||
|
||||
let wrapper_struct = list.into_struct_value();
|
||||
let list_elem_layout = match &list_layout {
|
||||
// this pointer will never actually be dereferenced
|
||||
Layout::Builtin(Builtin::EmptyList) => &Layout::Builtin(Builtin::Int64),
|
||||
Layout::Builtin(Builtin::List(_, element_layout)) => element_layout,
|
||||
_ => unreachable!("Invalid layout {:?} in List.contains", list_layout),
|
||||
};
|
||||
|
||||
let list_elem_type =
|
||||
basic_type_from_layout(env.arena, env.context, list_elem_layout, env.ptr_bytes);
|
||||
|
||||
let list_ptr = load_list_ptr(
|
||||
builder,
|
||||
wrapper_struct,
|
||||
list_elem_type.ptr_type(AddressSpace::Generic),
|
||||
);
|
||||
|
||||
let length = list_len(builder, list.into_struct_value());
|
||||
|
||||
list_contains_help(
|
||||
env,
|
||||
parent,
|
||||
length,
|
||||
list_ptr,
|
||||
list_elem_layout,
|
||||
elem,
|
||||
elem_layout,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_contains_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
length: IntValue<'ctx>,
|
||||
source_ptr: PointerValue<'ctx>,
|
||||
list_elem_layout: &Layout<'a>,
|
||||
elem: BasicValueEnum<'ctx>,
|
||||
elem_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let bool_alloca = builder.build_alloca(ctx.bool_type(), "bool_alloca");
|
||||
let index_alloca = builder.build_alloca(ctx.i64_type(), "index_alloca");
|
||||
let next_free_index_alloca = builder.build_alloca(ctx.i64_type(), "next_free_index_alloca");
|
||||
|
||||
builder.build_store(bool_alloca, ctx.bool_type().const_zero());
|
||||
builder.build_store(index_alloca, ctx.i64_type().const_zero());
|
||||
builder.build_store(next_free_index_alloca, ctx.i64_type().const_zero());
|
||||
|
||||
let condition_bb = ctx.append_basic_block(parent, "condition");
|
||||
builder.build_unconditional_branch(condition_bb);
|
||||
builder.position_at_end(condition_bb);
|
||||
|
||||
let index = builder.build_load(index_alloca, "index").into_int_value();
|
||||
|
||||
let condition = builder.build_int_compare(IntPredicate::SGT, length, index, "loopcond");
|
||||
|
||||
let body_bb = ctx.append_basic_block(parent, "body");
|
||||
let cont_bb = ctx.append_basic_block(parent, "cont");
|
||||
builder.build_conditional_branch(condition, body_bb, cont_bb);
|
||||
|
||||
// loop body
|
||||
builder.position_at_end(body_bb);
|
||||
|
||||
let current_elem_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[index], "elem_ptr") };
|
||||
|
||||
let current_elem = builder.build_load(current_elem_ptr, "load_elem");
|
||||
|
||||
let has_found = build_eq(env, current_elem, elem, list_elem_layout, elem_layout);
|
||||
|
||||
builder.build_store(bool_alloca, has_found.into_int_value());
|
||||
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
|
||||
let next_free_index = builder
|
||||
.build_load(next_free_index_alloca, "load_next_free")
|
||||
.into_int_value();
|
||||
|
||||
builder.build_store(
|
||||
next_free_index_alloca,
|
||||
builder.build_int_add(next_free_index, one, "incremented_next_free_index"),
|
||||
);
|
||||
|
||||
builder.build_store(
|
||||
index_alloca,
|
||||
builder.build_int_add(index, one, "incremented_index"),
|
||||
);
|
||||
|
||||
builder.build_conditional_branch(has_found.into_int_value(), cont_bb, condition_bb);
|
||||
|
||||
// continuation
|
||||
builder.position_at_end(cont_bb);
|
||||
|
||||
builder.build_load(bool_alloca, "answer")
|
||||
}
|
||||
|
||||
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
||||
pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -850,7 +1041,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
|||
elem_layout.clone(),
|
||||
),
|
||||
|
||||
_ => unreachable!("Invalid layout {:?} in List.reverse", list_layout),
|
||||
_ => unreachable!("Invalid layout {:?} in List.keepIf", list_layout),
|
||||
};
|
||||
|
||||
let list_type = basic_type_from_layout(env.arena, env.context, &list_layout, env.ptr_bytes);
|
||||
|
@ -1399,11 +1590,6 @@ pub fn incrementing_index_loop<'ctx, LoopFn>(
|
|||
parent: FunctionValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
index_name: &str,
|
||||
// allocating memory for an index is costly, so sometimes
|
||||
// we want to reuse an index if multiple loops happen in a
|
||||
// series, such as the case in List.concat. A memory
|
||||
// allocation cab be passed in to be used, and the memory
|
||||
// allocation that _is_ used is the return value.
|
||||
mut loop_fn: LoopFn,
|
||||
) -> PointerValue<'ctx>
|
||||
where
|
||||
|
@ -1575,12 +1761,7 @@ pub fn load_list<'ctx>(
|
|||
wrapper_struct: StructValue<'ctx>,
|
||||
ptr_type: PointerType<'ctx>,
|
||||
) -> (IntValue<'ctx>, PointerValue<'ctx>) {
|
||||
let ptr_as_int = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let ptr = builder.build_int_to_ptr(ptr_as_int, ptr_type, "list_cast_ptr");
|
||||
let ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
||||
|
||||
let length = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "list_len")
|
||||
|
@ -1595,12 +1776,14 @@ pub fn load_list_ptr<'ctx>(
|
|||
wrapper_struct: StructValue<'ctx>,
|
||||
ptr_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let ptr_as_int = builder
|
||||
// a `*mut u8` pointer
|
||||
let generic_ptr = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
.into_pointer_value();
|
||||
|
||||
builder.build_int_to_ptr(ptr_as_int, ptr_type, "list_cast_ptr")
|
||||
// cast to the expected pointer type
|
||||
cast_basic_basic(builder, generic_ptr.into(), ptr_type.into()).into_pointer_value()
|
||||
}
|
||||
|
||||
pub fn clone_nonempty_list<'a, 'ctx, 'env>(
|
||||
|
@ -1625,9 +1808,6 @@ pub fn clone_nonempty_list<'a, 'ctx, 'env>(
|
|||
// Allocate space for the new array that we'll copy into.
|
||||
let clone_ptr = allocate_list(env, inplace, elem_layout, list_len);
|
||||
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
|
||||
// Either memcpy or deep clone the array elements
|
||||
|
@ -1636,12 +1816,17 @@ pub fn clone_nonempty_list<'a, 'ctx, 'env>(
|
|||
// one we just malloc'd.
|
||||
//
|
||||
// TODO how do we decide when to do the small memcpy vs the normal one?
|
||||
builder.build_memcpy(clone_ptr, ptr_bytes, elems_ptr, ptr_bytes, size);
|
||||
builder
|
||||
.build_memcpy(clone_ptr, ptr_bytes, elems_ptr, ptr_bytes, size)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
||||
}
|
||||
|
||||
// Create a fresh wrapper struct for the newly populated array
|
||||
let u8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let generic_ptr = cast_basic_basic(builder, clone_ptr.into(), u8_ptr_type.into());
|
||||
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
let mut struct_val;
|
||||
|
||||
|
@ -1649,9 +1834,9 @@ pub fn clone_nonempty_list<'a, 'ctx, 'env>(
|
|||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
generic_ptr,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
"insert_ptr_clone_nonempty_list",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -1692,7 +1877,9 @@ pub fn clone_list<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
// copy old elements in
|
||||
builder.build_memcpy(new_ptr, ptr_bytes, old_ptr, ptr_bytes, bytes);
|
||||
builder
|
||||
.build_memcpy(new_ptr, ptr_bytes, old_ptr, ptr_bytes, bytes)
|
||||
.unwrap();
|
||||
|
||||
new_ptr
|
||||
}
|
||||
|
@ -1706,53 +1893,13 @@ pub fn allocate_list<'a, 'ctx, 'env>(
|
|||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||
|
||||
let len_type = env.ptr_int();
|
||||
// bytes per element
|
||||
let bytes_len = len_type.const_int(elem_bytes, false);
|
||||
let offset = (env.ptr_bytes as u64).max(elem_bytes);
|
||||
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||
let bytes_per_element = len_type.const_int(elem_bytes, false);
|
||||
|
||||
let ptr = {
|
||||
let len = builder.build_int_mul(bytes_len, length, "data_length");
|
||||
let len =
|
||||
builder.build_int_add(len, len_type.const_int(offset, false), "add_refcount_space");
|
||||
let number_of_data_bytes = builder.build_int_mul(bytes_per_element, length, "data_length");
|
||||
|
||||
env.builder
|
||||
.build_array_malloc(ctx.i8_type(), len, "create_list_ptr")
|
||||
.unwrap()
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
};
|
||||
|
||||
// We must return a pointer to the first element:
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
|
||||
let incremented = builder.build_int_add(
|
||||
ptr_as_int,
|
||||
ctx.i64_type().const_int(offset, false),
|
||||
"increment_list_ptr",
|
||||
);
|
||||
|
||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||
let list_element_ptr = builder.build_int_to_ptr(incremented, ptr_type, "list_cast_ptr");
|
||||
|
||||
// subtract ptr_size, to access the refcount
|
||||
let refcount_ptr = builder.build_int_sub(
|
||||
incremented,
|
||||
ctx.i64_type().const_int(env.ptr_bytes as u64, false),
|
||||
"refcount_ptr",
|
||||
);
|
||||
|
||||
let refcount_ptr = builder.build_int_to_ptr(
|
||||
refcount_ptr,
|
||||
int_type.ptr_type(AddressSpace::Generic),
|
||||
"make ptr",
|
||||
);
|
||||
|
||||
let ref_count_one = match inplace {
|
||||
let rc1 = match inplace {
|
||||
InPlace::InPlace => length,
|
||||
InPlace::Clone => {
|
||||
// the refcount of a new list is initially 1
|
||||
|
@ -1761,33 +1908,33 @@ pub fn allocate_list<'a, 'ctx, 'env>(
|
|||
}
|
||||
};
|
||||
|
||||
builder.build_store(refcount_ptr, ref_count_one);
|
||||
|
||||
list_element_ptr
|
||||
allocate_with_refcount_help(env, elem_layout, number_of_data_bytes, rc1)
|
||||
}
|
||||
|
||||
pub fn store_list<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
list_ptr: PointerValue<'ctx>,
|
||||
pointer_to_first_element: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
|
||||
let u8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let generic_ptr =
|
||||
cast_basic_basic(builder, pointer_to_first_element.into(), u8_ptr_type.into());
|
||||
|
||||
let mut struct_val;
|
||||
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
generic_ptr,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
"insert_ptr_store_list",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,18 +1,96 @@
|
|||
use crate::llvm::build::{ptr_from_symbol, Env, InPlace, Scope};
|
||||
use crate::llvm::build::{
|
||||
call_bitcode_fn, call_void_bitcode_fn, ptr_from_symbol, Env, InPlace, Scope,
|
||||
};
|
||||
use crate::llvm::build_list::{
|
||||
allocate_list, build_basic_phi2, empty_list, incrementing_elem_loop, incrementing_index_loop,
|
||||
load_list_ptr, store_list,
|
||||
};
|
||||
use crate::llvm::convert::{collection, ptr_int};
|
||||
use crate::llvm::convert::collection;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::types::BasicTypeEnum;
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
|
||||
pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
|
||||
|
||||
/// Str.split : Str, Str -> List Str
|
||||
pub fn str_split<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
inplace: InPlace,
|
||||
str_symbol: Symbol,
|
||||
delimiter_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let str_ptr = ptr_from_symbol(scope, str_symbol);
|
||||
let delimiter_ptr = ptr_from_symbol(scope, delimiter_symbol);
|
||||
|
||||
let str_wrapper_type = BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes));
|
||||
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*str_ptr,
|
||||
str_wrapper_type,
|
||||
|str_bytes_ptr, str_len, _str_smallness| {
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*delimiter_ptr,
|
||||
str_wrapper_type,
|
||||
|delimiter_bytes_ptr, delimiter_len, _delimiter_smallness| {
|
||||
let segment_count = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
BasicValueEnum::PointerValue(str_bytes_ptr),
|
||||
BasicValueEnum::IntValue(str_len),
|
||||
BasicValueEnum::PointerValue(delimiter_bytes_ptr),
|
||||
BasicValueEnum::IntValue(delimiter_len),
|
||||
],
|
||||
&bitcode::STR_COUNT_SEGMENTS,
|
||||
)
|
||||
.into_int_value();
|
||||
|
||||
// a pointer to the elements
|
||||
let ret_list_ptr =
|
||||
allocate_list(env, inplace, &Layout::Builtin(Builtin::Str), segment_count);
|
||||
|
||||
// get the RocStr type defined by zig
|
||||
let roc_str_type = env.module.get_struct_type("str.RocStr").unwrap();
|
||||
|
||||
// convert `*mut { *mut u8, i64 }` to `*mut RocStr`
|
||||
let ret_list_ptr_zig_rocstr = builder.build_bitcast(
|
||||
ret_list_ptr,
|
||||
roc_str_type.ptr_type(AddressSpace::Generic),
|
||||
"convert_to_zig_rocstr",
|
||||
);
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
ret_list_ptr_zig_rocstr,
|
||||
BasicValueEnum::IntValue(segment_count),
|
||||
BasicValueEnum::PointerValue(str_bytes_ptr),
|
||||
BasicValueEnum::IntValue(str_len),
|
||||
BasicValueEnum::PointerValue(delimiter_bytes_ptr),
|
||||
BasicValueEnum::IntValue(delimiter_len),
|
||||
],
|
||||
&bitcode::STR_STR_SPLIT_IN_PLACE,
|
||||
);
|
||||
|
||||
store_list(env, ret_list_ptr, segment_count)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Str.concat : Str, Str -> Str
|
||||
pub fn str_concat<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -28,19 +106,19 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
let second_str_ptr = ptr_from_symbol(scope, second_str_symbol);
|
||||
let first_str_ptr = ptr_from_symbol(scope, first_str_symbol);
|
||||
|
||||
let str_wrapper_type = BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes));
|
||||
let ret_type = BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes));
|
||||
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*second_str_ptr,
|
||||
str_wrapper_type,
|
||||
ret_type,
|
||||
|second_str_ptr, second_str_len, second_str_smallness| {
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*first_str_ptr,
|
||||
str_wrapper_type,
|
||||
ret_type,
|
||||
|first_str_ptr, first_str_len, first_str_smallness| {
|
||||
// first_str_len > 0
|
||||
// We do this check to avoid allocating memory. If the first input
|
||||
|
@ -73,7 +151,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
|||
second_str_length_comparison,
|
||||
if_second_str_is_nonempty,
|
||||
if_second_str_is_empty,
|
||||
str_wrapper_type,
|
||||
ret_type,
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -458,14 +536,14 @@ fn clone_nonempty_str<'a, 'ctx, 'env>(
|
|||
}
|
||||
Smallness::Big => {
|
||||
let clone_ptr = allocate_list(env, inplace, &CHAR_LAYOUT, len);
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
|
||||
// Copy the bytes from the original array into the new
|
||||
// one we just malloc'd.
|
||||
builder.build_memcpy(clone_ptr, ptr_bytes, bytes_ptr, ptr_bytes, len);
|
||||
builder
|
||||
.build_memcpy(clone_ptr, ptr_bytes, bytes_ptr, ptr_bytes, len)
|
||||
.unwrap();
|
||||
|
||||
// Create a fresh wrapper struct for the newly populated array
|
||||
let struct_type = collection(ctx, env.ptr_bytes);
|
||||
|
@ -475,7 +553,7 @@ fn clone_nonempty_str<'a, 'ctx, 'env>(
|
|||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
clone_ptr,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
|
@ -703,3 +781,33 @@ pub fn str_starts_with<'a, 'ctx, 'env>(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Str.countGraphemes : Str -> Int
|
||||
pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
str_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
|
||||
let sym_str_ptr = ptr_from_symbol(scope, str_symbol);
|
||||
let ret_type = BasicTypeEnum::IntType(ctx.i64_type());
|
||||
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*sym_str_ptr,
|
||||
ret_type,
|
||||
|str_ptr, str_len, _str_smallness| {
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
BasicValueEnum::PointerValue(str_ptr),
|
||||
BasicValueEnum::IntValue(str_len),
|
||||
],
|
||||
&bitcode::STR_COUNT_GRAPEHEME_CLUSTERS,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -131,6 +131,7 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
Pointer(layout) => basic_type_from_layout(arena, context, &layout, ptr_bytes)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into(),
|
||||
PhantomEmptyStruct => context.struct_type(&[], false).into(),
|
||||
Struct(sorted_fields) => basic_type_from_record(arena, context, sorted_fields, ptr_bytes),
|
||||
Union(tags) if tags.len() == 1 => {
|
||||
let sorted_fields = tags.iter().next().unwrap();
|
||||
|
@ -202,26 +203,13 @@ pub fn block_of_memory<'ctx>(
|
|||
|
||||
/// Two usize values. Could be a wrapper for a List or a Str.
|
||||
///
|
||||
/// It would be nicer if we could store this as a tuple containing one usize
|
||||
/// and one pointer. However, if we do that, we run into a problem with the
|
||||
/// empty list: it doesn't know what pointer type it should initailize to,
|
||||
/// so it can only create an empty (usize, usize) struct.
|
||||
///
|
||||
/// This way, we always initialize it to (usize, usize), and then if there's
|
||||
/// actually a pointer, we use build_int_to_ptr and build_ptr_to_int to convert
|
||||
/// the field when necessary. (It's not allowed to cast the entire struct from
|
||||
/// (usize, usize) to (usize, ptr) or vice versa.)
|
||||
/// This way, we always initialize it to (*mut u8, usize), and may have to cast the pointer type
|
||||
/// for lists.
|
||||
pub fn collection(ctx: &Context, ptr_bytes: u32) -> StructType<'_> {
|
||||
let int_type = BasicTypeEnum::IntType(ptr_int(ctx, ptr_bytes));
|
||||
let usize_type = ptr_int(ctx, ptr_bytes);
|
||||
let u8_ptr = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
ctx.struct_type(&[int_type, int_type], false)
|
||||
}
|
||||
|
||||
/// Two usize values.
|
||||
pub fn collection_int_wrapper(ctx: &Context, ptr_bytes: u32) -> StructType<'_> {
|
||||
let usize_type = BasicTypeEnum::IntType(ptr_int(ctx, ptr_bytes));
|
||||
|
||||
ctx.struct_type(&[usize_type, usize_type], false)
|
||||
ctx.struct_type(&[u8_ptr.into(), usize_type.into()], false)
|
||||
}
|
||||
|
||||
pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -28,24 +28,28 @@ impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function {
|
||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||
run_jit_function!($execution_engine, $main_fn_name, $ty, $transform, v)
|
||||
run_jit_function!($lib, $main_fn_name, $ty, $transform, v)
|
||||
}};
|
||||
|
||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::JitFunction;
|
||||
use roc_gen::run_roc::RocCallResult;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn() -> RocCallResult<$ty>> = $execution_engine
|
||||
.get_function($main_fn_name)
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<$ty>) -> ()> =
|
||||
$lib.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
match main.call().into() {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
|
||||
main(result.as_mut_ptr());
|
||||
|
||||
match result.assume_init().into() {
|
||||
Ok(success) => {
|
||||
// only if there are no exceptions thrown, check for errors
|
||||
assert_eq!(
|
||||
|
@ -68,26 +72,25 @@ macro_rules! run_jit_function {
|
|||
/// It explicitly allocates a buffer that the roc main function can write its result into.
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function_dynamic_type {
|
||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||
run_jit_function_dynamic_type!($execution_engine, $main_fn_name, $bytes, $transform, v)
|
||||
run_jit_function_dynamic_type!($lib, $main_fn_name, $bytes, $transform, v)
|
||||
}};
|
||||
|
||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::JitFunction;
|
||||
use roc_gen::run_roc::RocCallResult;
|
||||
|
||||
unsafe {
|
||||
let main: JitFunction<unsafe extern "C" fn(*const u8)> = $execution_engine
|
||||
.get_function($main_fn_name)
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*const u8)> = $lib
|
||||
.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
let layout = std::alloc::Layout::array::<u8>($bytes).unwrap();
|
||||
let result = std::alloc::alloc(layout);
|
||||
main.call(result);
|
||||
main(result);
|
||||
|
||||
let flag = *result;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue