diff --git a/Cargo.lock b/Cargo.lock index 76b426922a..b53b0d9b05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2570,6 +2570,12 @@ dependencies = [ "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]] name = "parking_lot" version = "0.11.1" @@ -3642,6 +3648,29 @@ dependencies = [ "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]] name = "roc_ident" version = "0.1.0" @@ -4348,6 +4377,7 @@ dependencies = [ "roc_collections", "roc_constrain", "roc_gen_llvm", + "roc_gen_wasm", "roc_load", "roc_module", "roc_mono", diff --git a/Cargo.toml b/Cargo.toml index 01b160bf9a..27242439f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "compiler/load", "compiler/gen_llvm", "compiler/gen_dev", + "compiler/gen_wasm", "compiler/build", "compiler/arena_pool", "compiler/test_gen", diff --git a/Earthfile b/Earthfile index 90a5fd6a25..29973ffe70 100644 --- a/Earthfile +++ b/Earthfile @@ -75,6 +75,10 @@ test-rust: RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc RUN --mount=type=cache,target=$SCCACHE_DIR \ 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: FROM +test-rust diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 101d0d87c7..a98ab09045 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -16,7 +16,8 @@ bench = false [features] 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, # it doesn't have LLVM installed. (Also, it doesn't need to do code gen.) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 5e20ecf4db..2b075bb660 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -108,7 +108,7 @@ mod cli_run { assert!(out.status.success()); } - #[cfg(feature = "wasm-cli-run")] + #[cfg(feature = "wasm32-cli-run")] fn check_wasm_output_with_stdin( file: &Path, stdin: &[&str], @@ -117,7 +117,7 @@ mod cli_run { expected_ending: &str, ) { 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()); if !compile_out.stderr.is_empty() { @@ -315,8 +315,8 @@ mod cli_run { )* - #[cfg(feature = "wasm-cli-run")] - mod wasm { + #[cfg(feature = "wasm32-cli-run")] + mod wasm32 { use super::*; $( #[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] #[cfg(not(debug_assertions))] 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 { use std::io::Write; use wasmer::{Instance, Module, Store}; diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 9041d277c0..a2c1fc40de 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -262,7 +262,7 @@ pub fn rebuild_host(target: &Triple, host_input_path: &Path) { &emit_bin, zig_host_src.to_str().unwrap(), zig_str_path.to_str().unwrap(), - "i386-linux-gnu", + "i386-linux-musl", ) } _ => panic!("Unsupported architecture {:?}", target.architecture), diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index d68d923183..260156b0a7 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -208,13 +208,7 @@ pub fn unsafeReallocate( new_length: usize, element_width: usize, ) [*]u8 { - const align_width: usize = blk: { - if (alignment > 8) { - break :blk (2 * @sizeOf(usize)); - } else { - break :blk @sizeOf(usize); - } - }; + const align_width: usize = std.math.max(alignment, @sizeOf(usize)); const old_width = align_width + old_length * element_width; const new_width = align_width + new_length * element_width; diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index d008440d67..73b63deee5 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -8,7 +8,9 @@ use roc_collections::all::{MutMap, MutSet}; use roc_module::ident::{ModuleName, TagName}; use roc_module::low_level::LowLevel; 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 target_lexicon::Triple; @@ -150,7 +152,7 @@ where match expr { Expr::Literal(lit) => { if self.env().lazy_literals { - self.literal_map().insert(*sym, lit.clone()); + self.literal_map().insert(*sym, *lit); } else { self.load_literal(sym, lit)?; } @@ -618,8 +620,10 @@ where owning_symbol.insert(*sym, *structure); } Expr::Array { elems, .. } => { - for sym in *elems { - self.set_last_seen(*sym, stmt, &owning_symbol); + for elem in *elems { + if let ListLiteralElement::Symbol(sym) = elem { + self.set_last_seen(*sym, stmt, &owning_symbol); + } } } Expr::Reuse { diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 322ec34de6..ebf0ecbc29 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -51,7 +51,10 @@ use roc_builtins::bitcode; use roc_collections::all::{ImMap, MutMap, MutSet}; use roc_module::low_level::LowLevel; 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 target_lexicon::{Architecture, OperatingSystem, Triple}; @@ -2157,57 +2160,141 @@ fn list_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, elem_layout: &Layout<'a>, - elems: &&[Symbol], + elems: &[ListLiteralElement], ) -> BasicValueEnum<'ctx> { let ctx = env.context; let builder = env.builder; - let len_u64 = elems.len() as u64; + let element_type = basic_type_from_layout(env, elem_layout); - let ptr = { - let len_type = env.ptr_int(); - let len = len_type.const_int(len_u64, false); + let list_length = elems.len(); + let list_length_intval = env.ptr_int().const_int(list_length as _, 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 - for (index, symbol) in elems.iter().enumerate() { - 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") }; + let mut is_all_constant = true; + let zero_elements = (env.ptr_bytes as f64 / element_width as f64).ceil() as usize; - 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>( diff --git a/compiler/gen_wasm/Cargo.toml b/compiler/gen_wasm/Cargo.toml new file mode 100644 index 0000000000..2804be6eab --- /dev/null +++ b/compiler/gen_wasm/Cargo.toml @@ -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" diff --git a/compiler/gen_wasm/src/from_wasm32_memory.rs b/compiler/gen_wasm/src/from_wasm32_memory.rs new file mode 100644 index 0000000000..db59b8ef95 --- /dev/null +++ b/compiler/gen_wasm/src/from_wasm32_memory.rs @@ -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 = MaybeUninit::uninit(); + let width = std::mem::size_of::(); + + 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 = 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 = ::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 = 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 FromWasm32Memory for RocList { + const SIZE_OF_WASM: usize = 8; + const ALIGN_OF_WASM: usize = 4; + + fn decode(memory: &wasmer::Memory, offset: u32) -> Self { + let bytes = ::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 = ::decode( + memory, + elements + i * ::SIZE_OF_WASM as u32, + ); + items.push(item); + } + + RocList::from_slice(&items) + } +} + +impl 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 = ::decode(memory, offset); + + let actual = ::decode(memory, elements); + + let b = Box::new(actual); + + std::boxed::Box::::leak(b) + } +} + +impl 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 = wasmer::WasmPtr::new(offset); + let width = ::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 { + ::decode(memory, offset) as usize + } +} + +impl 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 = ::decode(memory, offset); + + let u = ::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 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 = ::decode(memory, offset); + + let u = ::decode(memory, offset + T::ACTUAL_WIDTH as u32); + + let v = ::decode( + memory, + offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32, + ); + + (t, u, v) + } +} diff --git a/compiler/gen_wasm/src/lib.rs b/compiler/gen_wasm/src/lib.rs new file mode 100644 index 0000000000..b620f374d7 --- /dev/null +++ b/compiler/gen_wasm/src/lib.rs @@ -0,0 +1 @@ +pub mod from_wasm32_memory; diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 467169b9bb..229ed7cbee 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -9,7 +9,7 @@ use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; 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}; // 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)?; - for symbol in elems.iter() { - let value_id = env.symbols[symbol]; + for element in elems.iter() { + 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)?; } diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 419158288a..89d6ae2446 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -740,7 +740,15 @@ impl<'a> BorrowInfState<'a> { use Expr::*; 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); // if the used symbol is an argument to the current function, diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index e8fc6476a2..9622355cc7 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -95,11 +95,11 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet) { Call(call) => occurring_variables_call(call, result), - Tag { arguments, .. } - | Struct(arguments) - | Array { + Array { elems: arguments, .. - } => { + } => result.extend(arguments.iter().filter_map(|e| e.to_symbol())), + + Tag { arguments, .. } | Struct(arguments) => { result.extend(arguments.iter().copied()); } Reuse { @@ -733,14 +733,21 @@ impl<'a> Context<'a> { live_vars.remove(&z); let new_b = match v { - Reuse { arguments: ys, .. } - | Tag { arguments: ys, .. } - | Struct(ys) - | Array { elems: ys, .. } => self.add_inc_before_consume_all( - ys, - self.arena.alloc(Stmt::Let(z, v, l, b)), - b_live_vars, - ), + Reuse { arguments: ys, .. } | Tag { arguments: ys, .. } | Struct(ys) => self + .add_inc_before_consume_all( + ys, + 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_type, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index daade748e6..b39bb8b722 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1020,7 +1020,7 @@ impl ModifyRc { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Literal<'a> { // Literals Int(i128), @@ -1038,6 +1038,21 @@ pub enum Literal<'a> { 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 { + match self { + Self::Symbol(s) => Some(*s), + _ => None, + } + } +} + #[derive(Clone, Debug, PartialEq)] pub struct Call<'a> { pub call_type: CallType<'a>, @@ -1177,7 +1192,7 @@ pub enum Expr<'a> { Array { elem_layout: Layout<'a>, - elems: &'a [Symbol], + elems: &'a [ListLiteralElement<'a>], }, EmptyArray, @@ -1317,7 +1332,10 @@ impl<'a> Expr<'a> { .append(alloc.text("}")) } 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 .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> { + 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>( env: &mut Env<'a, '_>, can_expr: roc_can::expr::Expr, @@ -3423,8 +3498,20 @@ pub fn with_hole<'a>( loc_elems, } => { let mut arg_symbols = Vec::with_capacity_in(loc_elems.len(), env.arena); - for arg_expr in loc_elems.iter() { - arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr.value)); + let mut elements = Vec::with_capacity_in(loc_elems.len(), env.arena); + + 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(); @@ -3434,7 +3521,7 @@ pub fn with_hole<'a>( let expr = Expr::Array { elem_layout, - elems: arg_symbols, + elems: elements.into_bump_slice(), }; let stmt = Stmt::Let( @@ -3444,7 +3531,7 @@ pub fn with_hole<'a>( hole, ); - let iter = loc_elems + let iter = symbol_exprs .into_iter() .rev() .map(|e| (elem_var, e)) @@ -5485,11 +5572,17 @@ fn substitute_in_expr<'a>( } => { let mut did_change = false; let new_args = Vec::from_iter_in( - args.iter().map(|s| match substitute(subs, *s) { - None => *s, - Some(s) => { - did_change = true; - s + args.iter().map(|e| { + if let ListLiteralElement::Symbol(s) = e { + match substitute(subs, *s) { + None => ListLiteralElement::Symbol(*s), + Some(s) => { + did_change = true; + ListLiteralElement::Symbol(s) + } + } + } else { + *e } }), arena, diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 41fdc63c63..490314c885 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -1,5 +1,5 @@ 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 bumpalo::collections::Vec; use bumpalo::Bump; @@ -564,8 +564,18 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool { match expr { Expr::Literal(_) => false, Expr::Call(call) => has_live_var_call(call, needle), - Expr::Array { elems: fields, .. } - | Expr::Tag { + Expr::Array { elems: fields, .. } => { + for element in fields.iter() { + if let ListLiteralElement::Symbol(s) = element { + if *s == needle { + return true; + } + } + } + + false + } + Expr::Tag { arguments: fields, .. } | Expr::Struct(fields) => fields.iter().any(|s| *s == needle), diff --git a/compiler/test_gen/Cargo.toml b/compiler/test_gen/Cargo.toml index e255efcf5a..ab08fc1527 100644 --- a/compiler/test_gen/Cargo.toml +++ b/compiler/test_gen/Cargo.toml @@ -23,6 +23,7 @@ roc_can = { path = "../can" } roc_parse = { path = "../parse" } roc_build = { path = "../build" } roc_std = { path = "../../roc_std" } +roc_gen_wasm = { path = "../gen_wasm" } 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! bumpalo = { version = "3.6.1", features = ["collections"] } diff --git a/compiler/test_gen/src/helpers/eval.rs b/compiler/test_gen/src/helpers/eval.rs index f380b8ac88..10c770a3d7 100644 --- a/compiler/test_gen/src/helpers/eval.rs +++ b/compiler/test_gen/src/helpers/eval.rs @@ -6,9 +6,9 @@ use roc_can::builtins::builtin_defs_map; use roc_can::def::Def; use roc_collections::all::{MutMap, MutSet}; 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_mono::ir::OptLevel; -use roc_std::{RocDec, RocList, RocOrder, RocStr}; use roc_types::subs::VarStore; use target_lexicon::Triple; @@ -366,29 +366,6 @@ pub fn helper_wasm<'a>( 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") .current_dir(dir_path) .args(&[ @@ -405,24 +382,6 @@ pub fn helper_wasm<'a>( .status() .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 use wasmer::{Function, Instance, Module, Store}; @@ -500,7 +459,7 @@ fn fake_wasm_main_function(_: u32, _: u32) -> u32 { #[allow(dead_code)] pub fn assert_wasm_evals_to_help(src: &str, ignore_problems: bool) -> Result where - T: FromWasmMemory, + T: FromWasm32Memory, { let arena = bumpalo::Bump::new(); let context = inkwell::context::Context::create(); @@ -534,7 +493,7 @@ where _ => panic!(), }; - let output = ::decode( + let output = ::decode( memory, // skip the RocCallResult tag id address as u32 + 8, @@ -643,232 +602,3 @@ macro_rules! assert_non_opt_evals_to { $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 = MaybeUninit::uninit(); - let width = std::mem::size_of::(); - - 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 = 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 = ::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 = 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 FromWasmMemory for RocList { - const SIZE_OF_WASM: usize = 8; - const ALIGN_OF_WASM: usize = 4; - - fn decode(memory: &wasmer::Memory, offset: u32) -> Self { - let bytes = ::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 = ::decode( - memory, - elements + i * ::SIZE_OF_WASM as u32, - ); - items.push(item); - } - - RocList::from_slice(&items) - } -} - -impl 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 = ::decode(memory, offset); - - let length = (bytes >> 32) as u32; - let elements = bytes as u32; - - let ptr: wasmer::WasmPtr = wasmer::WasmPtr::new(elements); - let width = ::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 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 = ::decode(memory, offset); - - let actual = ::decode(memory, elements); - - let b = Box::new(actual); - - std::boxed::Box::::leak(b) - } -} - -impl 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 = wasmer::WasmPtr::new(offset); - let width = ::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 { - ::decode(memory, offset) as usize - } -} - -impl 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 = ::decode(memory, offset); - - let u = ::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 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 = ::decode(memory, offset); - - let u = ::decode(memory, offset + T::ACTUAL_WIDTH as u32); - - let v = ::decode( - memory, - offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32, - ); - - (t, u, v) - } -} diff --git a/compiler/test_mono/generated/fst.txt b/compiler/test_mono/generated/fst.txt index 13e6954e29..fe81173aa5 100644 --- a/compiler/test_mono/generated/fst.txt +++ b/compiler/test_mono/generated/fst.txt @@ -3,14 +3,8 @@ procedure Test.1 (Test.2, Test.3): ret Test.2; procedure Test.0 (): - let Test.11 = 1i64; - let Test.12 = 2i64; - 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.5 = Array [1i64, 2i64, 3i64]; + let Test.6 = Array [3i64, 2i64, 1i64]; let Test.4 = CallByName Test.1 Test.5 Test.6; dec Test.6; dec Test.5; diff --git a/compiler/test_mono/generated/ir_int_add.txt b/compiler/test_mono/generated/ir_int_add.txt index a4168a5109..0da2caed32 100644 --- a/compiler/test_mono/generated/ir_int_add.txt +++ b/compiler/test_mono/generated/ir_int_add.txt @@ -7,9 +7,7 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Test.5; procedure Test.0 (): - let Test.11 = 1i64; - let Test.12 = 2i64; - let Test.1 = Array [Test.11, Test.12]; + let Test.1 = Array [1i64, 2i64]; let Test.9 = 5i64; let Test.10 = 4i64; let Test.7 = CallByName Num.24 Test.9 Test.10; diff --git a/compiler/test_mono/generated/let_with_record_pattern_list.txt b/compiler/test_mono/generated/let_with_record_pattern_list.txt index e2ed3831d0..91f6ea8249 100644 --- a/compiler/test_mono/generated/let_with_record_pattern_list.txt +++ b/compiler/test_mono/generated/let_with_record_pattern_list.txt @@ -1,8 +1,5 @@ procedure Test.0 (): - let Test.6 = 1i64; - let Test.7 = 3i64; - let Test.8 = 4i64; - let Test.4 = Array [Test.6, Test.7, Test.8]; + let Test.4 = Array [1i64, 3i64, 4i64]; let Test.5 = 3.14f64; let Test.3 = Struct {Test.4, Test.5}; let Test.1 = StructAtIndex 0 Test.3; diff --git a/compiler/test_mono/generated/list_append.txt b/compiler/test_mono/generated/list_append.txt index 49c502afd3..8cb3ba9758 100644 --- a/compiler/test_mono/generated/list_append.txt +++ b/compiler/test_mono/generated/list_append.txt @@ -3,8 +3,7 @@ procedure List.5 (#Attr.2, #Attr.3): ret Test.4; procedure Test.0 (): - let Test.5 = 1i64; - let Test.2 = Array [Test.5]; + let Test.2 = Array [1i64]; let Test.3 = 2i64; let Test.1 = CallByName List.5 Test.2 Test.3; ret Test.1; diff --git a/compiler/test_mono/generated/list_append_closure.txt b/compiler/test_mono/generated/list_append_closure.txt index cd33ef5754..cffdbe7e48 100644 --- a/compiler/test_mono/generated/list_append_closure.txt +++ b/compiler/test_mono/generated/list_append_closure.txt @@ -8,8 +8,6 @@ procedure Test.1 (Test.2): ret Test.5; procedure Test.0 (): - let Test.8 = 1i64; - let Test.9 = 2i64; - let Test.4 = Array [Test.8, Test.9]; + let Test.4 = Array [1i64, 2i64]; let Test.3 = CallByName Test.1 Test.4; ret Test.3; diff --git a/compiler/test_mono/generated/list_cannot_update_inplace.txt b/compiler/test_mono/generated/list_cannot_update_inplace.txt index 0a29c418b8..88039ce8a0 100644 --- a/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,9 +1,9 @@ procedure List.4 (#Attr.2, #Attr.3, #Attr.4): - let Test.22 = lowlevel ListLen #Attr.2; - let Test.20 = lowlevel NumLt #Attr.3 Test.22; - if Test.20 then - let Test.21 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; - ret Test.21; + let Test.19 = lowlevel ListLen #Attr.2; + let Test.17 = lowlevel NumLt #Attr.3 Test.19; + if Test.17 then + let Test.18 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4; + ret Test.18; else ret #Attr.2; @@ -16,23 +16,20 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Test.7; procedure Test.1 (): - let Test.11 = 1i64; - let Test.12 = 2i64; - let Test.13 = 3i64; - let Test.10 = Array [Test.11, Test.12, Test.13]; + let Test.10 = Array [1i64, 2i64, 3i64]; ret Test.10; procedure Test.2 (Test.3): - let Test.17 = 0i64; - let Test.18 = 0i64; - let Test.16 = CallByName List.4 Test.3 Test.17 Test.18; - ret Test.16; + let Test.14 = 0i64; + let Test.15 = 0i64; + let Test.13 = CallByName List.4 Test.3 Test.14 Test.15; + ret Test.13; procedure Test.0 (): - let Test.15 = CallByName Test.1; - let Test.14 = CallByName Test.2 Test.15; - let Test.5 = CallByName List.7 Test.14; - dec Test.14; + let Test.12 = CallByName Test.1; + let Test.11 = CallByName Test.2 Test.12; + let Test.5 = CallByName List.7 Test.11; + dec Test.11; let Test.8 = CallByName Test.1; let Test.6 = CallByName List.7 Test.8; dec Test.8; diff --git a/compiler/test_mono/generated/list_get.txt b/compiler/test_mono/generated/list_get.txt index e2da3a0e9b..49280478d1 100644 --- a/compiler/test_mono/generated/list_get.txt +++ b/compiler/test_mono/generated/list_get.txt @@ -11,10 +11,7 @@ procedure List.3 (#Attr.2, #Attr.3): ret Test.8; procedure Test.1 (Test.2): - let Test.14 = 1i64; - let Test.15 = 2i64; - let Test.16 = 3i64; - let Test.6 = Array [Test.14, Test.15, Test.16]; + let Test.6 = Array [1i64, 2i64, 3i64]; let Test.7 = 0i64; let Test.5 = CallByName List.3 Test.6 Test.7; dec Test.6; diff --git a/compiler/test_mono/generated/list_len.txt b/compiler/test_mono/generated/list_len.txt index d5cd03a2b9..3cc3d980bd 100644 --- a/compiler/test_mono/generated/list_len.txt +++ b/compiler/test_mono/generated/list_len.txt @@ -11,12 +11,8 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Test.6; procedure Test.0 (): - let Test.10 = 1i64; - let Test.11 = 2i64; - 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.1 = Array [1i64, 2i64, 3i64]; + let Test.2 = Array [1f64]; let Test.4 = CallByName List.7 Test.1; dec Test.1; let Test.5 = CallByName List.7 Test.2; diff --git a/compiler/test_mono/generated/list_pass_to_function.txt b/compiler/test_mono/generated/list_pass_to_function.txt index b33881bde8..c2d21a9d87 100644 --- a/compiler/test_mono/generated/list_pass_to_function.txt +++ b/compiler/test_mono/generated/list_pass_to_function.txt @@ -14,9 +14,6 @@ procedure Test.2 (Test.3): ret Test.5; procedure Test.0 (): - let Test.12 = 1i64; - let Test.13 = 2i64; - let Test.14 = 3i64; - let Test.1 = Array [Test.12, Test.13, Test.14]; + let Test.1 = Array [1i64, 2i64, 3i64]; let Test.4 = CallByName Test.2 Test.1; ret Test.4; diff --git a/compiler/test_mono/generated/mk_pair_of.txt b/compiler/test_mono/generated/mk_pair_of.txt index ae4932f71a..a223ddd1c2 100644 --- a/compiler/test_mono/generated/mk_pair_of.txt +++ b/compiler/test_mono/generated/mk_pair_of.txt @@ -4,9 +4,6 @@ procedure Test.1 (Test.2): ret Test.6; procedure Test.0 (): - let Test.7 = 1i64; - let Test.8 = 2i64; - let Test.9 = 3i64; - let Test.5 = Array [Test.7, Test.8, Test.9]; + let Test.5 = Array [1i64, 2i64, 3i64]; let Test.4 = CallByName Test.1 Test.5; ret Test.4; diff --git a/compiler/test_mono/generated/quicksort_swap.txt b/compiler/test_mono/generated/quicksort_swap.txt index 4845bb7c58..5bb60a4c11 100644 --- a/compiler/test_mono/generated/quicksort_swap.txt +++ b/compiler/test_mono/generated/quicksort_swap.txt @@ -56,8 +56,6 @@ procedure Test.1 (Test.2): jump Test.26; procedure Test.0 (): - let Test.39 = 1i64; - let Test.40 = 2i64; - let Test.7 = Array [Test.39, Test.40]; + let Test.7 = Array [1i64, 2i64]; let Test.6 = CallByName Test.1 Test.7; ret Test.6; diff --git a/compiler/test_mono/generated/rigids.txt b/compiler/test_mono/generated/rigids.txt index d6a146ba1b..583b0a0182 100644 --- a/compiler/test_mono/generated/rigids.txt +++ b/compiler/test_mono/generated/rigids.txt @@ -54,7 +54,6 @@ procedure Test.1 (Test.2, Test.3, Test.4): procedure Test.0 (): let Test.10 = 0i64; let Test.11 = 0i64; - let Test.40 = 1i64; - let Test.12 = Array [Test.40]; + let Test.12 = Array [1i64]; let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12; ret Test.9; diff --git a/examples/benchmarks/platform/host.zig b/examples/benchmarks/platform/host.zig index 420a8f379d..9f3e5efb5a 100644 --- a/examples/benchmarks/platform/host.zig +++ b/examples/benchmarks/platform/host.zig @@ -40,7 +40,7 @@ export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { if (DEBUG) { var ptr = malloc(size); 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; } else { 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 { - _ = old_size; - _ = alignment; + if (DEBUG) { + 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); }