mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge pull request #3621 from rtfeldman/expect-memcpy
Clone more values into shared expect buffer
This commit is contained in:
commit
daec85a75e
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3716,6 +3716,7 @@ dependencies = [
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_region",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"target-lexicon",
|
||||
|
@ -143,6 +143,7 @@ comptime {
|
||||
exportStrFn(str.strTrim, "trim");
|
||||
exportStrFn(str.strTrimLeft, "trim_left");
|
||||
exportStrFn(str.strTrimRight, "trim_right");
|
||||
exportStrFn(str.strCloneTo, "clone_to");
|
||||
|
||||
inline for (INTEGERS) |T| {
|
||||
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");
|
||||
|
@ -2521,3 +2521,35 @@ test "getScalarUnsafe" {
|
||||
try expectEqual(result.scalar, @intCast(u32, expected));
|
||||
try expectEqual(result.bytesParsed, 1);
|
||||
}
|
||||
|
||||
pub fn strCloneTo(
|
||||
ptr: [*]u8,
|
||||
offset: usize,
|
||||
string: RocStr,
|
||||
) callconv(.C) usize {
|
||||
const WIDTH: usize = @sizeOf(RocStr);
|
||||
if (string.isSmallStr()) {
|
||||
const array: [@sizeOf(RocStr)]u8 = @bitCast([@sizeOf(RocStr)]u8, string);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < array.len) : (i += 1) {
|
||||
ptr[offset + i] = array[i];
|
||||
}
|
||||
|
||||
return offset + WIDTH;
|
||||
} else {
|
||||
const slice = string.asSlice();
|
||||
|
||||
var relative = string;
|
||||
relative.str_bytes = @intToPtr(?[*]u8, offset + WIDTH); // i.e. just after the string struct
|
||||
|
||||
// write the string struct
|
||||
const array = relative.asArray();
|
||||
@memcpy(ptr + offset, &array, WIDTH);
|
||||
|
||||
// write the string bytes just after the struct
|
||||
@memcpy(ptr + offset + WIDTH, slice.ptr, slice.len);
|
||||
|
||||
return offset + WIDTH + slice.len;
|
||||
}
|
||||
}
|
||||
|
@ -336,6 +336,7 @@ pub const STR_GET_UNSAFE: &str = "roc_builtins.str.get_unsafe";
|
||||
pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
|
||||
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
|
||||
pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe";
|
||||
pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to";
|
||||
|
||||
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
||||
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
|
||||
|
@ -16,6 +16,7 @@ roc_mono = { path = "../mono" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_std = { path = "../../roc_std", default-features = false }
|
||||
roc_debug_flags = { path = "../debug_flags" }
|
||||
roc_region = { path = "../region" }
|
||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
|
@ -13,6 +13,7 @@ use crate::llvm::compare::{generic_eq, generic_neq};
|
||||
use crate::llvm::convert::{
|
||||
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
|
||||
};
|
||||
use crate::llvm::expect::clone_to_shared_memory;
|
||||
use crate::llvm::refcounting::{
|
||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
};
|
||||
@ -2817,105 +2818,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() {
|
||||
|
@ -383,6 +383,29 @@ pub fn list_capacity<'ctx>(
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
pub fn destructure<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
) -> (PointerValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
|
||||
let length = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "list_len")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let capacity = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "list_cap")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
// a `*mut u8` pointer
|
||||
let generic_ptr = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
(generic_ptr, length, capacity)
|
||||
}
|
||||
|
||||
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
pub fn list_sort_with<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
331
crates/compiler/gen_llvm/src/llvm/expect.rs
Normal file
331
crates/compiler/gen_llvm/src/llvm/expect.rs
Normal file
@ -0,0 +1,331 @@
|
||||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::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") }
|
||||
}
|
||||
|
||||
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 mut offset = env.ptr_int().const_int(12, false);
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[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() {
|
||||
build_copy(env, ptr, offset, value)
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -111,9 +111,11 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
||||
Expect {
|
||||
condition,
|
||||
remainder,
|
||||
lookups,
|
||||
..
|
||||
} => {
|
||||
result.insert(*condition);
|
||||
result.extend(lookups.iter().copied());
|
||||
stack.push(remainder);
|
||||
}
|
||||
|
||||
@ -1180,7 +1182,7 @@ impl<'a> Context<'a> {
|
||||
lookups,
|
||||
layouts,
|
||||
} => {
|
||||
let (b, b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||
|
||||
let expect = self.arena.alloc(Stmt::Expect {
|
||||
condition: *condition,
|
||||
@ -1190,6 +1192,10 @@ impl<'a> Context<'a> {
|
||||
remainder: b,
|
||||
});
|
||||
|
||||
let expect = self.add_inc_before_consume_all(lookups, expect, &b_live_vars);
|
||||
|
||||
b_live_vars.extend(lookups.iter().copied());
|
||||
|
||||
(expect, b_live_vars)
|
||||
}
|
||||
|
||||
@ -1299,9 +1305,11 @@ pub fn collect_stmt(
|
||||
Expect {
|
||||
condition,
|
||||
remainder,
|
||||
lookups,
|
||||
..
|
||||
} => {
|
||||
vars.insert(*condition);
|
||||
vars.extend(lookups.iter().copied());
|
||||
collect_stmt(remainder, jp_live_vars, vars)
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ impl<'a> ReplApp<'a> for CliApp {
|
||||
|
||||
/// Run user code that returns a type with a `Builtin` layout
|
||||
/// Size of the return value is statically determined from its Rust type
|
||||
fn call_function<Return, F>(&self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
fn call_function<Return, F>(&mut self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
|
||||
Self::Memory: 'a,
|
||||
@ -145,7 +145,7 @@ impl<'a> ReplApp<'a> for CliApp {
|
||||
|
||||
/// Run user code that returns a struct or union, whose size is provided as an argument
|
||||
fn call_function_dynamic_size<T, F>(
|
||||
&self,
|
||||
&mut self,
|
||||
main_fn_name: &str,
|
||||
ret_bytes: usize,
|
||||
transform: F,
|
||||
@ -292,6 +292,8 @@ pub fn expect_mono_module_to_dylib<'a>(
|
||||
);
|
||||
}
|
||||
|
||||
env.module.print_to_file("/tmp/test.ll").unwrap();
|
||||
|
||||
llvm_module_to_dylib(env.module, &target, opt_level).map(|lib| (lib, expects))
|
||||
}
|
||||
|
||||
@ -424,11 +426,11 @@ fn gen_and_eval_llvm<'a>(
|
||||
let (lib, main_fn_name, subs) =
|
||||
mono_module_to_dylib(&arena, target, loaded, opt_level).expect("we produce a valid Dylib");
|
||||
|
||||
let app = CliApp { lib };
|
||||
let mut app = CliApp { lib };
|
||||
|
||||
let res_answer = jit_to_ast(
|
||||
&arena,
|
||||
&app,
|
||||
&mut app,
|
||||
main_fn_name,
|
||||
main_fn_layout,
|
||||
&content,
|
||||
|
@ -43,7 +43,7 @@ pub enum ToAstProblem {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn jit_to_ast<'a, A: ReplApp<'a>>(
|
||||
arena: &'a Bump,
|
||||
app: &'a A,
|
||||
app: &mut A,
|
||||
main_fn_name: &str,
|
||||
layout: ProcLayout<'a>,
|
||||
content: &'a Content,
|
||||
@ -94,6 +94,7 @@ enum NewtypeKind<'a> {
|
||||
/// Returns (new type containers, optional alias content, real content).
|
||||
fn unroll_newtypes_and_aliases<'a>(
|
||||
env: &Env<'a, 'a>,
|
||||
|
||||
mut content: &'a Content,
|
||||
) -> (Vec<'a, NewtypeKind<'a>>, Option<&'a Content>, &'a Content) {
|
||||
let mut newtype_containers = Vec::with_capacity_in(1, env.arena);
|
||||
@ -278,7 +279,7 @@ const OPAQUE_FUNCTION: Expr = Expr::Var {
|
||||
|
||||
fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||
env: &Env<'a, 'a>,
|
||||
app: &'a A,
|
||||
app: &mut A,
|
||||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &'a Content,
|
||||
@ -338,21 +339,23 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||
}
|
||||
Layout::Builtin(Builtin::Decimal) => Ok(num_helper!(RocDec)),
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
let size = layout.stack_size(env.target_info) as usize;
|
||||
Ok(
|
||||
app.call_function_dynamic_size(main_fn_name, size, |mem: &A::Memory, addr| {
|
||||
let string = mem.deref_str(addr);
|
||||
let arena_str = env.arena.alloc_str(string);
|
||||
Expr::Str(StrLiteral::PlainLine(arena_str))
|
||||
}),
|
||||
)
|
||||
let body = |mem: &A::Memory, addr| {
|
||||
let string = mem.deref_str(addr);
|
||||
let arena_str = env.arena.alloc_str(string);
|
||||
Expr::Str(StrLiteral::PlainLine(arena_str))
|
||||
};
|
||||
|
||||
Ok(app.call_function_returns_roc_str(env.target_info, main_fn_name, body))
|
||||
}
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||
//
|
||||
Ok(app.call_function_returns_roc_list(
|
||||
main_fn_name,
|
||||
|mem: &A::Memory, (addr, len, _cap)| {
|
||||
list_to_ast(env, mem, addr, len, elem_layout, raw_content)
|
||||
},
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => Ok(app.call_function(
|
||||
main_fn_name,
|
||||
|mem: &A::Memory, (addr, len): (usize, usize)| {
|
||||
list_to_ast(env, mem, addr, len, elem_layout, raw_content)
|
||||
},
|
||||
)),
|
||||
Layout::Struct { field_layouts, .. } => {
|
||||
let struct_addr_to_ast = |mem: &'a A::Memory, addr: usize| match raw_content {
|
||||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
@ -541,6 +544,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
|
||||
(_, Layout::Builtin(Builtin::List(elem_layout))) => {
|
||||
let elem_addr = mem.deref_usize(addr);
|
||||
let len = mem.deref_usize(addr + env.target_info.ptr_width() as usize);
|
||||
let _cap = mem.deref_usize(addr + 2 * env.target_info.ptr_width() as usize);
|
||||
|
||||
list_to_ast(env, mem, elem_addr, len, elem_layout, raw_content)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use roc_parse::ast::Expr;
|
||||
use roc_std::RocDec;
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
pub mod eval;
|
||||
pub mod gen;
|
||||
@ -10,15 +11,41 @@ pub trait ReplApp<'a> {
|
||||
/// Run user code that returns a type with a `Builtin` layout
|
||||
/// Size of the return value is statically determined from its Rust type
|
||||
/// The `transform` callback takes the app's memory and the returned value
|
||||
fn call_function<Return, F>(&self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
fn call_function<Return, F>(&mut self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
|
||||
Self::Memory: 'a;
|
||||
|
||||
fn call_function_returns_roc_list<F>(&mut self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, (usize, usize, usize)) -> Expr<'a>,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
self.call_function(main_fn_name, transform)
|
||||
}
|
||||
|
||||
fn call_function_returns_roc_str<T, F>(
|
||||
&mut self,
|
||||
target_info: TargetInfo,
|
||||
main_fn_name: &str,
|
||||
transform: F,
|
||||
) -> T
|
||||
where
|
||||
F: Fn(&'a Self::Memory, usize) -> T,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
let roc_str_width = match target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => 12,
|
||||
roc_target::PtrWidth::Bytes8 => 24,
|
||||
};
|
||||
|
||||
self.call_function_dynamic_size(main_fn_name, roc_str_width, transform)
|
||||
}
|
||||
|
||||
/// Run user code that returns a struct or union, whose size is provided as an argument
|
||||
/// The `transform` callback takes the app's memory and the address of the returned value
|
||||
fn call_function_dynamic_size<T, F>(
|
||||
&self,
|
||||
&mut self,
|
||||
main_fn_name: &str,
|
||||
ret_bytes: usize,
|
||||
transform: F,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_mono::{
|
||||
ir::ProcLayout,
|
||||
@ -19,21 +21,27 @@ pub fn get_values<'a>(
|
||||
subs: &'a Subs,
|
||||
interns: &'a Interns,
|
||||
start: *const u8,
|
||||
mut start_offset: usize,
|
||||
start_offset: usize,
|
||||
variables: &[Variable],
|
||||
) -> Result<Vec<Expr<'a>>, ToAstProblem> {
|
||||
let mut result = Vec::with_capacity(variables.len());
|
||||
let memory = ExpectMemory { start };
|
||||
|
||||
let memory = ExpectMemory {
|
||||
start,
|
||||
bytes_read: RefCell::new(0),
|
||||
};
|
||||
|
||||
let app = ExpectReplApp {
|
||||
memory: arena.alloc(memory),
|
||||
offset: start_offset,
|
||||
};
|
||||
|
||||
let app = arena.alloc(app);
|
||||
|
||||
for variable in variables {
|
||||
let expr = {
|
||||
let variable = *variable;
|
||||
|
||||
let app = ExpectReplApp {
|
||||
memory: arena.alloc(memory),
|
||||
start_offset,
|
||||
};
|
||||
|
||||
let content = subs.get_content_without_compacting(variable);
|
||||
|
||||
let mut layout_cache = LayoutCache::new(target_info);
|
||||
@ -45,19 +53,19 @@ 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,
|
||||
)
|
||||
}?;
|
||||
)?;
|
||||
|
||||
element
|
||||
};
|
||||
|
||||
result.push(expr);
|
||||
}
|
||||
@ -65,16 +73,18 @@ pub fn get_values<'a>(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ExpectMemory {
|
||||
start: *const u8,
|
||||
bytes_read: RefCell<usize>,
|
||||
}
|
||||
|
||||
macro_rules! deref_number {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name(&self, addr: usize) -> $t {
|
||||
// dbg!(std::any::type_name::<$t>(), self.start, addr);
|
||||
let ptr = unsafe { self.start.add(addr) } as *const _;
|
||||
unsafe { *ptr }
|
||||
*self.bytes_read.borrow_mut() += std::mem::size_of::<$t>();
|
||||
unsafe { std::ptr::read_unaligned(ptr) }
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -114,6 +124,9 @@ impl ReplAppMemory for ExpectMemory {
|
||||
let offset = self.deref_usize(addr);
|
||||
let length = self.deref_usize(addr + std::mem::size_of::<usize>());
|
||||
|
||||
// we don't store extra capacity
|
||||
// let capacity = self.deref_usize(addr + 2 * std::mem::size_of::<usize>());
|
||||
|
||||
unsafe {
|
||||
let ptr = self.start.add(offset);
|
||||
let slice = std::slice::from_raw_parts(ptr, length);
|
||||
@ -126,7 +139,7 @@ impl ReplAppMemory for ExpectMemory {
|
||||
|
||||
struct ExpectReplApp<'a> {
|
||||
memory: &'a ExpectMemory,
|
||||
start_offset: usize,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> ReplApp<'a> for ExpectReplApp<'a> {
|
||||
@ -136,18 +149,65 @@ impl<'a> ReplApp<'a> for ExpectReplApp<'a> {
|
||||
/// Size of the return value is statically determined from its Rust type
|
||||
/// The `transform` callback takes the app's memory and the returned value
|
||||
/// _main_fn_name is always the same and we don't use it here
|
||||
fn call_function<Return, F>(&self, _main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
fn call_function<Return, F>(&mut self, _main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
let result: Return = unsafe {
|
||||
let ptr = self.memory.start.add(self.start_offset);
|
||||
let ptr = self.memory.start.add(self.offset);
|
||||
let ptr: *const Return = std::mem::transmute(ptr);
|
||||
ptr.read()
|
||||
};
|
||||
|
||||
transform(self.memory, result)
|
||||
self.offset += std::mem::size_of::<Return>();
|
||||
|
||||
*self.memory.bytes_read.borrow_mut() = 0;
|
||||
|
||||
let transformed = transform(self.memory, result);
|
||||
|
||||
self.offset += *self.memory.bytes_read.borrow();
|
||||
|
||||
transformed
|
||||
}
|
||||
|
||||
fn call_function_returns_roc_list<F>(&mut self, main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, (usize, usize, usize)) -> Expr<'a>,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
self.call_function(main_fn_name, transform)
|
||||
}
|
||||
|
||||
fn call_function_returns_roc_str<T, F>(
|
||||
&mut self,
|
||||
_target_info: TargetInfo,
|
||||
main_fn_name: &str,
|
||||
transform: F,
|
||||
) -> T
|
||||
where
|
||||
F: Fn(&'a Self::Memory, usize) -> T,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
let string_length = RefCell::new(0);
|
||||
|
||||
let result = self.call_function_dynamic_size(main_fn_name, 24, |memory, addr| {
|
||||
let last_byte_addr = addr + (3 * std::mem::size_of::<usize>()) - 1;
|
||||
let last_byte = memory.deref_i8(last_byte_addr);
|
||||
|
||||
let is_small = last_byte < 0;
|
||||
|
||||
if !is_small {
|
||||
let length = memory.deref_usize(addr + std::mem::size_of::<usize>());
|
||||
*string_length.borrow_mut() = length;
|
||||
}
|
||||
|
||||
transform(memory, addr)
|
||||
});
|
||||
|
||||
self.offset += *string_length.borrow();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Run user code that returns a struct or union, whose size is provided as an argument
|
||||
@ -155,15 +215,17 @@ impl<'a> ReplApp<'a> for ExpectReplApp<'a> {
|
||||
/// _main_fn_name and _ret_bytes are only used for the CLI REPL. For Wasm they are compiled-in
|
||||
/// to the test_wrapper function of the app itself
|
||||
fn call_function_dynamic_size<T, F>(
|
||||
&self,
|
||||
&mut self,
|
||||
_main_fn_name: &str,
|
||||
_ret_bytes: usize,
|
||||
ret_bytes: usize,
|
||||
transform: F,
|
||||
) -> T
|
||||
where
|
||||
F: Fn(&'a Self::Memory, usize) -> T,
|
||||
Self::Memory: 'a,
|
||||
{
|
||||
transform(self.memory, self.start_offset)
|
||||
let result = transform(self.memory, self.offset);
|
||||
self.offset += ret_bytes;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> {
|
||||
/// Size of the return value is statically determined from its Rust type
|
||||
/// The `transform` callback takes the app's memory and the returned value
|
||||
/// _main_fn_name is always the same and we don't use it here
|
||||
fn call_function<Return, F>(&self, _main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
fn call_function<Return, F>(&mut self, _main_fn_name: &str, transform: F) -> Expr<'a>
|
||||
where
|
||||
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
|
||||
Self::Memory: 'a,
|
||||
@ -135,7 +135,7 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> {
|
||||
/// _main_fn_name and _ret_bytes are only used for the CLI REPL. For Wasm they are compiled-in
|
||||
/// to the test_wrapper function of the app itself
|
||||
fn call_function_dynamic_size<T, F>(
|
||||
&self,
|
||||
&mut self,
|
||||
_main_fn_name: &str,
|
||||
_ret_bytes: usize,
|
||||
transform: F,
|
||||
@ -242,13 +242,13 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
||||
.await
|
||||
.map_err(|js| format!("{:?}", js))?;
|
||||
|
||||
let app = WasmReplApp { arena };
|
||||
let mut app = WasmReplApp { arena };
|
||||
|
||||
// Run the app and transform the result value to an AST `Expr`
|
||||
// Restore type constructor names, and other user-facing info that was erased during compilation.
|
||||
let res_answer = jit_to_ast(
|
||||
arena,
|
||||
&app,
|
||||
&mut app,
|
||||
"", // main_fn_name is ignored (only passed to WasmReplApp methods)
|
||||
main_fn_layout,
|
||||
content,
|
||||
|
Loading…
Reference in New Issue
Block a user