diff --git a/Cargo.lock b/Cargo.lock index 8272512bfa..1b442cbd37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1959,9 +1959,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.53" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" +checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" dependencies = [ "wasm-bindgen", ] @@ -4154,9 +4154,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9204c41a1597a8c5af23c82d1c921cb01ec0a4c59e07a9c7306062829a3903f3" +checksum = "91e36fa7752016a6c4483706d634fd82c48860dd2df17a0cfaaebc714f23b2dd" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -4819,9 +4819,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" +checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -4829,9 +4829,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" +checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" dependencies = [ "bumpalo", "lazy_static", @@ -4844,9 +4844,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fded345a6559c2cfee778d562300c581f7d4ff3edb9b0d230d69800d213972" +checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4856,9 +4856,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" +checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" dependencies = [ "quote 1.0.9", "wasm-bindgen-macro-support", @@ -4866,9 +4866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" +checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" dependencies = [ "proc-macro2 1.0.29", "quote 1.0.9", @@ -4879,9 +4879,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" +checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" [[package]] name = "wasmer" diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 2b075bb660..a5d491d912 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -327,7 +327,7 @@ mod cli_run { // TODO fix QuicksortApp and then remove this! match benchmark.filename { - "QuicksortApp.roc" | "TestBase64.roc" => { + "QuicksortApp.roc" => { eprintln!("WARNING: skipping testing benchmark {} because the test is broken right now!", benchmark.filename); return; } @@ -608,6 +608,14 @@ fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String { use std::io::Write; use wasmer::{Instance, Module, Store}; + // std::process::Command::new("cp") + // .args(&[ + // wasm_path.to_str().unwrap(), + // "/home/folkertdev/roc/wasm/nqueens.wasm", + // ]) + // .output() + // .unwrap(); + let store = Store::default(); let module = Module::from_file(&store, &wasm_path).unwrap(); diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index ef1af19981..48457d5753 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -618,26 +618,27 @@ fn link_wasm32( ) -> io::Result<(Child, PathBuf)> { let zig_str_path = find_zig_str_path(); - let child = - Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig") - // .env_clear() - // .env("PATH", &env_path) - .args(&["build-exe"]) - .args(input_paths) - .args([ - &format!("-femit-bin={}", output_path.to_str().unwrap()), - // include libc - "-lc", - "-target", - "wasm32-wasi", - "--pkg-begin", - "str", - zig_str_path.to_str().unwrap(), - "--pkg-end", - // useful for debugging - // "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll", - ]) - .spawn()?; + let child = Command::new("zig9") + // .env_clear() + // .env("PATH", &env_path) + .args(&["build-exe"]) + .args(input_paths) + .args([ + &format!("-femit-bin={}", output_path.to_str().unwrap()), + // include libc + "-lc", + "-target", + "wasm32-wasi-musl", + "--pkg-begin", + "str", + zig_str_path.to_str().unwrap(), + "--pkg-end", + "--strip", + // "-O", "ReleaseSmall", + // useful for debugging + // "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll", + ]) + .spawn()?; Ok((child, output_path)) } diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 2172e693d7..801550663c 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -1150,8 +1150,8 @@ fn strToBytes(arg: RocStr) RocList { } const FromUtf8Result = extern struct { - byte_index: usize, string: RocStr, + byte_index: usize, is_ok: bool, problem_code: Utf8ByteProblem, }; diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index ebf0ecbc29..b8c9b922e8 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -2189,8 +2189,15 @@ fn list_literal<'a, 'ctx, 'env>( 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) + // Add zero bytes that represent the refcount + // + // - if all elements are const, then we store the whole list as a constant. + // It then needs a refcount before the first element. + // - but if the list is not all constants, then we will just copy the constant values, + // and we do not need that refcount at the start + // + // In the latter case, we won't store the zeros in the globals + // (we slice them off again below) for _ in 0..zero_elements { global_elements.push(element_type.const_zero()); } diff --git a/compiler/gen_llvm/src/llvm/build_str.rs b/compiler/gen_llvm/src/llvm/build_str.rs index 3b35d6d9a9..87a8c50c57 100644 --- a/compiler/gen_llvm/src/llvm/build_str.rs +++ b/compiler/gen_llvm/src/llvm/build_str.rs @@ -1,5 +1,5 @@ use crate::llvm::bitcode::{call_bitcode_fn, call_void_bitcode_fn}; -use crate::llvm::build::{complex_bitcast, Env, Scope}; +use crate::llvm::build::{complex_bitcast, struct_from_fields, Env, Scope}; use crate::llvm::build_list::{allocate_list, call_bitcode_fn_returns_list, store_list}; use inkwell::builder::Builder; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; @@ -260,6 +260,73 @@ pub fn str_to_utf8<'a, 'ctx, 'env>( call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_UTF8) } +fn decode_from_utf8_result<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + pointer: PointerValue<'ctx>, +) -> StructValue<'ctx> { + let builder = env.builder; + let ctx = env.context; + + let fields = match env.ptr_bytes { + 8 => [ + env.ptr_int().into(), + super::convert::zig_str_type(env).into(), + env.context.bool_type().into(), + ctx.i8_type().into(), + ], + 4 => [ + super::convert::zig_str_type(env).into(), + env.ptr_int().into(), + env.context.bool_type().into(), + ctx.i8_type().into(), + ], + _ => unreachable!(), + }; + + let record_type = env.context.struct_type(&fields, false); + + match env.ptr_bytes { + 8 => { + let zig_struct = builder + .build_load(pointer, "load_utf8_validate_bytes_result") + .into_struct_value(); + + let string = builder + .build_extract_value(zig_struct, 0, "string") + .unwrap(); + + let byte_index = builder + .build_extract_value(zig_struct, 1, "byte_index") + .unwrap(); + + let is_ok = builder.build_extract_value(zig_struct, 2, "is_ok").unwrap(); + + let problem_code = builder + .build_extract_value(zig_struct, 3, "problem_code") + .unwrap(); + + let values = [byte_index, string, is_ok, problem_code]; + + struct_from_fields(env, record_type, values.iter().copied().enumerate()) + } + 4 => { + let result_ptr_cast = env + .builder + .build_bitcast( + pointer, + record_type.ptr_type(AddressSpace::Generic), + "to_unnamed", + ) + .into_pointer_value(); + + builder + .build_load(result_ptr_cast, "load_utf8_validate_bytes_result") + .into_struct_value() + } + _ => unreachable!(), + } +} + /// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 } pub fn str_from_utf8_range<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -268,7 +335,6 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>( count_and_start: StructValue<'ctx>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; - let ctx = env.context; let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap(); let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result"); @@ -293,26 +359,7 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>( bitcode::STR_FROM_UTF8_RANGE, ); - let record_type = env.context.struct_type( - &[ - env.ptr_int().into(), - super::convert::zig_str_type(env).into(), - env.context.bool_type().into(), - ctx.i8_type().into(), - ], - false, - ); - - let result_ptr_cast = env - .builder - .build_bitcast( - result_ptr, - record_type.ptr_type(AddressSpace::Generic), - "to_unnamed", - ) - .into_pointer_value(); - - builder.build_load(result_ptr_cast, "load_utf8_validate_bytes_result") + decode_from_utf8_result(env, result_ptr).into() } /// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 } @@ -322,7 +369,6 @@ pub fn str_from_utf8<'a, 'ctx, 'env>( original_wrapper: StructValue<'ctx>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; - let ctx = env.context; let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap(); let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result"); @@ -341,26 +387,7 @@ pub fn str_from_utf8<'a, 'ctx, 'env>( bitcode::STR_FROM_UTF8, ); - let record_type = env.context.struct_type( - &[ - env.ptr_int().into(), - super::convert::zig_str_type(env).into(), - env.context.bool_type().into(), - ctx.i8_type().into(), - ], - false, - ); - - let result_ptr_cast = env - .builder - .build_bitcast( - result_ptr, - record_type.ptr_type(AddressSpace::Generic), - "to_unnamed", - ) - .into_pointer_value(); - - builder.build_load(result_ptr_cast, "load_utf8_validate_bytes_result") + decode_from_utf8_result(env, result_ptr).into() } /// Str.fromInt : Int -> Str diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 21d50f2715..3568ce5045 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1151,10 +1151,15 @@ impl<'a> Builtin<'a> { Float64 => align_of::() as u32, Float32 => align_of::() as u32, Float16 => align_of::() as u32, - Str | EmptyStr => pointer_size, Dict(_, _) | EmptyDict => pointer_size, Set(_) | EmptySet => pointer_size, - List(_) | EmptyList => pointer_size, + // we often treat these as i128 (64-bit systems) + // or i64 (32-bit systems). + // + // In webassembly, For that to be safe + // they must be aligned to allow such access + List(_) | EmptyList => pointer_size.max(8), + Str | EmptyStr => pointer_size.max(8), } } @@ -1240,9 +1245,10 @@ impl<'a> Builtin<'a> { Builtin::Str => pointer_size, Builtin::Dict(k, v) => k .alignment_bytes(pointer_size) - .max(v.alignment_bytes(pointer_size)), - Builtin::Set(k) => k.alignment_bytes(pointer_size), - Builtin::List(e) => e.alignment_bytes(pointer_size), + .max(v.alignment_bytes(pointer_size)) + .max(pointer_size), + Builtin::Set(k) => k.alignment_bytes(pointer_size).max(pointer_size), + Builtin::List(e) => e.alignment_bytes(pointer_size).max(pointer_size), Builtin::EmptyStr | Builtin::EmptyList | Builtin::EmptyDict | Builtin::EmptySet => { unreachable!("not heap-allocated") } diff --git a/compiler/test_gen/src/helpers/eval.rs b/compiler/test_gen/src/helpers/eval.rs index 10c770a3d7..dc4c377c84 100644 --- a/compiler/test_gen/src/helpers/eval.rs +++ b/compiler/test_gen/src/helpers/eval.rs @@ -366,7 +366,7 @@ pub fn helper_wasm<'a>( use std::process::Command; - Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig") + Command::new("zig9") .current_dir(dir_path) .args(&[ "wasm-ld",