mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Merge branch 'trunk' into gen-dev/mem
This commit is contained in:
commit
08aa3d31d4
32 changed files with 652 additions and 428 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -2570,6 +2570,12 @@ dependencies = [
|
||||||
"syn 1.0.72",
|
"syn 1.0.72",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parity-wasm"
|
||||||
|
version = "0.42.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -3642,6 +3648,29 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_gen_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"indoc 0.3.6",
|
||||||
|
"libc",
|
||||||
|
"parity-wasm",
|
||||||
|
"pretty_assertions 0.5.1",
|
||||||
|
"roc_builtins",
|
||||||
|
"roc_can",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_load",
|
||||||
|
"roc_module",
|
||||||
|
"roc_mono",
|
||||||
|
"roc_std",
|
||||||
|
"roc_types",
|
||||||
|
"target-lexicon",
|
||||||
|
"tempfile",
|
||||||
|
"wasmer",
|
||||||
|
"wasmer-wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_ident"
|
name = "roc_ident"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -4348,6 +4377,7 @@ dependencies = [
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
"roc_gen_llvm",
|
"roc_gen_llvm",
|
||||||
|
"roc_gen_wasm",
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
|
|
|
@ -20,6 +20,7 @@ members = [
|
||||||
"compiler/load",
|
"compiler/load",
|
||||||
"compiler/gen_llvm",
|
"compiler/gen_llvm",
|
||||||
"compiler/gen_dev",
|
"compiler/gen_dev",
|
||||||
|
"compiler/gen_wasm",
|
||||||
"compiler/build",
|
"compiler/build",
|
||||||
"compiler/arena_pool",
|
"compiler/arena_pool",
|
||||||
"compiler/test_gen",
|
"compiler/test_gen",
|
||||||
|
|
|
@ -75,6 +75,10 @@ test-rust:
|
||||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release && sccache --show-stats
|
cargo test --release && sccache --show-stats
|
||||||
|
# run i386 (32-bit linux) cli tests
|
||||||
|
RUN echo "4" | cargo run --release -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
||||||
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
|
cargo test --release --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
||||||
|
|
||||||
verify-no-git-changes:
|
verify-no-git-changes:
|
||||||
FROM +test-rust
|
FROM +test-rust
|
||||||
|
|
|
@ -16,7 +16,8 @@ bench = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["target-x86", "llvm", "editor"]
|
default = ["target-x86", "llvm", "editor"]
|
||||||
wasm-cli-run = []
|
wasm32-cli-run = []
|
||||||
|
i386-cli-run = []
|
||||||
|
|
||||||
# This is a separate feature because when we generate docs on Netlify,
|
# This is a separate feature because when we generate docs on Netlify,
|
||||||
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
||||||
|
|
|
@ -108,7 +108,7 @@ mod cli_run {
|
||||||
assert!(out.status.success());
|
assert!(out.status.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wasm-cli-run")]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
fn check_wasm_output_with_stdin(
|
fn check_wasm_output_with_stdin(
|
||||||
file: &Path,
|
file: &Path,
|
||||||
stdin: &[&str],
|
stdin: &[&str],
|
||||||
|
@ -117,7 +117,7 @@ mod cli_run {
|
||||||
expected_ending: &str,
|
expected_ending: &str,
|
||||||
) {
|
) {
|
||||||
let mut flags = flags.to_vec();
|
let mut flags = flags.to_vec();
|
||||||
flags.push("--backend=wasm");
|
flags.push("--backend=wasm32");
|
||||||
|
|
||||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
|
@ -315,8 +315,8 @@ mod cli_run {
|
||||||
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
#[cfg(feature = "wasm-cli-run")]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
mod wasm {
|
mod wasm32 {
|
||||||
use super::*;
|
use super::*;
|
||||||
$(
|
$(
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -354,6 +354,47 @@ mod cli_run {
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "i386-cli-run")]
|
||||||
|
mod i386 {
|
||||||
|
use super::*;
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(not(debug_assertions), serial(benchmark))]
|
||||||
|
fn $test_name() {
|
||||||
|
let benchmark = $benchmark;
|
||||||
|
let file_name = examples_dir("benchmarks").join(benchmark.filename);
|
||||||
|
|
||||||
|
// TODO fix QuicksortApp and then remove this!
|
||||||
|
match benchmark.filename {
|
||||||
|
"QuicksortApp.roc" => {
|
||||||
|
eprintln!("WARNING: skipping testing benchmark {} because the test is broken right now!", benchmark.filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check with and without optimizations
|
||||||
|
check_output_with_stdin(
|
||||||
|
&file_name,
|
||||||
|
benchmark.stdin,
|
||||||
|
benchmark.executable_filename,
|
||||||
|
&["--backend=x86_32"],
|
||||||
|
benchmark.expected_ending,
|
||||||
|
benchmark.use_valgrind,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_output_with_stdin(
|
||||||
|
&file_name,
|
||||||
|
benchmark.stdin,
|
||||||
|
benchmark.executable_filename,
|
||||||
|
&["--backend=x86_32", "--optimize"],
|
||||||
|
benchmark.expected_ending,
|
||||||
|
benchmark.use_valgrind,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn all_benchmarks_have_tests() {
|
fn all_benchmarks_have_tests() {
|
||||||
|
@ -562,7 +603,7 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wasm-cli-run")]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String {
|
fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use wasmer::{Instance, Module, Store};
|
use wasmer::{Instance, Module, Store};
|
||||||
|
|
|
@ -262,7 +262,7 @@ pub fn rebuild_host(target: &Triple, host_input_path: &Path) {
|
||||||
&emit_bin,
|
&emit_bin,
|
||||||
zig_host_src.to_str().unwrap(),
|
zig_host_src.to_str().unwrap(),
|
||||||
zig_str_path.to_str().unwrap(),
|
zig_str_path.to_str().unwrap(),
|
||||||
"i386-linux-gnu",
|
"i386-linux-musl",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
||||||
|
|
|
@ -208,13 +208,7 @@ pub fn unsafeReallocate(
|
||||||
new_length: usize,
|
new_length: usize,
|
||||||
element_width: usize,
|
element_width: usize,
|
||||||
) [*]u8 {
|
) [*]u8 {
|
||||||
const align_width: usize = blk: {
|
const align_width: usize = std.math.max(alignment, @sizeOf(usize));
|
||||||
if (alignment > 8) {
|
|
||||||
break :blk (2 * @sizeOf(usize));
|
|
||||||
} else {
|
|
||||||
break :blk @sizeOf(usize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const old_width = align_width + old_length * element_width;
|
const old_width = align_width + old_length * element_width;
|
||||||
const new_width = align_width + new_length * element_width;
|
const new_width = align_width + new_length * element_width;
|
||||||
|
|
|
@ -8,7 +8,9 @@ use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{ModuleName, TagName};
|
use roc_module::ident::{ModuleName, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::ir::{BranchInfo, CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
use roc_mono::ir::{
|
||||||
|
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Proc, Stmt,
|
||||||
|
};
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
|
@ -150,7 +152,7 @@ where
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal(lit) => {
|
Expr::Literal(lit) => {
|
||||||
if self.env().lazy_literals {
|
if self.env().lazy_literals {
|
||||||
self.literal_map().insert(*sym, lit.clone());
|
self.literal_map().insert(*sym, *lit);
|
||||||
} else {
|
} else {
|
||||||
self.load_literal(sym, lit)?;
|
self.load_literal(sym, lit)?;
|
||||||
}
|
}
|
||||||
|
@ -618,8 +620,10 @@ where
|
||||||
owning_symbol.insert(*sym, *structure);
|
owning_symbol.insert(*sym, *structure);
|
||||||
}
|
}
|
||||||
Expr::Array { elems, .. } => {
|
Expr::Array { elems, .. } => {
|
||||||
for sym in *elems {
|
for elem in *elems {
|
||||||
self.set_last_seen(*sym, stmt, &owning_symbol);
|
if let ListLiteralElement::Symbol(sym) = elem {
|
||||||
|
self.set_last_seen(*sym, stmt, &owning_symbol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Reuse {
|
Expr::Reuse {
|
||||||
|
|
|
@ -51,7 +51,10 @@ use roc_builtins::bitcode;
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{BranchInfo, CallType, EntryPoint, JoinPointId, ModifyRc, OptLevel, ProcLayout};
|
use roc_mono::ir::{
|
||||||
|
BranchInfo, CallType, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc, OptLevel,
|
||||||
|
ProcLayout,
|
||||||
|
};
|
||||||
use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, UnionLayout};
|
use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, UnionLayout};
|
||||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||||
|
|
||||||
|
@ -2157,57 +2160,141 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
elem_layout: &Layout<'a>,
|
elem_layout: &Layout<'a>,
|
||||||
elems: &&[Symbol],
|
elems: &[ListLiteralElement],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let len_u64 = elems.len() as u64;
|
let element_type = basic_type_from_layout(env, elem_layout);
|
||||||
|
|
||||||
let ptr = {
|
let list_length = elems.len();
|
||||||
let len_type = env.ptr_int();
|
let list_length_intval = env.ptr_int().const_int(list_length as _, false);
|
||||||
let len = len_type.const_int(len_u64, false);
|
|
||||||
|
|
||||||
allocate_list(env, elem_layout, len)
|
if element_type.is_int_type() {
|
||||||
};
|
let element_type = element_type.into_int_type();
|
||||||
|
let element_width = elem_layout.stack_size(env.ptr_bytes);
|
||||||
|
let size = list_length * element_width as usize;
|
||||||
|
let alignment = elem_layout
|
||||||
|
.alignment_bytes(env.ptr_bytes)
|
||||||
|
.max(env.ptr_bytes);
|
||||||
|
|
||||||
// Copy the elements from the list literal into the array
|
let mut is_all_constant = true;
|
||||||
for (index, symbol) in elems.iter().enumerate() {
|
let zero_elements = (env.ptr_bytes as f64 / element_width as f64).ceil() as usize;
|
||||||
let val = load_symbol(scope, symbol);
|
|
||||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
|
||||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
|
||||||
|
|
||||||
builder.build_store(elem_ptr, val);
|
// runtime-evaluated elements
|
||||||
|
let mut runtime_evaluated_elements = Vec::with_capacity_in(list_length, env.arena);
|
||||||
|
|
||||||
|
// set up a global that contains all the literal elements of the array
|
||||||
|
// any variables or expressions are represented as `undef`
|
||||||
|
let global = {
|
||||||
|
let mut global_elements = Vec::with_capacity_in(list_length, env.arena);
|
||||||
|
|
||||||
|
// insert NULL bytes for the refcount
|
||||||
|
// these elements are (dropped again if the list contains non-constants)
|
||||||
|
for _ in 0..zero_elements {
|
||||||
|
global_elements.push(element_type.const_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the elements from the list literal into the array
|
||||||
|
for (index, element) in elems.iter().enumerate() {
|
||||||
|
match element {
|
||||||
|
ListLiteralElement::Literal(literal) => {
|
||||||
|
let val = build_exp_literal(env, elem_layout, literal);
|
||||||
|
global_elements.push(val.into_int_value());
|
||||||
|
}
|
||||||
|
ListLiteralElement::Symbol(symbol) => {
|
||||||
|
let val = load_symbol(scope, symbol);
|
||||||
|
let intval = val.into_int_value();
|
||||||
|
|
||||||
|
if intval.is_const() {
|
||||||
|
global_elements.push(intval);
|
||||||
|
} else {
|
||||||
|
is_all_constant = false;
|
||||||
|
|
||||||
|
runtime_evaluated_elements.push((index, val));
|
||||||
|
|
||||||
|
global_elements.push(element_type.get_undef());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let const_elements = if is_all_constant {
|
||||||
|
global_elements.into_bump_slice()
|
||||||
|
} else {
|
||||||
|
&global_elements[zero_elements..]
|
||||||
|
};
|
||||||
|
|
||||||
|
// use None for the address space (e.g. Const does not work)
|
||||||
|
let typ = element_type.array_type(const_elements.len() as u32);
|
||||||
|
let global = env.module.add_global(typ, None, "roc__list_literal");
|
||||||
|
|
||||||
|
global.set_constant(true);
|
||||||
|
global.set_alignment(alignment);
|
||||||
|
global.set_unnamed_addr(true);
|
||||||
|
global.set_linkage(inkwell::module::Linkage::Private);
|
||||||
|
|
||||||
|
global.set_initializer(&element_type.const_array(const_elements));
|
||||||
|
global.as_pointer_value()
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_all_constant {
|
||||||
|
// all elements are constants, so we can use the memory in the constants section directly
|
||||||
|
// here we make a pointer to the first actual element (skipping the 0 bytes that
|
||||||
|
// represent the refcount)
|
||||||
|
let zero = env.ptr_int().const_zero();
|
||||||
|
let offset = env.ptr_int().const_int(zero_elements as _, false);
|
||||||
|
|
||||||
|
let ptr = unsafe {
|
||||||
|
env.builder
|
||||||
|
.build_in_bounds_gep(global, &[zero, offset], "first_element_pointer")
|
||||||
|
};
|
||||||
|
|
||||||
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
|
} else {
|
||||||
|
// some of our elements are non-constant, so we must allocate space on the heap
|
||||||
|
let ptr = allocate_list(env, elem_layout, list_length_intval);
|
||||||
|
|
||||||
|
// then, copy the relevant segment from the constant section into the heap
|
||||||
|
env.builder
|
||||||
|
.build_memcpy(
|
||||||
|
ptr,
|
||||||
|
alignment,
|
||||||
|
global,
|
||||||
|
alignment,
|
||||||
|
env.ptr_int().const_int(size as _, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// then replace the `undef`s with the values that we evaluate at runtime
|
||||||
|
for (index, val) in runtime_evaluated_elements {
|
||||||
|
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||||
|
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
||||||
|
|
||||||
|
builder.build_store(elem_ptr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ptr = allocate_list(env, elem_layout, list_length_intval);
|
||||||
|
|
||||||
|
// Copy the elements from the list literal into the array
|
||||||
|
for (index, element) in elems.iter().enumerate() {
|
||||||
|
let val = match element {
|
||||||
|
ListLiteralElement::Literal(literal) => {
|
||||||
|
build_exp_literal(env, elem_layout, literal)
|
||||||
|
}
|
||||||
|
ListLiteralElement::Symbol(symbol) => load_symbol(scope, symbol),
|
||||||
|
};
|
||||||
|
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||||
|
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
||||||
|
|
||||||
|
builder.build_store(elem_ptr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
}
|
}
|
||||||
|
|
||||||
let u8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
let generic_ptr = builder.build_bitcast(ptr, u8_ptr_type, "to_generic_ptr");
|
|
||||||
|
|
||||||
let struct_type = super::convert::zig_list_type(env);
|
|
||||||
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(len_u64, false));
|
|
||||||
let mut struct_val;
|
|
||||||
|
|
||||||
// Store the pointer
|
|
||||||
struct_val = builder
|
|
||||||
.build_insert_value(
|
|
||||||
struct_type.get_undef(),
|
|
||||||
generic_ptr,
|
|
||||||
Builtin::WRAPPER_PTR,
|
|
||||||
"insert_ptr_list_literal",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Store the length
|
|
||||||
struct_val = builder
|
|
||||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Bitcast to an array of raw bytes
|
|
||||||
builder.build_bitcast(
|
|
||||||
struct_val.into_struct_value(),
|
|
||||||
super::convert::zig_list_type(env),
|
|
||||||
"cast_collection",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrement_with_size_check<'a, 'ctx, 'env>(
|
fn decrement_with_size_check<'a, 'ctx, 'env>(
|
||||||
|
|
29
compiler/gen_wasm/Cargo.toml
Normal file
29
compiler/gen_wasm/Cargo.toml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "roc_gen_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
roc_mono = { path = "../mono" }
|
||||||
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
parity-wasm = "0.42"
|
||||||
|
|
||||||
|
roc_std = { path = "../../roc_std" }
|
||||||
|
wasmer = "2.0.0"
|
||||||
|
wasmer-wasi = "2.0.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
roc_can = { path = "../can" }
|
||||||
|
roc_builtins = { path = "../builtins" }
|
||||||
|
roc_load = { path = "../load" }
|
||||||
|
roc_types = { path = "../types" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
indoc = "0.3.3"
|
||||||
|
pretty_assertions = "0.5.1"
|
||||||
|
libc = "0.2"
|
||||||
|
target-lexicon = "0.12.2"
|
||||||
|
tempfile = "3.1.0"
|
210
compiler/gen_wasm/src/from_wasm32_memory.rs
Normal file
210
compiler/gen_wasm/src/from_wasm32_memory.rs
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||||
|
|
||||||
|
pub trait FromWasm32Memory: Sized {
|
||||||
|
const SIZE_OF_WASM: usize;
|
||||||
|
const ALIGN_OF_WASM: usize;
|
||||||
|
const ACTUAL_WIDTH: usize = if (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM) == 0 {
|
||||||
|
Self::SIZE_OF_WASM
|
||||||
|
} else {
|
||||||
|
Self::SIZE_OF_WASM + (Self::ALIGN_OF_WASM - (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM))
|
||||||
|
};
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! from_wasm_memory_primitive_decode {
|
||||||
|
($type_name:ident) => {
|
||||||
|
const SIZE_OF_WASM: usize = core::mem::size_of::<$type_name>();
|
||||||
|
const ALIGN_OF_WASM: usize = core::mem::align_of::<$type_name>();
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
let mut output: MaybeUninit<Self> = MaybeUninit::uninit();
|
||||||
|
let width = std::mem::size_of::<Self>();
|
||||||
|
|
||||||
|
let ptr = output.as_mut_ptr();
|
||||||
|
let raw_ptr = ptr as *mut u8;
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts_mut(raw_ptr, width) };
|
||||||
|
|
||||||
|
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset as u32);
|
||||||
|
let foobar = (ptr.deref(memory, 0, width as u32)).unwrap();
|
||||||
|
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||||
|
|
||||||
|
slice.copy_from_slice(wasm_slice);
|
||||||
|
|
||||||
|
unsafe { output.assume_init() }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! from_wasm_memory_primitive {
|
||||||
|
($($type_name:ident ,)+) => {
|
||||||
|
$(
|
||||||
|
impl FromWasm32Memory for $type_name {
|
||||||
|
from_wasm_memory_primitive_decode!($type_name);
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_wasm_memory_primitive!(
|
||||||
|
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl FromWasm32Memory for () {
|
||||||
|
const SIZE_OF_WASM: usize = 0;
|
||||||
|
const ALIGN_OF_WASM: usize = 0;
|
||||||
|
|
||||||
|
fn decode(_: &wasmer::Memory, _: u32) -> Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasm32Memory for RocStr {
|
||||||
|
const SIZE_OF_WASM: usize = 8;
|
||||||
|
const ALIGN_OF_WASM: usize = 4;
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
let bytes = <u64 as FromWasm32Memory>::decode(memory, offset);
|
||||||
|
|
||||||
|
let length = (bytes >> 32) as u32;
|
||||||
|
let elements = bytes as u32;
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
RocStr::default()
|
||||||
|
} else if (length as i32) < 0 {
|
||||||
|
// this is a small string
|
||||||
|
let last_byte = bytes.to_ne_bytes()[7];
|
||||||
|
let actual_length = (last_byte ^ 0b1000_0000) as usize;
|
||||||
|
|
||||||
|
let slice = &bytes.to_ne_bytes()[..actual_length as usize];
|
||||||
|
RocStr::from_slice(slice)
|
||||||
|
} else {
|
||||||
|
// this is a big string
|
||||||
|
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
||||||
|
let foobar = (ptr.deref(memory, 0, length)).unwrap();
|
||||||
|
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||||
|
|
||||||
|
RocStr::from_slice(wasm_slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T> {
|
||||||
|
const SIZE_OF_WASM: usize = 8;
|
||||||
|
const ALIGN_OF_WASM: usize = 4;
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
let bytes = <u64 as FromWasm32Memory>::decode(memory, offset);
|
||||||
|
|
||||||
|
let length = (bytes >> 32) as u32;
|
||||||
|
let elements = bytes as u32;
|
||||||
|
|
||||||
|
let mut items = Vec::with_capacity(length as usize);
|
||||||
|
|
||||||
|
for i in 0..length {
|
||||||
|
let item = <T as FromWasm32Memory>::decode(
|
||||||
|
memory,
|
||||||
|
elements + i * <T as FromWasm32Memory>::SIZE_OF_WASM as u32,
|
||||||
|
);
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
RocList::from_slice(&items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromWasm32Memory> FromWasm32Memory for &'_ T {
|
||||||
|
const SIZE_OF_WASM: usize = 4;
|
||||||
|
const ALIGN_OF_WASM: usize = 4;
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
let elements = <u32 as FromWasm32Memory>::decode(memory, offset);
|
||||||
|
|
||||||
|
let actual = <T as FromWasm32Memory>::decode(memory, elements);
|
||||||
|
|
||||||
|
let b = Box::new(actual);
|
||||||
|
|
||||||
|
std::boxed::Box::<T>::leak(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromWasm32Memory + Clone, const N: usize> FromWasm32Memory for [T; N] {
|
||||||
|
const SIZE_OF_WASM: usize = N * T::SIZE_OF_WASM;
|
||||||
|
const ALIGN_OF_WASM: usize = T::ALIGN_OF_WASM;
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset);
|
||||||
|
let width = <T as FromWasm32Memory>::SIZE_OF_WASM as u32 * N as u32;
|
||||||
|
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
||||||
|
let wasm_slice: &[T; N] = unsafe { &*(foobar as *const _ as *const [T; N]) };
|
||||||
|
|
||||||
|
wasm_slice.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasm32Memory for usize {
|
||||||
|
const SIZE_OF_WASM: usize = 4;
|
||||||
|
const ALIGN_OF_WASM: usize = 4;
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
<u32 as FromWasm32Memory>::decode(memory, offset) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromWasm32Memory, U: FromWasm32Memory> FromWasm32Memory for (T, U) {
|
||||||
|
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM;
|
||||||
|
const ALIGN_OF_WASM: usize = max2(T::SIZE_OF_WASM, U::SIZE_OF_WASM);
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
debug_assert!(
|
||||||
|
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||||
|
"this function does not handle alignment"
|
||||||
|
);
|
||||||
|
|
||||||
|
let t = <T as FromWasm32Memory>::decode(memory, offset);
|
||||||
|
|
||||||
|
let u = <U as FromWasm32Memory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||||
|
|
||||||
|
(t, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn max2(a: usize, b: usize) -> usize {
|
||||||
|
if a > b {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn max3(a: usize, b: usize, c: usize) -> usize {
|
||||||
|
max2(max2(a, b), c)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromWasm32Memory, U: FromWasm32Memory, V: FromWasm32Memory> FromWasm32Memory for (T, U, V) {
|
||||||
|
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM + V::SIZE_OF_WASM;
|
||||||
|
const ALIGN_OF_WASM: usize = max3(T::SIZE_OF_WASM, U::SIZE_OF_WASM, V::SIZE_OF_WASM);
|
||||||
|
|
||||||
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
|
debug_assert!(
|
||||||
|
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||||
|
"this function does not handle alignment"
|
||||||
|
);
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
U::ALIGN_OF_WASM >= V::ALIGN_OF_WASM,
|
||||||
|
"this function does not handle alignment"
|
||||||
|
);
|
||||||
|
|
||||||
|
let t = <T as FromWasm32Memory>::decode(memory, offset);
|
||||||
|
|
||||||
|
let u = <U as FromWasm32Memory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||||
|
|
||||||
|
let v = <V as FromWasm32Memory>::decode(
|
||||||
|
memory,
|
||||||
|
offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
(t, u, v)
|
||||||
|
}
|
||||||
|
}
|
1
compiler/gen_wasm/src/lib.rs
Normal file
1
compiler/gen_wasm/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod from_wasm32_memory;
|
|
@ -9,7 +9,7 @@ use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::ir::{Call, CallType, Expr, Literal, ModifyRc, Proc, Stmt};
|
use crate::ir::{Call, CallType, Expr, ListLiteralElement, Literal, ModifyRc, Proc, Stmt};
|
||||||
use crate::layout::{Builtin, Layout, ListLayout, UnionLayout};
|
use crate::layout::{Builtin, Layout, ListLayout, UnionLayout};
|
||||||
|
|
||||||
// just using one module for now
|
// just using one module for now
|
||||||
|
@ -1118,8 +1118,12 @@ fn expr_spec<'a>(
|
||||||
|
|
||||||
let mut bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let mut bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
for symbol in elems.iter() {
|
for element in elems.iter() {
|
||||||
let value_id = env.symbols[symbol];
|
let value_id = if let ListLiteralElement::Symbol(symbol) = element {
|
||||||
|
env.symbols[symbol]
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[]).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
bag = builder.add_bag_insert(block, bag, value_id)?;
|
bag = builder.add_bag_insert(block, bag, value_id)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -740,7 +740,15 @@ impl<'a> BorrowInfState<'a> {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
|
|
||||||
match e {
|
match e {
|
||||||
Tag { arguments: xs, .. } | Struct(xs) | Array { elems: xs, .. } => {
|
Array { elems: xs, .. } => {
|
||||||
|
let xs = Vec::from_iter_in(xs.iter().filter_map(|e| e.to_symbol()), self.arena);
|
||||||
|
self.own_var(z);
|
||||||
|
|
||||||
|
// if the used symbol is an argument to the current function,
|
||||||
|
// the function must take it as an owned parameter
|
||||||
|
self.own_args_if_param(&xs);
|
||||||
|
}
|
||||||
|
Tag { arguments: xs, .. } | Struct(xs) => {
|
||||||
self.own_var(z);
|
self.own_var(z);
|
||||||
|
|
||||||
// if the used symbol is an argument to the current function,
|
// if the used symbol is an argument to the current function,
|
||||||
|
|
|
@ -95,11 +95,11 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
|
|
||||||
Call(call) => occurring_variables_call(call, result),
|
Call(call) => occurring_variables_call(call, result),
|
||||||
|
|
||||||
Tag { arguments, .. }
|
Array {
|
||||||
| Struct(arguments)
|
|
||||||
| Array {
|
|
||||||
elems: arguments, ..
|
elems: arguments, ..
|
||||||
} => {
|
} => result.extend(arguments.iter().filter_map(|e| e.to_symbol())),
|
||||||
|
|
||||||
|
Tag { arguments, .. } | Struct(arguments) => {
|
||||||
result.extend(arguments.iter().copied());
|
result.extend(arguments.iter().copied());
|
||||||
}
|
}
|
||||||
Reuse {
|
Reuse {
|
||||||
|
@ -733,14 +733,21 @@ impl<'a> Context<'a> {
|
||||||
live_vars.remove(&z);
|
live_vars.remove(&z);
|
||||||
|
|
||||||
let new_b = match v {
|
let new_b = match v {
|
||||||
Reuse { arguments: ys, .. }
|
Reuse { arguments: ys, .. } | Tag { arguments: ys, .. } | Struct(ys) => self
|
||||||
| Tag { arguments: ys, .. }
|
.add_inc_before_consume_all(
|
||||||
| Struct(ys)
|
ys,
|
||||||
| Array { elems: ys, .. } => self.add_inc_before_consume_all(
|
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
||||||
ys,
|
b_live_vars,
|
||||||
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
),
|
||||||
b_live_vars,
|
|
||||||
),
|
Array { elems, .. } => {
|
||||||
|
let ys = Vec::from_iter_in(elems.iter().filter_map(|e| e.to_symbol()), self.arena);
|
||||||
|
self.add_inc_before_consume_all(
|
||||||
|
&ys,
|
||||||
|
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
||||||
|
b_live_vars,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Call(crate::ir::Call {
|
Call(crate::ir::Call {
|
||||||
call_type,
|
call_type,
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ impl ModifyRc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Literal<'a> {
|
pub enum Literal<'a> {
|
||||||
// Literals
|
// Literals
|
||||||
Int(i128),
|
Int(i128),
|
||||||
|
@ -1038,6 +1038,21 @@ pub enum Literal<'a> {
|
||||||
Byte(u8),
|
Byte(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ListLiteralElement<'a> {
|
||||||
|
Literal(Literal<'a>),
|
||||||
|
Symbol(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ListLiteralElement<'a> {
|
||||||
|
pub fn to_symbol(&self) -> Option<Symbol> {
|
||||||
|
match self {
|
||||||
|
Self::Symbol(s) => Some(*s),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Call<'a> {
|
pub struct Call<'a> {
|
||||||
pub call_type: CallType<'a>,
|
pub call_type: CallType<'a>,
|
||||||
|
@ -1177,7 +1192,7 @@ pub enum Expr<'a> {
|
||||||
|
|
||||||
Array {
|
Array {
|
||||||
elem_layout: Layout<'a>,
|
elem_layout: Layout<'a>,
|
||||||
elems: &'a [Symbol],
|
elems: &'a [ListLiteralElement<'a>],
|
||||||
},
|
},
|
||||||
EmptyArray,
|
EmptyArray,
|
||||||
|
|
||||||
|
@ -1317,7 +1332,10 @@ impl<'a> Expr<'a> {
|
||||||
.append(alloc.text("}"))
|
.append(alloc.text("}"))
|
||||||
}
|
}
|
||||||
Array { elems, .. } => {
|
Array { elems, .. } => {
|
||||||
let it = elems.iter().map(|s| symbol_to_doc(alloc, *s));
|
let it = elems.iter().map(|e| match e {
|
||||||
|
ListLiteralElement::Literal(l) => l.to_doc(alloc),
|
||||||
|
ListLiteralElement::Symbol(s) => symbol_to_doc(alloc, *s),
|
||||||
|
});
|
||||||
|
|
||||||
alloc
|
alloc
|
||||||
.text("Array [")
|
.text("Array [")
|
||||||
|
@ -2728,6 +2746,63 @@ macro_rules! match_on_closure_argument {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_make_literal<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
can_expr: &roc_can::expr::Expr,
|
||||||
|
) -> Option<Literal<'a>> {
|
||||||
|
use roc_can::expr::Expr::*;
|
||||||
|
|
||||||
|
match can_expr {
|
||||||
|
Int(_, precision, _, int) => {
|
||||||
|
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, false) {
|
||||||
|
IntOrFloat::SignedIntType(_) | IntOrFloat::UnsignedIntType(_) => {
|
||||||
|
Some(Literal::Int(*int))
|
||||||
|
}
|
||||||
|
_ => unreachable!("unexpected float precision for integer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Float(_, precision, float_str, float) => {
|
||||||
|
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, true) {
|
||||||
|
IntOrFloat::BinaryFloatType(_) => Some(Literal::Float(*float)),
|
||||||
|
IntOrFloat::DecimalFloatType => {
|
||||||
|
let dec = match RocDec::from_str(float_str) {
|
||||||
|
Some(d) => d,
|
||||||
|
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Literal::Decimal(dec))
|
||||||
|
}
|
||||||
|
_ => unreachable!("unexpected float precision for integer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO investigate lifetime trouble
|
||||||
|
// Str(string) => Some(Literal::Str(env.arena.alloc(string))),
|
||||||
|
Num(var, num_str, num) => {
|
||||||
|
// first figure out what kind of number this is
|
||||||
|
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
|
||||||
|
IntOrFloat::SignedIntType(_) | IntOrFloat::UnsignedIntType(_) => {
|
||||||
|
Some(Literal::Int((*num).into()))
|
||||||
|
}
|
||||||
|
IntOrFloat::BinaryFloatType(_) => Some(Literal::Float(*num as f64)),
|
||||||
|
IntOrFloat::DecimalFloatType => {
|
||||||
|
let dec = match RocDec::from_str(num_str) {
|
||||||
|
Some(d) => d,
|
||||||
|
None => panic!(
|
||||||
|
r"Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message",
|
||||||
|
num_str
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Literal::Decimal(dec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_hole<'a>(
|
pub fn with_hole<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
@ -3423,8 +3498,20 @@ pub fn with_hole<'a>(
|
||||||
loc_elems,
|
loc_elems,
|
||||||
} => {
|
} => {
|
||||||
let mut arg_symbols = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
let mut arg_symbols = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
||||||
for arg_expr in loc_elems.iter() {
|
let mut elements = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
||||||
arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr.value));
|
|
||||||
|
let mut symbol_exprs = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
||||||
|
|
||||||
|
for arg_expr in loc_elems.into_iter() {
|
||||||
|
if let Some(literal) = try_make_literal(env, &arg_expr.value) {
|
||||||
|
elements.push(ListLiteralElement::Literal(literal));
|
||||||
|
} else {
|
||||||
|
let symbol = possible_reuse_symbol(env, procs, &arg_expr.value);
|
||||||
|
|
||||||
|
elements.push(ListLiteralElement::Symbol(symbol));
|
||||||
|
arg_symbols.push(symbol);
|
||||||
|
symbol_exprs.push(arg_expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let arg_symbols = arg_symbols.into_bump_slice();
|
let arg_symbols = arg_symbols.into_bump_slice();
|
||||||
|
|
||||||
|
@ -3434,7 +3521,7 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
let expr = Expr::Array {
|
let expr = Expr::Array {
|
||||||
elem_layout,
|
elem_layout,
|
||||||
elems: arg_symbols,
|
elems: elements.into_bump_slice(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stmt = Stmt::Let(
|
let stmt = Stmt::Let(
|
||||||
|
@ -3444,7 +3531,7 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
);
|
);
|
||||||
|
|
||||||
let iter = loc_elems
|
let iter = symbol_exprs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|e| (elem_var, e))
|
.map(|e| (elem_var, e))
|
||||||
|
@ -5485,11 +5572,17 @@ fn substitute_in_expr<'a>(
|
||||||
} => {
|
} => {
|
||||||
let mut did_change = false;
|
let mut did_change = false;
|
||||||
let new_args = Vec::from_iter_in(
|
let new_args = Vec::from_iter_in(
|
||||||
args.iter().map(|s| match substitute(subs, *s) {
|
args.iter().map(|e| {
|
||||||
None => *s,
|
if let ListLiteralElement::Symbol(s) = e {
|
||||||
Some(s) => {
|
match substitute(subs, *s) {
|
||||||
did_change = true;
|
None => ListLiteralElement::Symbol(*s),
|
||||||
s
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
ListLiteralElement::Symbol(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*e
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
arena,
|
arena,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveVarSet};
|
use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveVarSet};
|
||||||
use crate::ir::{BranchInfo, Call, Expr, Proc, Stmt};
|
use crate::ir::{BranchInfo, Call, Expr, ListLiteralElement, Proc, Stmt};
|
||||||
use crate::layout::{Layout, UnionLayout};
|
use crate::layout::{Layout, UnionLayout};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -564,8 +564,18 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal(_) => false,
|
Expr::Literal(_) => false,
|
||||||
Expr::Call(call) => has_live_var_call(call, needle),
|
Expr::Call(call) => has_live_var_call(call, needle),
|
||||||
Expr::Array { elems: fields, .. }
|
Expr::Array { elems: fields, .. } => {
|
||||||
| Expr::Tag {
|
for element in fields.iter() {
|
||||||
|
if let ListLiteralElement::Symbol(s) = element {
|
||||||
|
if *s == needle {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Expr::Tag {
|
||||||
arguments: fields, ..
|
arguments: fields, ..
|
||||||
}
|
}
|
||||||
| Expr::Struct(fields) => fields.iter().any(|s| *s == needle),
|
| Expr::Struct(fields) => fields.iter().any(|s| *s == needle),
|
||||||
|
|
|
@ -23,6 +23,7 @@ roc_can = { path = "../can" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_build = { path = "../build" }
|
roc_build = { path = "../build" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
|
roc_gen_wasm = { path = "../gen_wasm" }
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
|
|
@ -6,9 +6,9 @@ use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_can::def::Def;
|
use roc_can::def::Def;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
|
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
|
||||||
use roc_types::subs::VarStore;
|
use roc_types::subs::VarStore;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
|
@ -366,29 +366,6 @@ pub fn helper_wasm<'a>(
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
// Command::new("/opt/wasi-sdk/bin/clang")
|
|
||||||
|
|
||||||
/*
|
|
||||||
Command::new("zig")
|
|
||||||
.current_dir(dir_path)
|
|
||||||
.args(&[
|
|
||||||
"cc",
|
|
||||||
"/home/folkertdev/roc/wasm/libmain.a",
|
|
||||||
test_a_path.to_str().unwrap(),
|
|
||||||
"-target",
|
|
||||||
"wasm32-wasi",
|
|
||||||
"-o",
|
|
||||||
test_wasm_path.to_str().unwrap(),
|
|
||||||
"--sysroot=/opt/wasi-sdk/share/wasi-sysroot/",
|
|
||||||
"-Xlinker", "--export-dynamic",
|
|
||||||
// "-Xlinker", "--allow-undefined"
|
|
||||||
// "--global-cache-dir",
|
|
||||||
// zig_global_cache_path.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
*/
|
|
||||||
|
|
||||||
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
||||||
.current_dir(dir_path)
|
.current_dir(dir_path)
|
||||||
.args(&[
|
.args(&[
|
||||||
|
@ -405,24 +382,6 @@ pub fn helper_wasm<'a>(
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
/*
|
|
||||||
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
|
||||||
.current_dir(dir_path)
|
|
||||||
.args(&[
|
|
||||||
"build-lib",
|
|
||||||
"/home/folkertdev/roc/wasm/libmain.a",
|
|
||||||
test_a_path.to_str().unwrap(),
|
|
||||||
"-target",
|
|
||||||
"wasm32-wasi",
|
|
||||||
"-dynamic",
|
|
||||||
"-lc",
|
|
||||||
// "--global-cache-dir",
|
|
||||||
// zig_global_cache_path.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
*/
|
|
||||||
|
|
||||||
// now, do wasmer stuff
|
// now, do wasmer stuff
|
||||||
|
|
||||||
use wasmer::{Function, Instance, Module, Store};
|
use wasmer::{Function, Instance, Module, Store};
|
||||||
|
@ -500,7 +459,7 @@ fn fake_wasm_main_function(_: u32, _: u32) -> u32 {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
|
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
|
||||||
where
|
where
|
||||||
T: FromWasmMemory,
|
T: FromWasm32Memory,
|
||||||
{
|
{
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
let context = inkwell::context::Context::create();
|
let context = inkwell::context::Context::create();
|
||||||
|
@ -534,7 +493,7 @@ where
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = <T as crate::helpers::eval::FromWasmMemory>::decode(
|
let output = <T as crate::helpers::eval::FromWasm32Memory>::decode(
|
||||||
memory,
|
memory,
|
||||||
// skip the RocCallResult tag id
|
// skip the RocCallResult tag id
|
||||||
address as u32 + 8,
|
address as u32 + 8,
|
||||||
|
@ -643,232 +602,3 @@ macro_rules! assert_non_opt_evals_to {
|
||||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromWasmMemory: Sized {
|
|
||||||
const SIZE_OF_WASM: usize;
|
|
||||||
const ALIGN_OF_WASM: usize;
|
|
||||||
const ACTUAL_WIDTH: usize = if (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM) == 0 {
|
|
||||||
Self::SIZE_OF_WASM
|
|
||||||
} else {
|
|
||||||
Self::SIZE_OF_WASM + (Self::ALIGN_OF_WASM - (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM))
|
|
||||||
};
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! from_wasm_memory_primitive_decode {
|
|
||||||
($type_name:ident) => {
|
|
||||||
const SIZE_OF_WASM: usize = core::mem::size_of::<$type_name>();
|
|
||||||
const ALIGN_OF_WASM: usize = core::mem::align_of::<$type_name>();
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
|
|
||||||
let mut output: MaybeUninit<Self> = MaybeUninit::uninit();
|
|
||||||
let width = std::mem::size_of::<Self>();
|
|
||||||
|
|
||||||
let ptr = output.as_mut_ptr();
|
|
||||||
let raw_ptr = ptr as *mut u8;
|
|
||||||
let slice = unsafe { std::slice::from_raw_parts_mut(raw_ptr, width) };
|
|
||||||
|
|
||||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset as u32);
|
|
||||||
let foobar = (ptr.deref(memory, 0, width as u32)).unwrap();
|
|
||||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
|
||||||
|
|
||||||
slice.copy_from_slice(wasm_slice);
|
|
||||||
|
|
||||||
unsafe { output.assume_init() }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! from_wasm_memory_primitive {
|
|
||||||
($($type_name:ident ,)+) => {
|
|
||||||
$(
|
|
||||||
impl FromWasmMemory for $type_name {
|
|
||||||
from_wasm_memory_primitive_decode!($type_name);
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
from_wasm_memory_primitive!(
|
|
||||||
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl FromWasmMemory for () {
|
|
||||||
const SIZE_OF_WASM: usize = 0;
|
|
||||||
const ALIGN_OF_WASM: usize = 0;
|
|
||||||
|
|
||||||
fn decode(_: &wasmer::Memory, _: u32) -> Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmMemory for RocStr {
|
|
||||||
const SIZE_OF_WASM: usize = 8;
|
|
||||||
const ALIGN_OF_WASM: usize = 4;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let length = (bytes >> 32) as u32;
|
|
||||||
let elements = bytes as u32;
|
|
||||||
|
|
||||||
if length == 0 {
|
|
||||||
RocStr::default()
|
|
||||||
} else if (length as i32) < 0 {
|
|
||||||
// this is a small string
|
|
||||||
let last_byte = bytes.to_ne_bytes()[7];
|
|
||||||
let actual_length = (last_byte ^ 0b1000_0000) as usize;
|
|
||||||
|
|
||||||
let slice = &bytes.to_ne_bytes()[..actual_length as usize];
|
|
||||||
RocStr::from_slice(slice)
|
|
||||||
} else {
|
|
||||||
// this is a big string
|
|
||||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
|
||||||
let foobar = (ptr.deref(memory, 0, length)).unwrap();
|
|
||||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
|
||||||
|
|
||||||
RocStr::from_slice(wasm_slice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory + Clone> FromWasmMemory for RocList<T> {
|
|
||||||
const SIZE_OF_WASM: usize = 8;
|
|
||||||
const ALIGN_OF_WASM: usize = 4;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let length = (bytes >> 32) as u32;
|
|
||||||
let elements = bytes as u32;
|
|
||||||
|
|
||||||
let mut items = Vec::with_capacity(length as usize);
|
|
||||||
|
|
||||||
for i in 0..length {
|
|
||||||
let item = <T as FromWasmMemory>::decode(
|
|
||||||
memory,
|
|
||||||
elements + i * <T as FromWasmMemory>::SIZE_OF_WASM as u32,
|
|
||||||
);
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
RocList::from_slice(&items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory + Clone> FromWasmMemory for &'_ [T] {
|
|
||||||
const SIZE_OF_WASM: usize = 8;
|
|
||||||
const ALIGN_OF_WASM: usize = 4;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let length = (bytes >> 32) as u32;
|
|
||||||
let elements = bytes as u32;
|
|
||||||
|
|
||||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
|
||||||
let width = <T as FromWasmMemory>::SIZE_OF_WASM as u32 * length;
|
|
||||||
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
|
||||||
let wasm_slice =
|
|
||||||
unsafe { std::slice::from_raw_parts(foobar as *const _ as *const _, length as usize) };
|
|
||||||
|
|
||||||
wasm_slice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory> FromWasmMemory for &'_ T {
|
|
||||||
const SIZE_OF_WASM: usize = 4;
|
|
||||||
const ALIGN_OF_WASM: usize = 4;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
let elements = <u32 as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let actual = <T as FromWasmMemory>::decode(memory, elements);
|
|
||||||
|
|
||||||
let b = Box::new(actual);
|
|
||||||
|
|
||||||
std::boxed::Box::<T>::leak(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory + Clone, const N: usize> FromWasmMemory for [T; N] {
|
|
||||||
const SIZE_OF_WASM: usize = N * T::SIZE_OF_WASM;
|
|
||||||
const ALIGN_OF_WASM: usize = T::ALIGN_OF_WASM;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset);
|
|
||||||
let width = <T as FromWasmMemory>::SIZE_OF_WASM as u32 * N as u32;
|
|
||||||
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
|
||||||
let wasm_slice: &[T; N] = unsafe { &*(foobar as *const _ as *const [T; N]) };
|
|
||||||
|
|
||||||
wasm_slice.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmMemory for usize {
|
|
||||||
const SIZE_OF_WASM: usize = 4;
|
|
||||||
const ALIGN_OF_WASM: usize = 4;
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
<u32 as FromWasmMemory>::decode(memory, offset) as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory, U: FromWasmMemory> FromWasmMemory for (T, U) {
|
|
||||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM;
|
|
||||||
const ALIGN_OF_WASM: usize = max2(T::SIZE_OF_WASM, U::SIZE_OF_WASM);
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
assert!(
|
|
||||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
|
||||||
"this function does not handle alignment"
|
|
||||||
);
|
|
||||||
|
|
||||||
let t = <T as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let u = <U as FromWasmMemory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
|
||||||
|
|
||||||
(t, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn max2(a: usize, b: usize) -> usize {
|
|
||||||
if a > b {
|
|
||||||
a
|
|
||||||
} else {
|
|
||||||
b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn max3(a: usize, b: usize, c: usize) -> usize {
|
|
||||||
max2(max2(a, b), c)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromWasmMemory, U: FromWasmMemory, V: FromWasmMemory> FromWasmMemory for (T, U, V) {
|
|
||||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM + V::SIZE_OF_WASM;
|
|
||||||
const ALIGN_OF_WASM: usize = max3(T::SIZE_OF_WASM, U::SIZE_OF_WASM, V::SIZE_OF_WASM);
|
|
||||||
|
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
|
||||||
assert!(
|
|
||||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
|
||||||
"this function does not handle alignment"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
U::ALIGN_OF_WASM >= V::ALIGN_OF_WASM,
|
|
||||||
"this function does not handle alignment"
|
|
||||||
);
|
|
||||||
|
|
||||||
let t = <T as FromWasmMemory>::decode(memory, offset);
|
|
||||||
|
|
||||||
let u = <U as FromWasmMemory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
|
||||||
|
|
||||||
let v = <V as FromWasmMemory>::decode(
|
|
||||||
memory,
|
|
||||||
offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
(t, u, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,14 +3,8 @@ procedure Test.1 (Test.2, Test.3):
|
||||||
ret Test.2;
|
ret Test.2;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.11 = 1i64;
|
let Test.5 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.12 = 2i64;
|
let Test.6 = Array [3i64, 2i64, 1i64];
|
||||||
let Test.13 = 3i64;
|
|
||||||
let Test.5 = Array [Test.11, Test.12, Test.13];
|
|
||||||
let Test.8 = 3i64;
|
|
||||||
let Test.9 = 2i64;
|
|
||||||
let Test.10 = 1i64;
|
|
||||||
let Test.6 = Array [Test.8, Test.9, Test.10];
|
|
||||||
let Test.4 = CallByName Test.1 Test.5 Test.6;
|
let Test.4 = CallByName Test.1 Test.5 Test.6;
|
||||||
dec Test.6;
|
dec Test.6;
|
||||||
dec Test.5;
|
dec Test.5;
|
||||||
|
|
|
@ -7,9 +7,7 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.11 = 1i64;
|
let Test.1 = Array [1i64, 2i64];
|
||||||
let Test.12 = 2i64;
|
|
||||||
let Test.1 = Array [Test.11, Test.12];
|
|
||||||
let Test.9 = 5i64;
|
let Test.9 = 5i64;
|
||||||
let Test.10 = 4i64;
|
let Test.10 = 4i64;
|
||||||
let Test.7 = CallByName Num.24 Test.9 Test.10;
|
let Test.7 = CallByName Num.24 Test.9 Test.10;
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.6 = 1i64;
|
let Test.4 = Array [1i64, 3i64, 4i64];
|
||||||
let Test.7 = 3i64;
|
|
||||||
let Test.8 = 4i64;
|
|
||||||
let Test.4 = Array [Test.6, Test.7, Test.8];
|
|
||||||
let Test.5 = 3.14f64;
|
let Test.5 = 3.14f64;
|
||||||
let Test.3 = Struct {Test.4, Test.5};
|
let Test.3 = Struct {Test.4, Test.5};
|
||||||
let Test.1 = StructAtIndex 0 Test.3;
|
let Test.1 = StructAtIndex 0 Test.3;
|
||||||
|
|
|
@ -3,8 +3,7 @@ procedure List.5 (#Attr.2, #Attr.3):
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.5 = 1i64;
|
let Test.2 = Array [1i64];
|
||||||
let Test.2 = Array [Test.5];
|
|
||||||
let Test.3 = 2i64;
|
let Test.3 = 2i64;
|
||||||
let Test.1 = CallByName List.5 Test.2 Test.3;
|
let Test.1 = CallByName List.5 Test.2 Test.3;
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
|
|
|
@ -8,8 +8,6 @@ procedure Test.1 (Test.2):
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.8 = 1i64;
|
let Test.4 = Array [1i64, 2i64];
|
||||||
let Test.9 = 2i64;
|
|
||||||
let Test.4 = Array [Test.8, Test.9];
|
|
||||||
let Test.3 = CallByName Test.1 Test.4;
|
let Test.3 = CallByName Test.1 Test.4;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let Test.22 = lowlevel ListLen #Attr.2;
|
let Test.19 = lowlevel ListLen #Attr.2;
|
||||||
let Test.20 = lowlevel NumLt #Attr.3 Test.22;
|
let Test.17 = lowlevel NumLt #Attr.3 Test.19;
|
||||||
if Test.20 then
|
if Test.17 then
|
||||||
let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
let Test.18 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret Test.21;
|
ret Test.18;
|
||||||
else
|
else
|
||||||
ret #Attr.2;
|
ret #Attr.2;
|
||||||
|
|
||||||
|
@ -16,23 +16,20 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.7;
|
ret Test.7;
|
||||||
|
|
||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.11 = 1i64;
|
let Test.10 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.12 = 2i64;
|
|
||||||
let Test.13 = 3i64;
|
|
||||||
let Test.10 = Array [Test.11, Test.12, Test.13];
|
|
||||||
ret Test.10;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
let Test.17 = 0i64;
|
let Test.14 = 0i64;
|
||||||
let Test.18 = 0i64;
|
let Test.15 = 0i64;
|
||||||
let Test.16 = CallByName List.4 Test.3 Test.17 Test.18;
|
let Test.13 = CallByName List.4 Test.3 Test.14 Test.15;
|
||||||
ret Test.16;
|
ret Test.13;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.15 = CallByName Test.1;
|
let Test.12 = CallByName Test.1;
|
||||||
let Test.14 = CallByName Test.2 Test.15;
|
let Test.11 = CallByName Test.2 Test.12;
|
||||||
let Test.5 = CallByName List.7 Test.14;
|
let Test.5 = CallByName List.7 Test.11;
|
||||||
dec Test.14;
|
dec Test.11;
|
||||||
let Test.8 = CallByName Test.1;
|
let Test.8 = CallByName Test.1;
|
||||||
let Test.6 = CallByName List.7 Test.8;
|
let Test.6 = CallByName List.7 Test.8;
|
||||||
dec Test.8;
|
dec Test.8;
|
||||||
|
|
|
@ -11,10 +11,7 @@ procedure List.3 (#Attr.2, #Attr.3):
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.14 = 1i64;
|
let Test.6 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.15 = 2i64;
|
|
||||||
let Test.16 = 3i64;
|
|
||||||
let Test.6 = Array [Test.14, Test.15, Test.16];
|
|
||||||
let Test.7 = 0i64;
|
let Test.7 = 0i64;
|
||||||
let Test.5 = CallByName List.3 Test.6 Test.7;
|
let Test.5 = CallByName List.3 Test.6 Test.7;
|
||||||
dec Test.6;
|
dec Test.6;
|
||||||
|
|
|
@ -11,12 +11,8 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 1i64;
|
let Test.1 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.11 = 2i64;
|
let Test.2 = Array [1f64];
|
||||||
let Test.12 = 3i64;
|
|
||||||
let Test.1 = Array [Test.10, Test.11, Test.12];
|
|
||||||
let Test.9 = 1f64;
|
|
||||||
let Test.2 = Array [Test.9];
|
|
||||||
let Test.4 = CallByName List.7 Test.1;
|
let Test.4 = CallByName List.7 Test.1;
|
||||||
dec Test.1;
|
dec Test.1;
|
||||||
let Test.5 = CallByName List.7 Test.2;
|
let Test.5 = CallByName List.7 Test.2;
|
||||||
|
|
|
@ -14,9 +14,6 @@ procedure Test.2 (Test.3):
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.12 = 1i64;
|
let Test.1 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.13 = 2i64;
|
|
||||||
let Test.14 = 3i64;
|
|
||||||
let Test.1 = Array [Test.12, Test.13, Test.14];
|
|
||||||
let Test.4 = CallByName Test.2 Test.1;
|
let Test.4 = CallByName Test.2 Test.1;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
|
@ -4,9 +4,6 @@ procedure Test.1 (Test.2):
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.7 = 1i64;
|
let Test.5 = Array [1i64, 2i64, 3i64];
|
||||||
let Test.8 = 2i64;
|
|
||||||
let Test.9 = 3i64;
|
|
||||||
let Test.5 = Array [Test.7, Test.8, Test.9];
|
|
||||||
let Test.4 = CallByName Test.1 Test.5;
|
let Test.4 = CallByName Test.1 Test.5;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
|
@ -56,8 +56,6 @@ procedure Test.1 (Test.2):
|
||||||
jump Test.26;
|
jump Test.26;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.39 = 1i64;
|
let Test.7 = Array [1i64, 2i64];
|
||||||
let Test.40 = 2i64;
|
|
||||||
let Test.7 = Array [Test.39, Test.40];
|
|
||||||
let Test.6 = CallByName Test.1 Test.7;
|
let Test.6 = CallByName Test.1 Test.7;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
|
@ -54,7 +54,6 @@ procedure Test.1 (Test.2, Test.3, Test.4):
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 0i64;
|
let Test.10 = 0i64;
|
||||||
let Test.11 = 0i64;
|
let Test.11 = 0i64;
|
||||||
let Test.40 = 1i64;
|
let Test.12 = Array [1i64];
|
||||||
let Test.12 = Array [Test.40];
|
|
||||||
let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12;
|
let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12;
|
||||||
ret Test.9;
|
ret Test.9;
|
||||||
|
|
|
@ -40,7 +40,7 @@ export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
var ptr = malloc(size);
|
var ptr = malloc(size);
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
stdout.print("alloc: {d} (alignment {d})\n", .{ ptr, alignment }) catch unreachable;
|
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
|
||||||
return ptr;
|
return ptr;
|
||||||
} else {
|
} else {
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
|
@ -48,8 +48,10 @@ export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||||
_ = old_size;
|
if (DEBUG) {
|
||||||
_ = alignment;
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue