From d4feaf871bc9ac7b348458cb673dbcea53ae7fbf Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 23 Jul 2022 00:09:25 +0200 Subject: [PATCH] add llvm expect cloning --- crates/compiler/gen_llvm/src/llvm/build.rs | 108 +------- crates/compiler/gen_llvm/src/llvm/expect.rs | 261 ++++++++++++++++++++ crates/compiler/gen_llvm/src/llvm/mod.rs | 1 + crates/repl_expect/src/lib.rs | 33 ++- 4 files changed, 296 insertions(+), 107 deletions(-) create mode 100644 crates/compiler/gen_llvm/src/llvm/expect.rs diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 5614d56a22..268fb22845 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -14,6 +14,7 @@ use crate::llvm::convert::{ self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, zig_str_type, }; +use crate::llvm::expect::clone_to_shared_memory; use crate::llvm::refcounting::{ build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, }; @@ -2911,105 +2912,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( match env.target_info.ptr_width() { roc_target::PtrWidth::Bytes8 => { - let func = env - .module - .get_function(bitcode::UTILS_EXPECT_FAILED_START) - .unwrap(); - - let call_result = bd.build_call(func, &[], "call_expect_start_failed"); - - let mut ptr = call_result - .try_as_basic_value() - .left() - .unwrap() - .into_pointer_value(); - - { - let value = env - .context - .i32_type() - .const_int(region.start().offset as _, false); - - let cast_ptr = env.builder.build_pointer_cast( - ptr, - value.get_type().ptr_type(AddressSpace::Generic), - "to_store_pointer", - ); - - env.builder.build_store(cast_ptr, value); - - // let increment = layout.stack_size(env.target_info); - let increment = 4; - let increment = env.ptr_int().const_int(increment as _, false); - - ptr = unsafe { - env.builder.build_gep(ptr, &[increment], "increment_ptr") - }; - } - - { - let value = env - .context - .i32_type() - .const_int(region.end().offset as _, false); - - let cast_ptr = env.builder.build_pointer_cast( - ptr, - value.get_type().ptr_type(AddressSpace::Generic), - "to_store_pointer", - ); - - env.builder.build_store(cast_ptr, value); - - // let increment = layout.stack_size(env.target_info); - let increment = 4; - let increment = env.ptr_int().const_int(increment as _, false); - - ptr = unsafe { - env.builder.build_gep(ptr, &[increment], "increment_ptr") - }; - } - - { - let region_bytes: u32 = - unsafe { std::mem::transmute(cond_symbol.module_id()) }; - let value = env.context.i32_type().const_int(region_bytes as _, false); - - let cast_ptr = env.builder.build_pointer_cast( - ptr, - value.get_type().ptr_type(AddressSpace::Generic), - "to_store_pointer", - ); - - env.builder.build_store(cast_ptr, value); - - // let increment = layout.stack_size(env.target_info); - let increment = 4; - let increment = env.ptr_int().const_int(increment as _, false); - - ptr = unsafe { - env.builder.build_gep(ptr, &[increment], "increment_ptr") - }; - } - - for lookup in lookups.iter() { - let (value, layout) = load_symbol_and_layout(scope, lookup); - - let cast_ptr = env.builder.build_pointer_cast( - ptr, - value.get_type().ptr_type(AddressSpace::Generic), - "to_store_pointer", - ); - - store_roc_value(env, *layout, cast_ptr, value); - - let increment = layout.stack_size(env.target_info); - let increment = env.ptr_int().const_int(increment as _, false); - - ptr = unsafe { - env.builder.build_gep(ptr, &[increment], "increment_ptr") - }; - } + clone_to_shared_memory( + env, + scope, + layout_ids, + *cond_symbol, + *region, + lookups, + ); // NOTE: signals to the parent process that an expect failed if env.mode.runs_expects_in_separate_process() { diff --git a/crates/compiler/gen_llvm/src/llvm/expect.rs b/crates/compiler/gen_llvm/src/llvm/expect.rs new file mode 100644 index 0000000000..76c47ce7ac --- /dev/null +++ b/crates/compiler/gen_llvm/src/llvm/expect.rs @@ -0,0 +1,261 @@ +use crate::llvm::bitcode::{call_bitcode_fn, call_str_bitcode_fn}; +use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV}; +use crate::llvm::build_list::{list_len, load_list_ptr}; +use crate::llvm::build_str::str_equal; +use crate::llvm::convert::basic_type_from_layout; +use bumpalo::collections::Vec; +use inkwell::types::BasicType; +use inkwell::values::{ + BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, +}; +use inkwell::{AddressSpace, FloatPredicate, IntPredicate}; +use roc_builtins::bitcode; +use roc_builtins::bitcode::{FloatWidth, IntWidth}; +use roc_module::symbol::Symbol; +use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; +use roc_region::all::Region; + +use super::build::{ + dec_binop_with_unchecked, load_roc_value, load_symbol_and_layout, use_roc_value, Scope, +}; +use super::convert::argument_type_from_union_layout; + +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 mut ptr = original_ptr; + + { + let value = env + .context + .i32_type() + .const_int(region.start().offset as _, false); + + let cast_ptr = env.builder.build_pointer_cast( + ptr, + value.get_type().ptr_type(AddressSpace::Generic), + "to_store_pointer", + ); + + env.builder.build_store(cast_ptr, value); + + // let increment = layout.stack_size(env.target_info); + let increment = 4; + let increment = env.ptr_int().const_int(increment as _, false); + + ptr = unsafe { env.builder.build_gep(ptr, &[increment], "increment_ptr") }; + } + + { + let value = env + .context + .i32_type() + .const_int(region.end().offset as _, false); + + let cast_ptr = env.builder.build_pointer_cast( + ptr, + value.get_type().ptr_type(AddressSpace::Generic), + "to_store_pointer", + ); + + env.builder.build_store(cast_ptr, value); + + // let increment = layout.stack_size(env.target_info); + let increment = 4; + let increment = env.ptr_int().const_int(increment as _, false); + + ptr = unsafe { env.builder.build_gep(ptr, &[increment], "increment_ptr") }; + } + + { + let region_bytes: u32 = unsafe { std::mem::transmute(condition.module_id()) }; + let value = env.context.i32_type().const_int(region_bytes as _, false); + + let cast_ptr = env.builder.build_pointer_cast( + ptr, + value.get_type().ptr_type(AddressSpace::Generic), + "to_store_pointer", + ); + + env.builder.build_store(cast_ptr, value); + + // let increment = layout.stack_size(env.target_info); + let increment = 4; + let increment = env.ptr_int().const_int(increment as _, false); + + ptr = unsafe { env.builder.build_gep(ptr, &[increment], "increment_ptr") }; + } + + let mut offset = env.ptr_int().const_int(12, false); + let mut ptr = original_ptr; + + for lookup in lookups.iter() { + let (value, layout) = load_symbol_and_layout(scope, lookup); + + offset = build_clone( + env, + layout_ids, + ptr, + offset, + value, + *layout, + WhenRecursive::Unreachable, + ); + } +} + +#[derive(Clone, Debug)] +enum WhenRecursive<'a> { + Unreachable, + 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, ptr, offset, value, builtin, when_recursive) + } + + /* + Layout::Struct { field_layouts, .. } => build_struct_eq( + env, + layout_ids, + field_layouts, + when_recursive, + lhs_val.into_struct_value(), + rhs_val.into_struct_value(), + ), + + Layout::LambdaSet(_) => unreachable!("cannot compare closures"), + + Layout::Union(union_layout) => build_tag_eq( + env, + layout_ids, + when_recursive, + union_layout, + lhs_val, + rhs_val, + ), + + 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>, + 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) => todo!(), + } +} diff --git a/crates/compiler/gen_llvm/src/llvm/mod.rs b/crates/compiler/gen_llvm/src/llvm/mod.rs index 5973ee0a51..b7e901eadb 100644 --- a/crates/compiler/gen_llvm/src/llvm/mod.rs +++ b/crates/compiler/gen_llvm/src/llvm/mod.rs @@ -4,5 +4,6 @@ pub mod build_list; pub mod build_str; pub mod compare; pub mod convert; +mod expect; pub mod externs; pub mod refcounting; diff --git a/crates/repl_expect/src/lib.rs b/crates/repl_expect/src/lib.rs index 1532be7e83..e369a839bc 100644 --- a/crates/repl_expect/src/lib.rs +++ b/crates/repl_expect/src/lib.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; + use roc_module::symbol::Interns; use roc_mono::{ ir::ProcLayout, @@ -23,9 +25,13 @@ pub fn get_values<'a>( variables: &[Variable], ) -> Result>, ToAstProblem> { let mut result = Vec::with_capacity(variables.len()); - let memory = ExpectMemory { start }; for variable in variables { + let memory = ExpectMemory { + start, + extra_offset: Default::default(), + }; + let expr = { let variable = *variable; @@ -34,6 +40,8 @@ pub fn get_values<'a>( start_offset, }; + let app = arena.alloc(app); + let content = subs.get_content_without_compacting(variable); let mut layout_cache = LayoutCache::new(target_info); @@ -45,19 +53,24 @@ pub fn get_values<'a>( captures_niche: CapturesNiche::no_niche(), }; - start_offset += layout.stack_size(target_info) as usize; - - jit_to_ast( + let element = jit_to_ast( arena, - arena.alloc(app), + app, "expect_repl_main_fn", proc_layout, content, subs, interns, target_info, - ) - }?; + )?; + + start_offset += layout.stack_size(target_info) as usize; + let mut extra_offset = app.memory.extra_offset.borrow_mut(); + start_offset += *extra_offset; + *extra_offset = 0; + + element + }; result.push(expr); } @@ -65,9 +78,10 @@ pub fn get_values<'a>( Ok(result) } -#[derive(Clone, Copy)] +#[derive(Clone)] struct ExpectMemory { start: *const u8, + extra_offset: RefCell, } macro_rules! deref_number { @@ -113,6 +127,9 @@ impl ReplAppMemory for ExpectMemory { } else { let offset = self.deref_usize(addr); let length = self.deref_usize(addr + std::mem::size_of::()); + let capacity = self.deref_usize(addr + std::mem::size_of::()); + + *self.extra_offset.borrow_mut() += capacity; unsafe { let ptr = self.start.add(offset);