mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-18 17:49:47 +00:00
355 lines
11 KiB
Rust
355 lines
11 KiB
Rust
use crate::llvm::bitcode::call_bitcode_fn;
|
|
use crate::llvm::build::{store_roc_value, Env};
|
|
use crate::llvm::build_list::{self, incrementing_elem_loop};
|
|
use crate::llvm::convert::basic_type_from_layout;
|
|
use inkwell::builder::Builder;
|
|
use inkwell::types::BasicType;
|
|
use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
|
|
use inkwell::AddressSpace;
|
|
use roc_builtins::bitcode;
|
|
use roc_module::symbol::Symbol;
|
|
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
|
|
use roc_region::all::Region;
|
|
|
|
use super::build::{load_symbol_and_layout, Scope};
|
|
|
|
fn pointer_at_offset<'ctx>(
|
|
bd: &Builder<'ctx>,
|
|
ptr: PointerValue<'ctx>,
|
|
offset: IntValue<'ctx>,
|
|
) -> PointerValue<'ctx> {
|
|
unsafe { bd.build_gep(ptr, &[offset], "offset_ptr") }
|
|
}
|
|
|
|
/// Writes the module and region into the buffer
|
|
fn write_header<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
ptr: PointerValue<'ctx>,
|
|
mut offset: IntValue<'ctx>,
|
|
condition: Symbol,
|
|
region: Region,
|
|
) -> IntValue<'ctx> {
|
|
let region_start = env
|
|
.context
|
|
.i32_type()
|
|
.const_int(region.start().offset as _, false);
|
|
|
|
let region_end = env
|
|
.context
|
|
.i32_type()
|
|
.const_int(region.end().offset as _, false);
|
|
|
|
let module_id: u32 = unsafe { std::mem::transmute(condition.module_id()) };
|
|
let module_id = env.context.i32_type().const_int(module_id as _, false);
|
|
|
|
offset = build_copy(env, ptr, offset, region_start.into());
|
|
offset = build_copy(env, ptr, offset, region_end.into());
|
|
offset = build_copy(env, ptr, offset, module_id.into());
|
|
|
|
offset
|
|
}
|
|
|
|
/// Read the first two 32-bit values from the shared memory,
|
|
/// representing the total number of expect frames and the next free position
|
|
fn read_state<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
ptr: PointerValue<'ctx>,
|
|
) -> (IntValue<'ctx>, IntValue<'ctx>) {
|
|
let ptr_type = env.ptr_int().ptr_type(AddressSpace::Generic);
|
|
let ptr = env.builder.build_pointer_cast(ptr, ptr_type, "");
|
|
|
|
let one = env.ptr_int().const_int(1, false);
|
|
let offset_ptr = pointer_at_offset(env.builder, ptr, one);
|
|
|
|
let count = env.builder.build_load(ptr, "load_count");
|
|
let offset = env.builder.build_load(offset_ptr, "load_offset");
|
|
|
|
(count.into_int_value(), offset.into_int_value())
|
|
}
|
|
|
|
fn write_state<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
ptr: PointerValue<'ctx>,
|
|
count: IntValue<'ctx>,
|
|
offset: IntValue<'ctx>,
|
|
) {
|
|
let ptr_type = env.ptr_int().ptr_type(AddressSpace::Generic);
|
|
let ptr = env.builder.build_pointer_cast(ptr, ptr_type, "");
|
|
|
|
let one = env.ptr_int().const_int(1, false);
|
|
let offset_ptr = pointer_at_offset(env.builder, ptr, one);
|
|
|
|
env.builder.build_store(ptr, count);
|
|
env.builder.build_store(offset_ptr, offset);
|
|
}
|
|
|
|
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
layout_ids: &mut LayoutIds<'a>,
|
|
condition: Symbol,
|
|
region: Region,
|
|
lookups: &[Symbol],
|
|
) {
|
|
let func = env
|
|
.module
|
|
.get_function(bitcode::UTILS_EXPECT_FAILED_START)
|
|
.unwrap();
|
|
|
|
let call_result = env
|
|
.builder
|
|
.build_call(func, &[], "call_expect_start_failed");
|
|
|
|
let original_ptr = call_result
|
|
.try_as_basic_value()
|
|
.left()
|
|
.unwrap()
|
|
.into_pointer_value();
|
|
|
|
let (count, mut offset) = read_state(env, original_ptr);
|
|
|
|
offset = write_header(env, original_ptr, offset, condition, region);
|
|
|
|
for lookup in lookups.iter() {
|
|
let (value, layout) = load_symbol_and_layout(scope, lookup);
|
|
|
|
offset = build_clone(
|
|
env,
|
|
layout_ids,
|
|
original_ptr,
|
|
offset,
|
|
value,
|
|
*layout,
|
|
WhenRecursive::Unreachable,
|
|
);
|
|
}
|
|
|
|
let one = env.ptr_int().const_int(1, false);
|
|
let new_count = env.builder.build_int_add(count, one, "inc");
|
|
write_state(env, original_ptr, new_count, offset)
|
|
}
|
|
|
|
#[derive(Clone, Debug, Copy)]
|
|
enum WhenRecursive<'a> {
|
|
Unreachable,
|
|
#[allow(dead_code)]
|
|
Loop(UnionLayout<'a>),
|
|
}
|
|
|
|
fn build_clone<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
layout_ids: &mut LayoutIds<'a>,
|
|
ptr: PointerValue<'ctx>,
|
|
offset: IntValue<'ctx>,
|
|
value: BasicValueEnum<'ctx>,
|
|
layout: Layout<'a>,
|
|
when_recursive: WhenRecursive<'a>,
|
|
) -> IntValue<'ctx> {
|
|
match layout {
|
|
Layout::Builtin(builtin) => {
|
|
build_clone_builtin(env, layout_ids, ptr, offset, value, builtin, when_recursive)
|
|
}
|
|
|
|
Layout::Struct {
|
|
field_layouts: _, ..
|
|
} => {
|
|
if layout.safe_to_memcpy() {
|
|
build_copy(env, ptr, offset, value)
|
|
} else {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
Layout::LambdaSet(_) => unreachable!("cannot compare closures"),
|
|
|
|
Layout::Union(_union_layout) => {
|
|
if layout.safe_to_memcpy() {
|
|
let ptr = unsafe {
|
|
env.builder
|
|
.build_in_bounds_gep(ptr, &[offset], "at_current_offset")
|
|
};
|
|
|
|
let ptr_type = value.get_type().ptr_type(AddressSpace::Generic);
|
|
let ptr = env
|
|
.builder
|
|
.build_pointer_cast(ptr, ptr_type, "cast_ptr_type");
|
|
|
|
store_roc_value(env, layout, ptr, value);
|
|
|
|
let width = value.get_type().size_of().unwrap();
|
|
env.builder.build_int_add(offset, width, "new_offset")
|
|
} else {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
/*
|
|
Layout::Boxed(inner_layout) => build_box_eq(
|
|
env,
|
|
layout_ids,
|
|
when_recursive,
|
|
lhs_layout,
|
|
inner_layout,
|
|
lhs_val,
|
|
rhs_val,
|
|
),
|
|
|
|
Layout::RecursivePointer => match when_recursive {
|
|
WhenRecursive::Unreachable => {
|
|
unreachable!("recursion pointers should never be compared directly")
|
|
}
|
|
|
|
WhenRecursive::Loop(union_layout) => {
|
|
let layout = Layout::Union(union_layout);
|
|
|
|
let bt = basic_type_from_layout(env, &layout);
|
|
|
|
// cast the i64 pointer to a pointer to block of memory
|
|
let field1_cast = env
|
|
.builder
|
|
.build_bitcast(lhs_val, bt, "i64_to_opaque")
|
|
.into_pointer_value();
|
|
|
|
let field2_cast = env
|
|
.builder
|
|
.build_bitcast(rhs_val, bt, "i64_to_opaque")
|
|
.into_pointer_value();
|
|
|
|
build_tag_eq(
|
|
env,
|
|
layout_ids,
|
|
WhenRecursive::Loop(union_layout),
|
|
&union_layout,
|
|
field1_cast.into(),
|
|
field2_cast.into(),
|
|
)
|
|
}
|
|
},
|
|
*/
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
|
|
fn build_copy<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
ptr: PointerValue<'ctx>,
|
|
offset: IntValue<'ctx>,
|
|
value: BasicValueEnum<'ctx>,
|
|
) -> IntValue<'ctx> {
|
|
let ptr = unsafe {
|
|
env.builder
|
|
.build_in_bounds_gep(ptr, &[offset], "at_current_offset")
|
|
};
|
|
|
|
let ptr_type = value.get_type().ptr_type(AddressSpace::Generic);
|
|
let ptr = env
|
|
.builder
|
|
.build_pointer_cast(ptr, ptr_type, "cast_ptr_type");
|
|
|
|
env.builder.build_store(ptr, value);
|
|
|
|
let width = value.get_type().size_of().unwrap();
|
|
env.builder.build_int_add(offset, width, "new_offset")
|
|
}
|
|
|
|
fn build_clone_builtin<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
layout_ids: &mut LayoutIds<'a>,
|
|
ptr: PointerValue<'ctx>,
|
|
offset: IntValue<'ctx>,
|
|
value: BasicValueEnum<'ctx>,
|
|
builtin: Builtin<'a>,
|
|
when_recursive: WhenRecursive<'a>,
|
|
) -> IntValue<'ctx> {
|
|
use Builtin::*;
|
|
|
|
match builtin {
|
|
Int(_) | Float(_) | Bool | Decimal => build_copy(env, ptr, offset, value),
|
|
|
|
Builtin::Str => {
|
|
//
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[ptr.into(), offset.into(), value],
|
|
bitcode::STR_CLONE_TO,
|
|
)
|
|
.into_int_value()
|
|
}
|
|
Builtin::List(elem) => {
|
|
let bd = env.builder;
|
|
|
|
let list = value.into_struct_value();
|
|
let (elements, len, _cap) = build_list::destructure(env.builder, list);
|
|
|
|
let list_width = env
|
|
.ptr_int()
|
|
.const_int(env.target_info.ptr_size() as u64 * 3, false);
|
|
let elements_offset = bd.build_int_add(offset, list_width, "new_offset");
|
|
|
|
let mut offset = offset;
|
|
|
|
// we only copy the elements we actually have (and skip extra capacity)
|
|
offset = build_copy(env, ptr, offset, elements_offset.into());
|
|
offset = build_copy(env, ptr, offset, len.into());
|
|
offset = build_copy(env, ptr, offset, len.into());
|
|
|
|
let (element_width, _element_align) = elem.stack_size_and_alignment(env.target_info);
|
|
let element_width = env.ptr_int().const_int(element_width as _, false);
|
|
|
|
let elements_width = bd.build_int_mul(element_width, len, "elements_width");
|
|
|
|
if elem.safe_to_memcpy() {
|
|
// NOTE we are not actually sure the dest is properly aligned
|
|
let dest = pointer_at_offset(bd, ptr, offset);
|
|
let src = bd.build_pointer_cast(
|
|
elements,
|
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
|
"to_bytes_pointer",
|
|
);
|
|
bd.build_memcpy(dest, 1, src, 1, elements_width).unwrap();
|
|
|
|
bd.build_int_add(offset, elements_width, "new_offset")
|
|
} else {
|
|
let elements_start_offset = offset;
|
|
|
|
let element_type = basic_type_from_layout(env, elem);
|
|
let elements = bd.build_pointer_cast(
|
|
elements,
|
|
element_type.ptr_type(AddressSpace::Generic),
|
|
"elements",
|
|
);
|
|
|
|
let element_offset = bd.build_alloca(env.ptr_int(), "element_offset");
|
|
bd.build_store(element_offset, elements_start_offset);
|
|
|
|
let body = |_index, element| {
|
|
let current_offset = bd.build_load(element_offset, "element_offset");
|
|
|
|
let new_offset = build_clone(
|
|
env,
|
|
layout_ids,
|
|
ptr,
|
|
current_offset.into_int_value(),
|
|
element,
|
|
*elem,
|
|
when_recursive,
|
|
);
|
|
|
|
bd.build_store(element_offset, new_offset);
|
|
};
|
|
|
|
let parent = env
|
|
.builder
|
|
.get_insert_block()
|
|
.and_then(|b| b.get_parent())
|
|
.unwrap();
|
|
|
|
incrementing_elem_loop(env, parent, *elem, elements, len, "index", body);
|
|
|
|
bd.build_load(element_offset, "element_offset")
|
|
.into_int_value()
|
|
}
|
|
}
|
|
}
|
|
}
|