Merge pull request #3621 from rtfeldman/expect-memcpy

Clone more values into shared expect buffer
This commit is contained in:
Richard Feldman 2022-07-23 23:00:54 -04:00 committed by GitHub
commit daec85a75e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 552 additions and 148 deletions

1
Cargo.lock generated
View File

@ -3716,6 +3716,7 @@ dependencies = [
"roc_error_macros",
"roc_module",
"roc_mono",
"roc_region",
"roc_std",
"roc_target",
"target-lexicon",

View File

@ -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.");

View File

@ -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;
}
}

View File

@ -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";

View File

@ -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" }

View File

@ -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() {

View File

@ -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>,

View 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()
}
}
}
}

View File

@ -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;

View File

@ -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)
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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,

View File

@ -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
}
}

View File

@ -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,