diff --git a/Cargo.lock b/Cargo.lock index 2aedb0b824..1c987c0452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3122,8 +3122,6 @@ name = "roc_build" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", "inkwell 0.1.0", "libloading 0.7.1", "roc_builtins", @@ -3164,8 +3162,6 @@ name = "roc_can" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", "indoc", "pretty_assertions", "roc_builtins", @@ -3187,8 +3183,6 @@ dependencies = [ "cli_utils", "const_format", "criterion", - "im", - "im-rc", "indoc", "inkwell 0.1.0", "libloading 0.7.1", @@ -3298,8 +3292,6 @@ dependencies = [ "fs_extra", "futures", "glyph_brush", - "im", - "im-rc", "libc", "log", "nonempty", @@ -3339,8 +3331,6 @@ name = "roc_fmt" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", "indoc", "pretty_assertions", "roc_collections", @@ -3354,8 +3344,6 @@ name = "roc_gen_dev" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", "object 0.26.2", "roc_builtins", "roc_can", @@ -3377,22 +3365,13 @@ name = "roc_gen_llvm" version = "0.1.0" dependencies = [ "bumpalo", - "im", - "im-rc", "inkwell 0.1.0", "morphic_lib", "roc_builtins", - "roc_can", "roc_collections", "roc_module", "roc_mono", - "roc_parse", - "roc_problem", - "roc_region", - "roc_solve", "roc_std", - "roc_types", - "roc_unify", "target-lexicon", ] @@ -3478,20 +3457,16 @@ version = "0.1.0" dependencies = [ "bumpalo", "hashbrown 0.11.2", - "linked-hash-map", "morphic_lib", - "roc_builtins", "roc_can", "roc_collections", "roc_module", - "roc_parse", "roc_problem", "roc_region", "roc_solve", "roc_std", "roc_types", "roc_unify", - "ven_ena", "ven_graph", "ven_pretty", ] @@ -3531,8 +3506,6 @@ version = "0.1.0" dependencies = [ "bumpalo", "distance", - "im", - "im-rc", "indoc", "pretty_assertions", "roc_builtins", @@ -4074,8 +4047,6 @@ version = "0.1.0" dependencies = [ "bumpalo", "either", - "im", - "im-rc", "indoc", "inkwell 0.1.0", "libc", diff --git a/Earthfile b/Earthfile index 315bbf1eb1..407477acf2 100644 --- a/Earthfile +++ b/Earthfile @@ -111,12 +111,14 @@ test-all: build-nightly-release: FROM +test-rust - COPY --dir .git ./ + COPY --dir .git LICENSE LEGAL_DETAILS ./ # version.txt is used by the CLI: roc --version RUN printf "nightly pre-release, built from commit " > version.txt RUN git log --pretty=format:'%h' -n 1 >> version.txt + RUN printf " on: " >> version.txt + RUN date >> version.txt RUN cargo build --features with_sound --release - RUN cd ./target/release && tar -czvf roc_linux_x86_64.tar.gz ./roc + RUN cd ./target/release && tar -czvf roc_linux_x86_64.tar.gz ./roc ../../LICENSE ../../LEGAL_DETAILS ../../examples/hello-world ../../examples/hello-rust ../../examples/hello-zig ../../compiler/builtins/bitcode/src/ ../../roc_std SAVE ARTIFACT ./target/release/roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz # compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run. diff --git a/ast/src/lang/core/str.rs b/ast/src/lang/core/str.rs index 47177b2e08..0463f77969 100644 --- a/ast/src/lang/core/str.rs +++ b/ast/src/lang/core/str.rs @@ -186,6 +186,7 @@ pub fn update_str_expr( let insert_either = match str_expr { Expr2::SmallStr(arr_string) => { + // TODO make sure this works for unicode "characters" let insert_res = arr_string.try_insert(insert_index as u8, new_char); match insert_res { diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 090688d80a..0d8ac9ce0e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -68,8 +68,6 @@ clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std" const_format = "0.2.22" rustyline = { git = "https://github.com/rtfeldman/rustyline", tag = "prompt-fix" } rustyline-derive = { git = "https://github.com/rtfeldman/rustyline", tag = "prompt-fix" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } libloading = "0.7.1" mimalloc = { version = "0.1.26", default-features = false } diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 3c0bf5381a..543f24981a 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -23,6 +23,7 @@ pub const CMD_REPL: &str = "repl"; pub const CMD_EDIT: &str = "edit"; pub const CMD_DOCS: &str = "docs"; pub const CMD_CHECK: &str = "check"; +pub const CMD_VERSION: &str = "version"; pub const FLAG_DEBUG: &str = "debug"; pub const FLAG_DEV: &str = "dev"; @@ -103,8 +104,11 @@ pub fn build_app<'a>() -> App<'a> { .subcommand(App::new(CMD_REPL) .about("Launch the interactive Read Eval Print Loop (REPL)") ) + .subcommand(App::new(CMD_VERSION) + .about("Print version information") + ) .subcommand(App::new(CMD_CHECK) - .about("Build a binary from the given .roc file, but don't run it") + .about("When developing, it's recommended to run `check` before `build`. It may provide a useful error message in cases where `build` panics") .arg( Arg::new(FLAG_TIME) .long(FLAG_TIME) @@ -190,13 +194,15 @@ pub fn build_app<'a>() -> App<'a> { if cfg!(feature = "editor") { app.subcommand( - App::new(CMD_EDIT).about("Launch the Roc editor").arg( - Arg::new(DIRECTORY_OR_FILES) - .index(1) - .multiple_values(true) - .required(false) - .about("(optional) The directory or files to open on launch."), - ), + App::new(CMD_EDIT) + .about("Launch the Roc editor (Work In Progress)") + .arg( + Arg::new(DIRECTORY_OR_FILES) + .index(1) + .multiple_values(true) + .required(false) + .about("(optional) The directory or files to open on launch."), + ), ) } else { app diff --git a/cli/src/main.rs b/cli/src/main.rs index 0143bbf3af..ef946b8470 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,13 +1,16 @@ use roc_cli::build::check_file; use roc_cli::{ build_app, docs, repl, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, CMD_REPL, - DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE, + CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE, }; use roc_load::file::LoadingProblem; use std::fs::{self, FileType}; use std::io; use std::path::{Path, PathBuf}; +#[macro_use] +extern crate const_format; + #[global_allocator] static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; @@ -122,6 +125,11 @@ fn main() -> io::Result<()> { Ok(0) } + Some(CMD_VERSION) => { + println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n")); + + Ok(0) + } _ => unreachable!(), }?; diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index 6633a74f8b..f9b0ba8640 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -7,19 +7,6 @@ extern crate indoc; #[cfg(test)] mod repl_eval { use cli_utils::helpers; - use roc_gen_llvm::run_roc::RocCallResult; - - #[test] - fn check_discriminant_size() { - // tells us if the size of the discriminant has changed. Lots of other code - // relies on this size - let value: i64 = 1234; - assert_eq!( - std::mem::size_of_val(&RocCallResult::Success(value)), - roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE - + std::mem::size_of_val(&value) - ) - } const ERROR_MESSAGE_START: char = '─'; diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index 80c87fe626..3a9e19187f 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -24,8 +24,6 @@ roc_gen_wasm = { path = "../gen_wasm", optional = true } roc_gen_dev = { path = "../gen_dev", default-features = false } roc_reporting = { path = "../reporting" } roc_std = { path = "../../roc_std" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } libloading = "0.7.1" tempfile = "3.2.0" diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index f7bf75301b..335340c256 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -992,6 +992,22 @@ pub fn types() -> MutMap { Box::new(list_type(flex(TVAR1))), ); + // sublist : List elem, { start : Nat, len : Nat } -> List elem + add_top_level_function_type!( + Symbol::LIST_SUBLIST, + vec![ + list_type(flex(TVAR1)), + SolvedType::Record { + fields: vec![ + ("start".into(), RecordField::Required(nat_type())), + ("len".into(), RecordField::Required(nat_type())), + ], + ext: Box::new(SolvedType::EmptyRecord), + }, + ], + Box::new(list_type(flex(TVAR1))), + ); + // drop : List elem, Nat -> List elem add_top_level_function_type!( Symbol::LIST_DROP, diff --git a/compiler/can/Cargo.toml b/compiler/can/Cargo.toml index e71e5b7c64..2e8bd639dd 100644 --- a/compiler/can/Cargo.toml +++ b/compiler/can/Cargo.toml @@ -14,8 +14,6 @@ roc_problem = { path = "../problem" } roc_types = { path = "../types" } roc_builtins = { path = "../builtins" } ven_graph = { path = "../../vendor/pathfinding" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } [dev-dependencies] diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 193be3d007..01fb6c404c 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -94,6 +94,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_MAP4 => list_map4, LIST_TAKE_FIRST => list_take_first, LIST_TAKE_LAST => list_take_last, + LIST_SUBLIST => list_sublist, LIST_DROP => list_drop, LIST_DROP_AT => list_drop_at, LIST_DROP_FIRST => list_drop_first, @@ -2060,6 +2061,54 @@ fn list_take_last(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } +/// List.sublist : List elem, { start : Nat, len : Nat } -> List elem +fn list_sublist(symbol: Symbol, var_store: &mut VarStore) -> Def { + let list_var = var_store.fresh(); + let rec_var = var_store.fresh(); + + let sym_list = Symbol::ARG_1; + let sym_rec = Symbol::ARG_2; + + let start_var = var_store.fresh(); + let len_var = var_store.fresh(); + + let get_start = Access { + record_var: rec_var, + ext_var: var_store.fresh(), + field_var: var_store.fresh(), + loc_expr: Box::new(no_region(Var(sym_rec))), + field: "start".into(), + }; + + let get_len = Access { + record_var: rec_var, + ext_var: var_store.fresh(), + field_var: var_store.fresh(), + loc_expr: Box::new(no_region(Var(sym_rec))), + field: "len".into(), + }; + + let body_drop = RunLowLevel { + op: LowLevel::ListDrop, + args: vec![(list_var, Var(sym_list)), (start_var, get_start)], + ret_var: list_var, + }; + + let body_take = RunLowLevel { + op: LowLevel::ListTakeFirst, + args: vec![(list_var, body_drop), (len_var, get_len)], + ret_var: list_var, + }; + + defn( + symbol, + vec![(list_var, sym_list), (rec_var, sym_rec)], + var_store, + body_take, + list_var, + ) +} + /// List.drop : List elem, Nat -> List elem fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index d74a8434ac..76bc58ae4e 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -872,7 +872,7 @@ fn canonicalize_pending_def<'a>( // TODO try to remove this .clone()! value: loc_can_expr.value.clone(), }, - pattern_vars: im::HashMap::clone(&vars_by_symbol), + pattern_vars: vars_by_symbol.clone(), annotation: Some(Annotation { signature: typ.clone(), introduced_variables: output.introduced_variables.clone(), @@ -1093,7 +1093,7 @@ fn canonicalize_pending_def<'a>( // TODO try to remove this .clone()! value: loc_can_expr.value.clone(), }, - pattern_vars: im::HashMap::clone(&vars_by_symbol), + pattern_vars: vars_by_symbol.clone(), annotation: Some(Annotation { signature: typ.clone(), introduced_variables: output.introduced_variables.clone(), @@ -1231,7 +1231,7 @@ fn canonicalize_pending_def<'a>( region: loc_can_expr.region, value: loc_can_expr.value.clone(), }, - pattern_vars: im::HashMap::clone(&vars_by_symbol), + pattern_vars: vars_by_symbol.clone(), annotation: None, }, ); diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index d22ee4c3f5..4e96b58e51 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -952,7 +952,7 @@ pub fn local_successors<'a>( references: &'a References, closures: &'a MutMap, ) -> ImSet { - let mut answer = im_rc::hashset::HashSet::clone(&references.lookups); + let mut answer = references.lookups.clone(); for call_symbol in references.calls.iter() { answer = answer.union(call_successors(*call_symbol, closures)); @@ -962,7 +962,7 @@ pub fn local_successors<'a>( } fn call_successors(call_symbol: Symbol, closures: &MutMap) -> ImSet { - let mut answer = im_rc::hashset::HashSet::default(); + let mut answer = ImSet::default(); let mut seen = MutSet::default(); let mut queue = vec![call_symbol]; diff --git a/compiler/fmt/Cargo.toml b/compiler/fmt/Cargo.toml index 3817722747..6b19b61815 100644 --- a/compiler/fmt/Cargo.toml +++ b/compiler/fmt/Cargo.toml @@ -10,8 +10,6 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } roc_parse = { path = "../parse" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } [dev-dependencies] diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 9922b67100..2f5bc78dba 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -16,8 +16,6 @@ roc_builtins = { path = "../builtins" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } target-lexicon = "0.12.2" # TODO: Deal with the update of object to 0.27. diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 10cae674a5..5ae5f0abac 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -8,23 +8,11 @@ edition = "2018" [dependencies] roc_collections = { path = "../collections" } -roc_region = { path = "../region" } roc_module = { path = "../module" } -roc_problem = { path = "../problem" } -roc_types = { path = "../types" } roc_builtins = { path = "../builtins" } -roc_unify = { path = "../unify" } -roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_std = { path = "../../roc_std" } morphic_lib = { path = "../../vendor/morphic_lib" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } inkwell = { path = "../../vendor/inkwell" } target-lexicon = "0.12.2" - -[dev-dependencies] -roc_can = { path = "../can" } -roc_parse = { path = "../parse" } -bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 1686391b3b..b89096f4a5 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -3351,8 +3351,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>( builder.position_at_end(entry); - let elements = [Layout::Builtin(Builtin::Int64), return_layout]; - let wrapped_layout = Layout::Struct(&elements); + let wrapped_layout = roc_result_layout(env.arena, return_layout); call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call) } else { call_roc_function(env, roc_function, &return_layout, arguments_for_call) @@ -3380,18 +3379,11 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>( return_layout: Layout<'a>, c_function_name: &str, ) -> FunctionValue<'ctx> { - let context = env.context; - // a tagged union to indicate to the test loader that a panic occurred. // especially when running 32-bit binaries on a 64-bit machine, there // does not seem to be a smarter solution - let wrapper_return_type = context.struct_type( - &[ - context.i64_type().into(), - basic_type_from_layout(env, &return_layout), - ], - false, - ); + let wrapper_return_type = + roc_result_type(env, roc_function.get_type().get_return_type().unwrap()); let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena); for layout in arguments { @@ -3510,8 +3502,6 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( return_layout: Layout<'a>, c_function_name: &str, ) -> FunctionValue<'ctx> { - let context = env.context; - if env.is_gen_test { return expose_function_to_host_help_c_abi_gen_test( env, @@ -3533,15 +3523,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( ); let wrapper_return_type = if env.is_gen_test { - context - .struct_type( - &[ - context.i64_type().into(), - roc_function.get_type().get_return_type().unwrap(), - ], - false, - ) - .into() + roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into() } else { // roc_function.get_type().get_return_type().unwrap() basic_type_from_layout(env, &return_layout) @@ -3708,12 +3690,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>( let builder = env.builder; let return_type = basic_type_from_layout(env, &return_layout); - - let call_result_type = context.struct_type( - &[context.i64_type().into(), return_type.as_basic_type_enum()], - false, - ); - + let call_result_type = roc_result_type(env, return_type.as_basic_type_enum()); let result_alloca = builder.build_alloca(call_result_type, "result"); let then_block = context.append_basic_block(parent, "then_block"); @@ -3811,12 +3788,8 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>( ptr_int }; - let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); - let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false); - // let return_type = call_result_type; - let return_value = { - let v1 = return_type.const_zero(); + let v1 = call_result_type.const_zero(); // flag is non-zero, indicating failure let flag = context.i64_type().const_int(1, false); @@ -3831,17 +3804,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>( v3 }; - // bitcast result alloca so we can store our concrete type { flag, error_msg } in there - let result_alloca_bitcast = builder - .build_bitcast( - result_alloca, - return_type.ptr_type(AddressSpace::Generic), - "result_alloca_bitcast", - ) - .into_pointer_value(); - - // store our return value - builder.build_store(result_alloca_bitcast, return_value); + builder.build_store(result_alloca, return_value); env.builder.build_unconditional_branch(cont_block); } @@ -3866,6 +3829,30 @@ fn make_exception_catcher<'a, 'ctx, 'env>( function_value } +fn roc_result_layout<'a>(arena: &'a Bump, return_layout: Layout<'a>) -> Layout<'a> { + let elements = [ + Layout::Builtin(Builtin::Int64), + Layout::Builtin(Builtin::Usize), + return_layout, + ]; + + Layout::Struct(arena.alloc(elements)) +} + +fn roc_result_type<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + return_type: BasicTypeEnum<'ctx>, +) -> StructType<'ctx> { + env.context.struct_type( + &[ + env.context.i64_type().into(), + env.context.i8_type().ptr_type(AddressSpace::Generic).into(), + return_type, + ], + false, + ) +} + fn make_good_roc_result<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, return_layout: Layout<'a>, @@ -3874,11 +3861,7 @@ fn make_good_roc_result<'a, 'ctx, 'env>( let context = env.context; let builder = env.builder; - let content_type = basic_type_from_layout(env, &return_layout); - let wrapper_return_type = - context.struct_type(&[context.i64_type().into(), content_type], false); - - let v1 = wrapper_return_type.const_zero(); + let v1 = roc_result_type(env, return_value.get_type()).const_zero(); let v2 = builder .build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error") @@ -3890,11 +3873,11 @@ fn make_good_roc_result<'a, 'ctx, 'env>( "load_call_result_passed_by_ptr", ); builder - .build_insert_value(v2, loaded, 1, "set_call_result") + .build_insert_value(v2, loaded, 2, "set_call_result") .unwrap() } else { builder - .build_insert_value(v2, return_value, 1, "set_call_result") + .build_insert_value(v2, return_value, 2, "set_call_result") .unwrap() }; @@ -3923,13 +3906,8 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>( } }; - let wrapper_return_type = context.struct_type( - &[ - context.i64_type().into(), - basic_type_from_layout(env, &return_layout), - ], - false, - ); + let wrapper_return_type = + roc_result_type(env, roc_function.get_type().get_return_type().unwrap()); // argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into()); diff --git a/compiler/gen_llvm/src/run_roc.rs b/compiler/gen_llvm/src/run_roc.rs index 884ef41fcc..4428f68fbf 100644 --- a/compiler/gen_llvm/src/run_roc.rs +++ b/compiler/gen_llvm/src/run_roc.rs @@ -1,22 +1,23 @@ use std::ffi::CString; +use std::mem::MaybeUninit; use std::os::raw::c_char; -use RocCallResult::*; /// This must have the same size as the repr() of RocCallResult! pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::(); -#[repr(u64)] -pub enum RocCallResult { - Success(T), - Failure(*mut c_char), +#[repr(C)] +pub struct RocCallResult { + tag: u64, + error_msg: *mut c_char, + value: MaybeUninit, } impl From> for Result { fn from(call_result: RocCallResult) -> Self { - match call_result { - Success(value) => Ok(value), - Failure(failure) => Err({ - let raw = unsafe { CString::from_raw(failure) }; + match call_result.tag { + 0 => Ok(unsafe { call_result.value.assume_init() }), + _ => Err({ + let raw = unsafe { CString::from_raw(call_result.error_msg) }; let result = format!("{:?}", raw); @@ -86,7 +87,7 @@ macro_rules! run_jit_function_dynamic_type { .ok_or(format!("Unable to JIT compile `{}`", $main_fn_name)) .expect("errored"); - let size = roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + $bytes; + let size = std::mem::size_of::>() + $bytes; let layout = std::alloc::Layout::array::(size).unwrap(); let result = std::alloc::alloc(layout); main(result); @@ -94,7 +95,7 @@ macro_rules! run_jit_function_dynamic_type { let flag = *result; if flag == 0 { - $transform(result.offset(8) as *const u8) + $transform(result.add(std::mem::size_of::>()) as *const u8) } else { use std::ffi::CString; use std::os::raw::c_char; diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 791dfffc04..3b4da4a465 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -1,14 +1,13 @@ use bumpalo::{self, collections::Vec}; use code_builder::Align; -use roc_builtins::bitcode::{self, FloatWidth}; use roc_collections::all::MutMap; -use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt}; use roc_mono::layout::{Layout, LayoutIds}; use crate::layout::WasmLayout; +use crate::low_level::{build_call_low_level, LowlevelBuildResult}; use crate::storage::{Storage, StoredValue, StoredValueKind}; use crate::wasm_module::linking::{ DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK, @@ -24,7 +23,7 @@ use crate::wasm_module::{ }; use crate::{ copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE, - STACK_POINTER_NAME, + STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, }; /// The memory address where the constants data will be loaded during module instantiation. @@ -84,12 +83,12 @@ impl<'a> WasmBackend<'a> { exports.push(Export { name: STACK_POINTER_NAME.to_string(), ty: ExportType::Global, - index: 0, + index: STACK_POINTER_GLOBAL_ID, }); linker_symbols.push(SymInfo::Global(WasmObjectSymbol::Defined { - flags: WASM_SYM_BINDING_WEAK, - index: 0, + flags: WASM_SYM_BINDING_WEAK, // TODO: this works but means external .o files decide how much stack we have! + index: STACK_POINTER_GLOBAL_ID, name: STACK_POINTER_NAME.to_string(), })); @@ -487,7 +486,29 @@ impl<'a> WasmBackend<'a> { } CallType::LowLevel { op: lowlevel, .. } => { - self.build_call_low_level(lowlevel, arguments, layout) + let return_layout = WasmLayout::new(layout); + self.storage.load_symbols(&mut self.code_builder, arguments); + + let build_result = build_call_low_level( + &mut self.code_builder, + &mut self.storage, + lowlevel, + arguments, + &return_layout, + ); + use LowlevelBuildResult::*; + + match build_result { + Done => Ok(()), + BuiltinCall(name) => { + self.call_imported_builtin(name, arguments, &return_layout); + Ok(()) + } + NotImplemented => Err(format!( + "Low level operation {:?} is not yet implemented", + lowlevel + )), + } } x => Err(format!("the call type, {:?}, is not yet implemented", x)), }, @@ -665,90 +686,11 @@ impl<'a> WasmBackend<'a> { Ok(()) } - fn build_call_low_level( - &mut self, - lowlevel: &LowLevel, - args: &'a [Symbol], - return_layout: &Layout<'a>, - ) -> Result<(), String> { - self.storage.load_symbols(&mut self.code_builder, args); - let wasm_layout = WasmLayout::new(return_layout); - let ret_type = wasm_layout.value_type(); - - let panic_ret_type = || panic!("Invalid return type for {:?}: {:?}", lowlevel, ret_type); - - match lowlevel { - LowLevel::NumAdd => match ret_type { - ValueType::I32 => self.code_builder.i32_add(), - ValueType::I64 => self.code_builder.i64_add(), - ValueType::F32 => self.code_builder.f32_add(), - ValueType::F64 => self.code_builder.f64_add(), - }, - LowLevel::NumSub => match ret_type { - ValueType::I32 => self.code_builder.i32_sub(), - ValueType::I64 => self.code_builder.i64_sub(), - ValueType::F32 => self.code_builder.f32_sub(), - ValueType::F64 => self.code_builder.f64_sub(), - }, - LowLevel::NumMul => match ret_type { - ValueType::I32 => self.code_builder.i32_mul(), - ValueType::I64 => self.code_builder.i64_mul(), - ValueType::F32 => self.code_builder.f32_mul(), - ValueType::F64 => self.code_builder.f64_mul(), - }, - LowLevel::NumGt => match self.get_uniform_arg_type(args) { - ValueType::I32 => self.code_builder.i32_gt_s(), - ValueType::I64 => self.code_builder.i64_gt_s(), - ValueType::F32 => self.code_builder.f32_gt(), - ValueType::F64 => self.code_builder.f64_gt(), - }, - LowLevel::Eq => match self.get_uniform_arg_type(args) { - ValueType::I32 => self.code_builder.i32_eq(), - ValueType::I64 => self.code_builder.i64_eq(), - ValueType::F32 => self.code_builder.f32_eq(), - ValueType::F64 => self.code_builder.f64_eq(), - }, - LowLevel::NumNeg => match ret_type { - ValueType::I32 => { - self.code_builder.i32_const(-1); - self.code_builder.i32_mul(); - } - ValueType::I64 => { - self.code_builder.i64_const(-1); - self.code_builder.i64_mul(); - } - ValueType::F32 => self.code_builder.f32_neg(), - ValueType::F64 => self.code_builder.f64_neg(), - }, - LowLevel::NumAtan => { - let name = match ret_type { - ValueType::F32 => &bitcode::NUM_ATAN[FloatWidth::F32], - ValueType::F64 => &bitcode::NUM_ATAN[FloatWidth::F64], - _ => panic_ret_type(), - }; - self.call_imported_builtin(name, &[ret_type], Some(ret_type)); - } - _ => { - return Err(format!("unsupported low-level op {:?}", lowlevel)); - } - }; - Ok(()) - } - - /// Get the ValueType for a set of arguments that are required to have the same type - fn get_uniform_arg_type(&self, args: &'a [Symbol]) -> ValueType { - let value_type = self.storage.get(&args[0]).value_type(); - for arg in args.iter().skip(1) { - debug_assert!(self.storage.get(arg).value_type() == value_type); - } - value_type - } - fn call_imported_builtin( &mut self, name: &'a str, - arg_types: &[ValueType], - ret_type: Option, + arguments: &[Symbol], + ret_layout: &WasmLayout, ) { let (fn_index, linker_symbol_index) = match self.builtin_sym_index_map.get(name) { Some(sym_idx) => match &self.linker_symbols[*sym_idx] { @@ -759,11 +701,11 @@ impl<'a> WasmBackend<'a> { }, None => { - let mut param_types = Vec::with_capacity_in(arg_types.len(), self.env.arena); - param_types.extend_from_slice(arg_types); + let mut param_types = Vec::with_capacity_in(arguments.len(), self.env.arena); + param_types.extend(arguments.iter().map(|a| self.storage.get(a).value_type())); let signature_index = self.module.types.insert(Signature { param_types, - ret_type, + ret_type: Some(ret_layout.value_type()), // TODO: handle builtins with no return value }); let import_index = self.module.import.entries.len() as u32; @@ -787,8 +729,8 @@ impl<'a> WasmBackend<'a> { self.code_builder.call( fn_index, linker_symbol_index, - arg_types.len(), - ret_type.is_some(), + arguments.len(), + true, // TODO: handle builtins with no return value ); } } diff --git a/compiler/gen_wasm/src/layout.rs b/compiler/gen_wasm/src/layout.rs index 49b2b6be93..2cd562dcc8 100644 --- a/compiler/gen_wasm/src/layout.rs +++ b/compiler/gen_wasm/src/layout.rs @@ -71,6 +71,14 @@ impl WasmLayout { } } + pub fn size(&self) -> u32 { + match self { + Self::Primitive(_, size) => *size, + Self::StackMemory { size, .. } => *size, + Self::HeapMemory => PTR_SIZE, + } + } + pub fn is_stack_memory(&self) -> bool { matches!(self, Self::StackMemory { .. }) } diff --git a/compiler/gen_wasm/src/lib.rs b/compiler/gen_wasm/src/lib.rs index 6fcad7c739..c1f2eec941 100644 --- a/compiler/gen_wasm/src/lib.rs +++ b/compiler/gen_wasm/src/lib.rs @@ -1,5 +1,6 @@ mod backend; mod layout; +mod low_level; mod storage; pub mod wasm_module; @@ -126,7 +127,15 @@ pub fn copy_memory(code_builder: &mut CodeBuilder, config: CopyMemoryConfig) { /// Round up to alignment_bytes (which must be a power of 2) pub fn round_up_to_alignment(unaligned: i32, alignment_bytes: i32) -> i32 { - debug_assert!(alignment_bytes.count_ones() == 1); + if alignment_bytes <= 1 { + return unaligned; + } + if alignment_bytes.count_ones() != 1 { + panic!( + "Cannot align to {} bytes. Not a power of 2.", + alignment_bytes + ); + } let mut aligned = unaligned; aligned += alignment_bytes - 1; // if lower bits are non-zero, push it over the next boundary aligned &= -alignment_bytes; // mask with a flag that has upper bits 1, lower bits 0 diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs new file mode 100644 index 0000000000..48eafa1621 --- /dev/null +++ b/compiler/gen_wasm/src/low_level.rs @@ -0,0 +1,360 @@ +use roc_builtins::bitcode::{self, FloatWidth}; +use roc_module::low_level::{LowLevel, LowLevel::*}; +use roc_module::symbol::Symbol; + +use crate::layout::WasmLayout; +use crate::storage::Storage; +use crate::wasm_module::{ + CodeBuilder, + ValueType::{self, *}, +}; + +pub enum LowlevelBuildResult { + Done, + BuiltinCall(&'static str), + NotImplemented, +} + +pub fn build_call_low_level<'a>( + code_builder: &mut CodeBuilder<'a>, + storage: &mut Storage<'a>, + lowlevel: &LowLevel, + args: &'a [Symbol], + ret_layout: &WasmLayout, +) -> LowlevelBuildResult { + use LowlevelBuildResult::*; + + let panic_ret_type = || panic!("Invalid return layout for {:?}: {:?}", lowlevel, ret_layout); + + match lowlevel { + StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt + | StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft + | StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen + | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat + | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2 + | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil + | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListTakeFirst + | ListTakeLast | ListDrop | ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize + | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys + | DictValues | DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => { + return NotImplemented; + } + + NumAdd => match ret_layout.value_type() { + I32 => code_builder.i32_add(), + I64 => code_builder.i64_add(), + F32 => code_builder.f32_add(), + F64 => code_builder.f64_add(), + }, + NumAddWrap => match ret_layout.value_type() { + I32 => { + code_builder.i32_add(); + wrap_i32(code_builder, ret_layout.size()); + } + I64 => code_builder.i64_add(), + F32 => code_builder.f32_add(), + F64 => code_builder.f64_add(), + }, + NumAddChecked => return NotImplemented, + NumSub => match ret_layout.value_type() { + I32 => code_builder.i32_sub(), + I64 => code_builder.i64_sub(), + F32 => code_builder.f32_sub(), + F64 => code_builder.f64_sub(), + }, + NumSubWrap => match ret_layout.value_type() { + I32 => { + code_builder.i32_sub(); + wrap_i32(code_builder, ret_layout.size()); + } + I64 => code_builder.i64_sub(), + F32 => code_builder.f32_sub(), + F64 => code_builder.f64_sub(), + }, + NumSubChecked => return NotImplemented, + NumMul => match ret_layout.value_type() { + I32 => code_builder.i32_mul(), + I64 => code_builder.i64_mul(), + F32 => code_builder.f32_mul(), + F64 => code_builder.f64_mul(), + }, + NumMulWrap => match ret_layout.value_type() { + I32 => { + code_builder.i32_mul(); + wrap_i32(code_builder, ret_layout.size()); + } + I64 => code_builder.i64_mul(), + F32 => code_builder.f32_mul(), + F64 => code_builder.f64_mul(), + }, + NumMulChecked => return NotImplemented, + NumGt => match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_gt_s(), + I64 => code_builder.i64_gt_s(), + F32 => code_builder.f32_gt(), + F64 => code_builder.f64_gt(), + }, + NumGte => match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_ge_s(), + I64 => code_builder.i64_ge_s(), + F32 => code_builder.f32_ge(), + F64 => code_builder.f64_ge(), + }, + NumLt => match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_lt_s(), + I64 => code_builder.i64_lt_s(), + F32 => code_builder.f32_lt(), + F64 => code_builder.f64_lt(), + }, + NumLte => match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_le_s(), + I64 => code_builder.i64_le_s(), + F32 => code_builder.f32_le(), + F64 => code_builder.f64_le(), + }, + NumCompare => return NotImplemented, + NumDivUnchecked => match ret_layout.value_type() { + I32 => code_builder.i32_div_s(), + I64 => code_builder.i64_div_s(), + F32 => code_builder.f32_div(), + F64 => code_builder.f64_div(), + }, + NumDivCeilUnchecked => return NotImplemented, + NumRemUnchecked => match ret_layout.value_type() { + I32 => code_builder.i32_rem_s(), + I64 => code_builder.i64_rem_s(), + F32 => return NotImplemented, + F64 => return NotImplemented, + }, + NumIsMultipleOf => return NotImplemented, + NumAbs => match ret_layout.value_type() { + I32 => { + code_builder.i32_const(0); + storage.load_symbols(code_builder, args); + code_builder.i32_sub(); + storage.load_symbols(code_builder, args); + code_builder.i32_const(0); + code_builder.i32_ge_s(); + code_builder.select(); + } + I64 => { + code_builder.i64_const(0); + storage.load_symbols(code_builder, args); + code_builder.i64_sub(); + storage.load_symbols(code_builder, args); + code_builder.i64_const(0); + code_builder.i64_ge_s(); + code_builder.select(); + } + F32 => code_builder.f32_abs(), + F64 => code_builder.f64_abs(), + }, + NumNeg => { + match ret_layout.value_type() { + I32 => { + // Unfortunate local.set/local.get + code_builder.i32_const(0); + storage.load_symbols(code_builder, args); + code_builder.i32_sub(); + } + I64 => { + // Unfortunate local.set/local.get + code_builder.i64_const(0); + storage.load_symbols(code_builder, args); + code_builder.i64_sub(); + } + F32 => code_builder.f32_neg(), + F64 => code_builder.f64_neg(), + } + } + NumSin => return NotImplemented, + NumCos => return NotImplemented, + NumSqrtUnchecked => return NotImplemented, + NumLogUnchecked => return NotImplemented, + NumRound => { + // FIXME + // thread 'gen_num::f64_round' panicked at 'called `Result::unwrap()` on an `Err` value: + // Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })', + // compiler/test_gen/src/helpers/wasm.rs:185:53 + // Note: Wasm has a `nearest` op, but it does round-to-even when fraction is exactly 0.5 + // which fails tests. Will this do? Or is specific behaviour important? + let width = float_width_from_layout(ret_layout); + return BuiltinCall(&bitcode::NUM_ROUND[width]); + } + NumToFloat => match (ret_layout.value_type(), storage.get(&args[0]).value_type()) { + (F32, I32) => code_builder.f32_convert_s_i32(), + (F32, I64) => code_builder.f32_convert_s_i64(), + (F32, F32) => {} + (F32, F64) => code_builder.f32_demote_f64(), + (F64, I32) => code_builder.f64_convert_s_i32(), + (F64, I64) => code_builder.f64_convert_s_i64(), + (F64, F32) => code_builder.f64_promote_f32(), + (F64, F64) => {} + _ => panic_ret_type(), + }, + NumPow => return NotImplemented, + NumCeiling => match ret_layout.value_type() { + I32 => { + code_builder.f32_ceil(); + code_builder.i32_trunc_s_f32() + } + I64 => { + code_builder.f64_ceil(); + code_builder.i64_trunc_s_f64() + } + _ => panic_ret_type(), + }, + NumPowInt => return NotImplemented, + NumFloor => match ret_layout.value_type() { + I32 => { + code_builder.f32_floor(); + code_builder.i32_trunc_s_f32() + } + I64 => { + code_builder.f64_floor(); + code_builder.i64_trunc_s_f64() + } + _ => panic_ret_type(), + }, + NumIsFinite => match ret_layout.value_type() { + I32 => code_builder.i32_const(1), + I64 => code_builder.i32_const(1), + F32 => { + code_builder.i32_reinterpret_f32(); + code_builder.i32_const(0x7f800000); + code_builder.i32_and(); + code_builder.i32_const(0x7f800000); + code_builder.i32_ne(); + } + F64 => { + code_builder.i64_reinterpret_f64(); + code_builder.i64_const(0x7ff0000000000000); + code_builder.i64_and(); + code_builder.i64_const(0x7ff0000000000000); + code_builder.i64_ne(); + } + }, + NumAtan => { + let width = float_width_from_layout(ret_layout); + return BuiltinCall(&bitcode::NUM_ATAN[width]); + } + NumAcos => { + let width = float_width_from_layout(ret_layout); + return BuiltinCall(&bitcode::NUM_ACOS[width]); + } + NumAsin => { + let width = float_width_from_layout(ret_layout); + return BuiltinCall(&bitcode::NUM_ASIN[width]); + } + NumBytesToU16 => return NotImplemented, + NumBytesToU32 => return NotImplemented, + NumBitwiseAnd => match ret_layout.value_type() { + I32 => code_builder.i32_and(), + I64 => code_builder.i64_and(), + _ => panic_ret_type(), + }, + NumBitwiseXor => match ret_layout.value_type() { + I32 => code_builder.i32_xor(), + I64 => code_builder.i64_xor(), + _ => panic_ret_type(), + }, + NumBitwiseOr => match ret_layout.value_type() { + I32 => code_builder.i32_or(), + I64 => code_builder.i64_or(), + _ => panic_ret_type(), + }, + NumShiftLeftBy => { + // Unfortunate local.set/local.get + storage.load_symbols(code_builder, &[args[1], args[0]]); + match ret_layout.value_type() { + I32 => code_builder.i32_shl(), + I64 => code_builder.i64_shl(), + _ => panic_ret_type(), + } + } + NumShiftRightBy => match ret_layout.value_type() { + I32 => code_builder.i32_shr_s(), + I64 => code_builder.i64_shr_s(), + _ => panic_ret_type(), + }, + NumShiftRightZfBy => match ret_layout.value_type() { + I32 => code_builder.i32_shr_u(), + I64 => code_builder.i64_shr_u(), + _ => panic_ret_type(), + }, + NumIntCast => match (ret_layout.value_type(), storage.get(&args[0]).value_type()) { + (I32, I32) => {} + (I32, I64) => code_builder.i32_wrap_i64(), + (I32, F32) => code_builder.i32_trunc_s_f32(), + (I32, F64) => code_builder.i32_trunc_s_f64(), + + (I64, I32) => code_builder.i64_extend_s_i32(), + (I64, I64) => {} + (I64, F32) => code_builder.i64_trunc_s_f32(), + (I64, F64) => code_builder.i64_trunc_s_f64(), + + (F32, I32) => code_builder.f32_convert_s_i32(), + (F32, I64) => code_builder.f32_convert_s_i64(), + (F32, F32) => {} + (F32, F64) => code_builder.f32_demote_f64(), + + (F64, I32) => code_builder.f64_convert_s_i32(), + (F64, I64) => code_builder.f64_convert_s_i64(), + (F64, F32) => code_builder.f64_promote_f32(), + (F64, F64) => {} + }, + Eq => { + // TODO: For non-number types, this will implement pointer equality, which is wrong + match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_eq(), + I64 => code_builder.i64_eq(), + F32 => code_builder.f32_eq(), + F64 => code_builder.f64_eq(), + } + } + NotEq => { + // TODO: For non-number types, this will implement pointer inequality, which is wrong + match storage.get(&args[0]).value_type() { + I32 => code_builder.i32_ne(), + I64 => code_builder.i64_ne(), + F32 => code_builder.f32_ne(), + F64 => code_builder.f64_ne(), + } + } + And => code_builder.i32_and(), + Or => code_builder.i32_or(), + Not => code_builder.i32_eqz(), + Hash => return NotImplemented, + ExpectTrue => return NotImplemented, + } + Done +} + +/// Wrap an integer whose Wasm representation is i32 +fn wrap_i32(code_builder: &mut CodeBuilder, size: u32) { + match size { + 1 => { + // Underlying Roc value is i8 + code_builder.i32_const(24); + code_builder.i32_shl(); + code_builder.i32_const(24); + code_builder.i32_shr_s(); + } + 2 => { + // Underlying Roc value is i16 + code_builder.i32_const(16); + code_builder.i32_shl(); + code_builder.i32_const(16); + code_builder.i32_shr_s(); + } + _ => {} // the only other possible value is 4, and i32 wraps natively + } +} + +fn float_width_from_layout(wasm_layout: &WasmLayout) -> FloatWidth { + if wasm_layout.value_type() == ValueType::F32 { + FloatWidth::F32 + } else { + FloatWidth::F64 + } +} diff --git a/compiler/gen_wasm/src/wasm_module/code_builder.rs b/compiler/gen_wasm/src/wasm_module/code_builder.rs index fd0466fecd..559221bb0b 100644 --- a/compiler/gen_wasm/src/wasm_module/code_builder.rs +++ b/compiler/gen_wasm/src/wasm_module/code_builder.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use roc_module::symbol::Symbol; use super::linking::{IndexRelocType, OffsetRelocType, RelocationEntry}; -use super::opcodes::*; +use super::opcodes::{OpCode, OpCode::*}; use super::serialize::{SerialBuffer, Serialize}; use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID}; @@ -244,7 +244,7 @@ impl<'a> CodeBuilder<'a> { // Symbol is not on top of the stack. Find it. if let Some(found_index) = self.vm_stack.iter().rposition(|&s| s == symbol) { // Insert a local.set where the value was created - self.add_insertion(pushed_at, SETLOCAL, next_local_id.0); + self.add_insertion(pushed_at, SETLOCAL as u8, next_local_id.0); // Take the value out of the stack where local.set was inserted self.vm_stack.remove(found_index); @@ -267,7 +267,7 @@ impl<'a> CodeBuilder<'a> { Popped { pushed_at } => { // This Symbol is being used for a second time // Insert a local.tee where it was pushed, so we don't interfere with the first usage - self.add_insertion(pushed_at, TEELOCAL, next_local_id.0); + self.add_insertion(pushed_at, TEELOCAL as u8, next_local_id.0); // Insert a local.get at the current position self.get_local(next_local_id); @@ -331,14 +331,14 @@ impl<'a> CodeBuilder<'a> { fn build_stack_frame_push(&mut self, frame_size: i32, frame_pointer: LocalId) { // Can't use the usual instruction methods because they push to self.code. // This is the only case where we push instructions somewhere different. - self.preamble.push(GETGLOBAL); + self.preamble.push(GETGLOBAL as u8); self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID); - self.preamble.push(I32CONST); + self.preamble.push(I32CONST as u8); self.preamble.encode_i32(frame_size); - self.preamble.push(I32SUB); - self.preamble.push(TEELOCAL); + self.preamble.push(I32SUB as u8); + self.preamble.push(TEELOCAL as u8); self.preamble.encode_u32(frame_pointer.0); - self.preamble.push(SETGLOBAL); + self.preamble.push(SETGLOBAL as u8); self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID); } @@ -366,7 +366,7 @@ impl<'a> CodeBuilder<'a> { self.build_stack_frame_pop(aligned_size, frame_ptr_id); } - self.code.push(END); + self.code.push(END as u8); let inner_len = self.preamble.len() + self.code.len() + self.insert_bytes.len(); self.inner_length.encode_u32(inner_len as u32); @@ -433,27 +433,28 @@ impl<'a> CodeBuilder<'a> { /// Base method for generating instructions /// Emits the opcode and simulates VM stack push/pop - fn inst(&mut self, opcode: u8, pops: usize, push: bool) { + fn inst(&mut self, opcode: OpCode, pops: usize, push: bool) { let new_len = self.vm_stack.len() - pops as usize; self.vm_stack.truncate(new_len); if push { self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE); } - self.code.push(opcode); + + self.code.push(opcode as u8); } - fn inst_imm8(&mut self, opcode: u8, pops: usize, push: bool, immediate: u8) { + fn inst_imm8(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u8) { self.inst(opcode, pops, push); self.code.push(immediate); } // public for use in test code - pub fn inst_imm32(&mut self, opcode: u8, pops: usize, push: bool, immediate: u32) { + pub fn inst_imm32(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u32) { self.inst(opcode, pops, push); self.code.encode_u32(immediate); } - fn inst_mem(&mut self, opcode: u8, pops: usize, push: bool, align: Align, offset: u32) { + fn inst_mem(&mut self, opcode: OpCode, pops: usize, push: bool, align: Align, offset: u32) { self.inst(opcode, pops, push); self.code.push(align as u8); self.code.encode_u32(offset); @@ -526,7 +527,7 @@ impl<'a> CodeBuilder<'a> { if has_return_val { self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE); } - self.code.push(CALL); + self.code.push(CALL as u8); // Write the index of the function to be called. // Also make a RelocationEntry so the linker can see that this byte offset relates to a function by name. diff --git a/compiler/gen_wasm/src/wasm_module/opcodes.rs b/compiler/gen_wasm/src/wasm_module/opcodes.rs index 714889bdae..895cf1e5f7 100644 --- a/compiler/gen_wasm/src/wasm_module/opcodes.rs +++ b/compiler/gen_wasm/src/wasm_module/opcodes.rs @@ -1,178 +1,182 @@ -pub const UNREACHABLE: u8 = 0x00; -pub const NOP: u8 = 0x01; -pub const BLOCK: u8 = 0x02; -pub const LOOP: u8 = 0x03; -pub const IF: u8 = 0x04; -pub const ELSE: u8 = 0x05; -pub const END: u8 = 0x0b; -pub const BR: u8 = 0x0c; -pub const BRIF: u8 = 0x0d; -pub const BRTABLE: u8 = 0x0e; -pub const RETURN: u8 = 0x0f; -pub const CALL: u8 = 0x10; -pub const CALLINDIRECT: u8 = 0x11; -pub const DROP: u8 = 0x1a; -pub const SELECT: u8 = 0x1b; -pub const GETLOCAL: u8 = 0x20; -pub const SETLOCAL: u8 = 0x21; -pub const TEELOCAL: u8 = 0x22; -pub const GETGLOBAL: u8 = 0x23; -pub const SETGLOBAL: u8 = 0x24; -pub const I32LOAD: u8 = 0x28; -pub const I64LOAD: u8 = 0x29; -pub const F32LOAD: u8 = 0x2a; -pub const F64LOAD: u8 = 0x2b; -pub const I32LOAD8S: u8 = 0x2c; -pub const I32LOAD8U: u8 = 0x2d; -pub const I32LOAD16S: u8 = 0x2e; -pub const I32LOAD16U: u8 = 0x2f; -pub const I64LOAD8S: u8 = 0x30; -pub const I64LOAD8U: u8 = 0x31; -pub const I64LOAD16S: u8 = 0x32; -pub const I64LOAD16U: u8 = 0x33; -pub const I64LOAD32S: u8 = 0x34; -pub const I64LOAD32U: u8 = 0x35; -pub const I32STORE: u8 = 0x36; -pub const I64STORE: u8 = 0x37; -pub const F32STORE: u8 = 0x38; -pub const F64STORE: u8 = 0x39; -pub const I32STORE8: u8 = 0x3a; -pub const I32STORE16: u8 = 0x3b; -pub const I64STORE8: u8 = 0x3c; -pub const I64STORE16: u8 = 0x3d; -pub const I64STORE32: u8 = 0x3e; -pub const CURRENTMEMORY: u8 = 0x3f; -pub const GROWMEMORY: u8 = 0x40; -pub const I32CONST: u8 = 0x41; -pub const I64CONST: u8 = 0x42; -pub const F32CONST: u8 = 0x43; -pub const F64CONST: u8 = 0x44; -pub const I32EQZ: u8 = 0x45; -pub const I32EQ: u8 = 0x46; -pub const I32NE: u8 = 0x47; -pub const I32LTS: u8 = 0x48; -pub const I32LTU: u8 = 0x49; -pub const I32GTS: u8 = 0x4a; -pub const I32GTU: u8 = 0x4b; -pub const I32LES: u8 = 0x4c; -pub const I32LEU: u8 = 0x4d; -pub const I32GES: u8 = 0x4e; -pub const I32GEU: u8 = 0x4f; -pub const I64EQZ: u8 = 0x50; -pub const I64EQ: u8 = 0x51; -pub const I64NE: u8 = 0x52; -pub const I64LTS: u8 = 0x53; -pub const I64LTU: u8 = 0x54; -pub const I64GTS: u8 = 0x55; -pub const I64GTU: u8 = 0x56; -pub const I64LES: u8 = 0x57; -pub const I64LEU: u8 = 0x58; -pub const I64GES: u8 = 0x59; -pub const I64GEU: u8 = 0x5a; +#[repr(u8)] +#[derive(Debug)] +pub enum OpCode { + UNREACHABLE = 0x00, + NOP = 0x01, + BLOCK = 0x02, + LOOP = 0x03, + IF = 0x04, + ELSE = 0x05, + END = 0x0b, + BR = 0x0c, + BRIF = 0x0d, + BRTABLE = 0x0e, + RETURN = 0x0f, + CALL = 0x10, + CALLINDIRECT = 0x11, + DROP = 0x1a, + SELECT = 0x1b, + GETLOCAL = 0x20, + SETLOCAL = 0x21, + TEELOCAL = 0x22, + GETGLOBAL = 0x23, + SETGLOBAL = 0x24, + I32LOAD = 0x28, + I64LOAD = 0x29, + F32LOAD = 0x2a, + F64LOAD = 0x2b, + I32LOAD8S = 0x2c, + I32LOAD8U = 0x2d, + I32LOAD16S = 0x2e, + I32LOAD16U = 0x2f, + I64LOAD8S = 0x30, + I64LOAD8U = 0x31, + I64LOAD16S = 0x32, + I64LOAD16U = 0x33, + I64LOAD32S = 0x34, + I64LOAD32U = 0x35, + I32STORE = 0x36, + I64STORE = 0x37, + F32STORE = 0x38, + F64STORE = 0x39, + I32STORE8 = 0x3a, + I32STORE16 = 0x3b, + I64STORE8 = 0x3c, + I64STORE16 = 0x3d, + I64STORE32 = 0x3e, + CURRENTMEMORY = 0x3f, + GROWMEMORY = 0x40, + I32CONST = 0x41, + I64CONST = 0x42, + F32CONST = 0x43, + F64CONST = 0x44, + I32EQZ = 0x45, + I32EQ = 0x46, + I32NE = 0x47, + I32LTS = 0x48, + I32LTU = 0x49, + I32GTS = 0x4a, + I32GTU = 0x4b, + I32LES = 0x4c, + I32LEU = 0x4d, + I32GES = 0x4e, + I32GEU = 0x4f, + I64EQZ = 0x50, + I64EQ = 0x51, + I64NE = 0x52, + I64LTS = 0x53, + I64LTU = 0x54, + I64GTS = 0x55, + I64GTU = 0x56, + I64LES = 0x57, + I64LEU = 0x58, + I64GES = 0x59, + I64GEU = 0x5a, -pub const F32EQ: u8 = 0x5b; -pub const F32NE: u8 = 0x5c; -pub const F32LT: u8 = 0x5d; -pub const F32GT: u8 = 0x5e; -pub const F32LE: u8 = 0x5f; -pub const F32GE: u8 = 0x60; + F32EQ = 0x5b, + F32NE = 0x5c, + F32LT = 0x5d, + F32GT = 0x5e, + F32LE = 0x5f, + F32GE = 0x60, -pub const F64EQ: u8 = 0x61; -pub const F64NE: u8 = 0x62; -pub const F64LT: u8 = 0x63; -pub const F64GT: u8 = 0x64; -pub const F64LE: u8 = 0x65; -pub const F64GE: u8 = 0x66; + F64EQ = 0x61, + F64NE = 0x62, + F64LT = 0x63, + F64GT = 0x64, + F64LE = 0x65, + F64GE = 0x66, -pub const I32CLZ: u8 = 0x67; -pub const I32CTZ: u8 = 0x68; -pub const I32POPCNT: u8 = 0x69; -pub const I32ADD: u8 = 0x6a; -pub const I32SUB: u8 = 0x6b; -pub const I32MUL: u8 = 0x6c; -pub const I32DIVS: u8 = 0x6d; -pub const I32DIVU: u8 = 0x6e; -pub const I32REMS: u8 = 0x6f; -pub const I32REMU: u8 = 0x70; -pub const I32AND: u8 = 0x71; -pub const I32OR: u8 = 0x72; -pub const I32XOR: u8 = 0x73; -pub const I32SHL: u8 = 0x74; -pub const I32SHRS: u8 = 0x75; -pub const I32SHRU: u8 = 0x76; -pub const I32ROTL: u8 = 0x77; -pub const I32ROTR: u8 = 0x78; + I32CLZ = 0x67, + I32CTZ = 0x68, + I32POPCNT = 0x69, + I32ADD = 0x6a, + I32SUB = 0x6b, + I32MUL = 0x6c, + I32DIVS = 0x6d, + I32DIVU = 0x6e, + I32REMS = 0x6f, + I32REMU = 0x70, + I32AND = 0x71, + I32OR = 0x72, + I32XOR = 0x73, + I32SHL = 0x74, + I32SHRS = 0x75, + I32SHRU = 0x76, + I32ROTL = 0x77, + I32ROTR = 0x78, -pub const I64CLZ: u8 = 0x79; -pub const I64CTZ: u8 = 0x7a; -pub const I64POPCNT: u8 = 0x7b; -pub const I64ADD: u8 = 0x7c; -pub const I64SUB: u8 = 0x7d; -pub const I64MUL: u8 = 0x7e; -pub const I64DIVS: u8 = 0x7f; -pub const I64DIVU: u8 = 0x80; -pub const I64REMS: u8 = 0x81; -pub const I64REMU: u8 = 0x82; -pub const I64AND: u8 = 0x83; -pub const I64OR: u8 = 0x84; -pub const I64XOR: u8 = 0x85; -pub const I64SHL: u8 = 0x86; -pub const I64SHRS: u8 = 0x87; -pub const I64SHRU: u8 = 0x88; -pub const I64ROTL: u8 = 0x89; -pub const I64ROTR: u8 = 0x8a; -pub const F32ABS: u8 = 0x8b; -pub const F32NEG: u8 = 0x8c; -pub const F32CEIL: u8 = 0x8d; -pub const F32FLOOR: u8 = 0x8e; -pub const F32TRUNC: u8 = 0x8f; -pub const F32NEAREST: u8 = 0x90; -pub const F32SQRT: u8 = 0x91; -pub const F32ADD: u8 = 0x92; -pub const F32SUB: u8 = 0x93; -pub const F32MUL: u8 = 0x94; -pub const F32DIV: u8 = 0x95; -pub const F32MIN: u8 = 0x96; -pub const F32MAX: u8 = 0x97; -pub const F32COPYSIGN: u8 = 0x98; -pub const F64ABS: u8 = 0x99; -pub const F64NEG: u8 = 0x9a; -pub const F64CEIL: u8 = 0x9b; -pub const F64FLOOR: u8 = 0x9c; -pub const F64TRUNC: u8 = 0x9d; -pub const F64NEAREST: u8 = 0x9e; -pub const F64SQRT: u8 = 0x9f; -pub const F64ADD: u8 = 0xa0; -pub const F64SUB: u8 = 0xa1; -pub const F64MUL: u8 = 0xa2; -pub const F64DIV: u8 = 0xa3; -pub const F64MIN: u8 = 0xa4; -pub const F64MAX: u8 = 0xa5; -pub const F64COPYSIGN: u8 = 0xa6; + I64CLZ = 0x79, + I64CTZ = 0x7a, + I64POPCNT = 0x7b, + I64ADD = 0x7c, + I64SUB = 0x7d, + I64MUL = 0x7e, + I64DIVS = 0x7f, + I64DIVU = 0x80, + I64REMS = 0x81, + I64REMU = 0x82, + I64AND = 0x83, + I64OR = 0x84, + I64XOR = 0x85, + I64SHL = 0x86, + I64SHRS = 0x87, + I64SHRU = 0x88, + I64ROTL = 0x89, + I64ROTR = 0x8a, + F32ABS = 0x8b, + F32NEG = 0x8c, + F32CEIL = 0x8d, + F32FLOOR = 0x8e, + F32TRUNC = 0x8f, + F32NEAREST = 0x90, + F32SQRT = 0x91, + F32ADD = 0x92, + F32SUB = 0x93, + F32MUL = 0x94, + F32DIV = 0x95, + F32MIN = 0x96, + F32MAX = 0x97, + F32COPYSIGN = 0x98, + F64ABS = 0x99, + F64NEG = 0x9a, + F64CEIL = 0x9b, + F64FLOOR = 0x9c, + F64TRUNC = 0x9d, + F64NEAREST = 0x9e, + F64SQRT = 0x9f, + F64ADD = 0xa0, + F64SUB = 0xa1, + F64MUL = 0xa2, + F64DIV = 0xa3, + F64MIN = 0xa4, + F64MAX = 0xa5, + F64COPYSIGN = 0xa6, -pub const I32WRAPI64: u8 = 0xa7; -pub const I32TRUNCSF32: u8 = 0xa8; -pub const I32TRUNCUF32: u8 = 0xa9; -pub const I32TRUNCSF64: u8 = 0xaa; -pub const I32TRUNCUF64: u8 = 0xab; -pub const I64EXTENDSI32: u8 = 0xac; -pub const I64EXTENDUI32: u8 = 0xad; -pub const I64TRUNCSF32: u8 = 0xae; -pub const I64TRUNCUF32: u8 = 0xaf; -pub const I64TRUNCSF64: u8 = 0xb0; -pub const I64TRUNCUF64: u8 = 0xb1; -pub const F32CONVERTSI32: u8 = 0xb2; -pub const F32CONVERTUI32: u8 = 0xb3; -pub const F32CONVERTSI64: u8 = 0xb4; -pub const F32CONVERTUI64: u8 = 0xb5; -pub const F32DEMOTEF64: u8 = 0xb6; -pub const F64CONVERTSI32: u8 = 0xb7; -pub const F64CONVERTUI32: u8 = 0xb8; -pub const F64CONVERTSI64: u8 = 0xb9; -pub const F64CONVERTUI64: u8 = 0xba; -pub const F64PROMOTEF32: u8 = 0xbb; + I32WRAPI64 = 0xa7, + I32TRUNCSF32 = 0xa8, + I32TRUNCUF32 = 0xa9, + I32TRUNCSF64 = 0xaa, + I32TRUNCUF64 = 0xab, + I64EXTENDSI32 = 0xac, + I64EXTENDUI32 = 0xad, + I64TRUNCSF32 = 0xae, + I64TRUNCUF32 = 0xaf, + I64TRUNCSF64 = 0xb0, + I64TRUNCUF64 = 0xb1, + F32CONVERTSI32 = 0xb2, + F32CONVERTUI32 = 0xb3, + F32CONVERTSI64 = 0xb4, + F32CONVERTUI64 = 0xb5, + F32DEMOTEF64 = 0xb6, + F64CONVERTSI32 = 0xb7, + F64CONVERTUI32 = 0xb8, + F64CONVERTSI64 = 0xb9, + F64CONVERTUI64 = 0xba, + F64PROMOTEF32 = 0xbb, -pub const I32REINTERPRETF32: u8 = 0xbc; -pub const I64REINTERPRETF64: u8 = 0xbd; -pub const F32REINTERPRETI32: u8 = 0xbe; -pub const F64REINTERPRETI64: u8 = 0xbf; + I32REINTERPRETF32 = 0xbc, + I64REINTERPRETF64 = 0xbd, + F32REINTERPRETI32 = 0xbe, + F64REINTERPRETI64 = 0xbf, +} diff --git a/compiler/gen_wasm/src/wasm_module/sections.rs b/compiler/gen_wasm/src/wasm_module/sections.rs index dc0b3ec978..2ff0805604 100644 --- a/compiler/gen_wasm/src/wasm_module/sections.rs +++ b/compiler/gen_wasm/src/wasm_module/sections.rs @@ -4,7 +4,7 @@ use bumpalo::Bump; use super::linking::{ IndexRelocType, LinkingSection, RelocationEntry, RelocationSection, SymInfo, WasmObjectSymbol, }; -use super::opcodes; +use super::opcodes::OpCode; use super::serialize::{SerialBuffer, Serialize}; use super::{CodeBuilder, ValueType}; @@ -353,23 +353,23 @@ impl Serialize for ConstExpr { fn serialize(&self, buffer: &mut T) { match self { ConstExpr::I32(x) => { - buffer.append_u8(opcodes::I32CONST); + buffer.append_u8(OpCode::I32CONST as u8); buffer.encode_i32(*x); } ConstExpr::I64(x) => { - buffer.append_u8(opcodes::I64CONST); + buffer.append_u8(OpCode::I64CONST as u8); buffer.encode_i64(*x); } ConstExpr::F32(x) => { - buffer.append_u8(opcodes::F32CONST); + buffer.append_u8(OpCode::F32CONST as u8); buffer.encode_f32(*x); } ConstExpr::F64(x) => { - buffer.append_u8(opcodes::F64CONST); + buffer.append_u8(OpCode::F64CONST as u8); buffer.encode_f64(*x); } } - buffer.append_u8(opcodes::END); + buffer.append_u8(OpCode::END as u8); } } diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 60e8d7a5f2..7cb65d0dbe 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -1734,7 +1734,7 @@ fn update<'a>( let mut shorthands = (*state.arc_shorthands).lock(); for (shorthand, package_or_path) in header.packages.iter() { - shorthands.insert(shorthand, package_or_path.clone()); + shorthands.insert(shorthand, *package_or_path); } if let PkgConfig { @@ -2273,7 +2273,7 @@ fn finish_specialization( let package_or_path = match platform_path { Valid(To::ExistingPackage(shorthand)) => { match (*state.arc_shorthands).lock().get(shorthand) { - Some(p_or_p) => p_or_p.clone(), + Some(p_or_p) => *p_or_p, None => unreachable!(), } } @@ -2978,7 +2978,7 @@ fn send_header<'a>( package_or_path, .. } => { - package_entries.insert(*shorthand, package_or_path.value.clone()); + package_entries.insert(*shorthand, package_or_path.value); } SpaceBefore(inner, _) | SpaceAfter(inner, _) => { parse_entries.push(inner); @@ -3211,7 +3211,7 @@ fn send_header_two<'a>( package_or_path, .. } => { - package_entries.insert(*shorthand, package_or_path.value.clone()); + package_entries.insert(*shorthand, package_or_path.value); } SpaceBefore(inner, _) | SpaceAfter(inner, _) => { parse_entries.push(inner); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 9aaea0c23d..31a02a5050 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -1070,6 +1070,7 @@ define_builtins! { 46 LIST_TAKE_LAST: "takeLast" 47 LIST_FIND: "find" 48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find + 49 LIST_SUBLIST: "sublist" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index 1771141780..6075ecb44c 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -19,11 +19,4 @@ ven_pretty = { path = "../../vendor/pretty" } morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.8.0", features = ["collections"] } hashbrown = { version = "0.11.2", features = [ "bumpalo" ] } -ven_ena = { path = "../../vendor/ena" } ven_graph = { path = "../../vendor/pathfinding" } -linked-hash-map = "0.5.4" - -[dev-dependencies] -roc_builtins = { path = "../builtins" } -roc_parse = { path = "../parse" } -roc_solve = { path = "../solve" } diff --git a/compiler/mono/src/expand_rc.rs b/compiler/mono/src/expand_rc.rs index 2812252285..9e311f2ed9 100644 --- a/compiler/mono/src/expand_rc.rs +++ b/compiler/mono/src/expand_rc.rs @@ -1,8 +1,46 @@ +/// UNUSED +/// +/// but kept for future reference +/// +/// +// pub fn optimize_refcount_operations<'i, T>( +// arena: &'a Bump, +// home: ModuleId, +// ident_ids: &'i mut IdentIds, +// procs: &mut MutMap>, +// ) { +// use crate::expand_rc; +// +// let deferred = expand_rc::Deferred { +// inc_dec_map: Default::default(), +// assignments: Vec::new_in(arena), +// decrefs: Vec::new_in(arena), +// }; +// +// let mut env = expand_rc::Env { +// home, +// arena, +// ident_ids, +// layout_map: Default::default(), +// alias_map: Default::default(), +// constructor_map: Default::default(), +// deferred, +// }; +// +// for (_, proc) in procs.iter_mut() { +// let b = expand_rc::expand_and_cancel_proc( +// &mut env, +// arena.alloc(proc.body.clone()), +// proc.args, +// ); +// proc.body = b.clone(); +// } +// } use crate::ir::{BranchInfo, Expr, ModifyRc, Stmt}; use crate::layout::{Layout, UnionLayout}; use bumpalo::collections::Vec; use bumpalo::Bump; -use linked_hash_map::LinkedHashMap; +// use linked_hash_map::LinkedHashMap; use roc_collections::all::MutMap; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index bb84793eac..c0c02f3466 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -370,40 +370,6 @@ impl<'a> Proc<'a> { } } - pub fn optimize_refcount_operations<'i, T>( - arena: &'a Bump, - home: ModuleId, - ident_ids: &'i mut IdentIds, - procs: &mut MutMap>, - ) { - use crate::expand_rc; - - let deferred = expand_rc::Deferred { - inc_dec_map: Default::default(), - assignments: Vec::new_in(arena), - decrefs: Vec::new_in(arena), - }; - - let mut env = expand_rc::Env { - home, - arena, - ident_ids, - layout_map: Default::default(), - alias_map: Default::default(), - constructor_map: Default::default(), - deferred, - }; - - for (_, proc) in procs.iter_mut() { - let b = expand_rc::expand_and_cancel_proc( - &mut env, - arena.alloc(proc.body.clone()), - proc.args, - ); - proc.body = b.clone(); - } - } - fn make_tail_recursive(&mut self, env: &mut Env<'a, '_>) { let mut args = Vec::with_capacity_in(self.args.len(), env.arena); let mut proc_args = Vec::with_capacity_in(self.args.len(), env.arena); diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index 659b3f7cc8..b1986bc2f8 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -4,7 +4,6 @@ pub mod alias_analysis; pub mod borrow; -pub mod expand_rc; pub mod inc_dec; pub mod ir; pub mod layout; diff --git a/compiler/parse/src/header.rs b/compiler/parse/src/header.rs index b373bd3f59..fb6f50b481 100644 --- a/compiler/parse/src/header.rs +++ b/compiler/parse/src/header.rs @@ -9,13 +9,13 @@ use crate::string_literal; use bumpalo::collections::Vec; use roc_region::all::Loc; -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct PackageName<'a> { pub account: &'a str, pub pkg: &'a str, } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum Version<'a> { Exact(&'a str), Range { @@ -32,7 +32,7 @@ pub enum VersionComparison { DisallowsEqual, } -#[derive(Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum PackageOrPath<'a> { Package(PackageName<'a>, Version<'a>), Path(StrLiteral<'a>), @@ -240,7 +240,7 @@ pub enum TypedIdent<'a> { SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum PackageEntry<'a> { Entry { shorthand: &'a str, diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index 72298d6209..b4088b5fdf 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -586,6 +586,7 @@ struct Packages<'a> { before_packages_keyword: &'a [CommentOrNewline<'a>], after_packages_keyword: &'a [CommentOrNewline<'a>], + final_comments: &'a [CommentOrNewline<'a>], } #[inline(always)] @@ -602,21 +603,24 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { EPackages::IndentPackages, EPackages::IndentListStart ), - collection_e!( + collection_trailing_sep_e!( word1(b'{', EPackages::ListStart), specialize(EPackages::PackageEntry, loc!(package_entry())), word1(b',', EPackages::ListEnd), word1(b'}', EPackages::ListEnd), min_indent, + EPackages::Open, EPackages::Space, - EPackages::IndentListEnd + EPackages::IndentListEnd, + PackageEntry::SpaceBefore ) ), - |((before_packages_keyword, after_packages_keyword), entries)| { + |((before_packages_keyword, after_packages_keyword), (entries, final_comments))| { Packages { entries, before_packages_keyword, after_packages_keyword, + final_comments, } } ) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 89e4968668..b7e7e3bf74 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -265,6 +265,7 @@ pub enum ETypedIdent<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum EPackages<'a> { + Open(Row, Col), Space(BadInputError, Row, Col), Packages(Row, Col), IndentPackages(Row, Col), diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 38a2b6ae7a..676863e87d 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -3223,6 +3223,62 @@ mod test_parse { assert_eq!(Ok(expected), actual); } + #[test] + fn full_app_header_trailing_commas() { + use ExposesEntry::Exposed; + use PackageOrPath::Path; + + let newlines = &[Newline]; + let pkg_entry = PackageEntry::Entry { + shorthand: "base", + spaces_after_shorthand: &[], + package_or_path: Located::new(1, 1, 21, 33, Path(PlainLine("./platform"))), + }; + let loc_pkg_entry = Located::new(1, 1, 15, 33, pkg_entry); + let arena = Bump::new(); + let packages = bumpalo::vec![in &arena; loc_pkg_entry]; + let import = ImportsEntry::Package("foo", ModuleName::new("Bar.Baz"), Vec::new_in(&arena)); + let loc_import = Located::new(2, 2, 14, 25, import); + let imports = bumpalo::vec![in &arena; loc_import]; + let provide_entry = Located::new(3, 3, 15, 24, Exposed("quicksort")); + let provides = bumpalo::vec![in &arena; provide_entry]; + let module_name = StrLiteral::PlainLine("quicksort"); + + let header = AppHeader { + before_header: &[], + name: Located::new(0, 0, 4, 15, module_name), + packages, + imports, + provides, + to: Located::new(3, 3, 30, 34, To::ExistingPackage("base")), + after_app_keyword: &[], + before_packages: newlines, + after_packages: &[], + before_imports: newlines, + after_imports: &[], + before_provides: newlines, + after_provides: &[], + before_to: &[], + after_to: &[], + }; + + let expected = roc_parse::ast::Module::App { header }; + + let src = indoc!( + r#" + app "quicksort" + packages { base: "./platform", } + imports [ foo.Bar.Baz ] + provides [ quicksort ] to base + "# + ); + + let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes())) + .map(|tuple| tuple.0); + + assert_eq!(Ok(expected), actual); + } + #[test] fn empty_platform_header() { let pkg_name = PackageName { diff --git a/compiler/reporting/Cargo.toml b/compiler/reporting/Cargo.toml index a0f87252e1..35df3f33ae 100644 --- a/compiler/reporting/Cargo.toml +++ b/compiler/reporting/Cargo.toml @@ -16,8 +16,6 @@ roc_can = { path = "../can" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } ven_pretty = { path = "../../vendor/pretty" } -im = "15.0.0" -im-rc = "15.0.0" distance = "0.4.0" bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 70894ddabc..dcf74d79bb 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -3781,6 +3781,18 @@ mod solve_expr { ); } + #[test] + fn list_sublist() { + infer_eq_without_problem( + indoc!( + r#" + List.sublist + "# + ), + "List a, { len : Nat, start : Nat } -> List a", + ); + } + #[test] fn list_drop_last() { infer_eq_without_problem( diff --git a/compiler/test_gen/Cargo.toml b/compiler/test_gen/Cargo.toml index ce94be492d..9e1a5d5aba 100644 --- a/compiler/test_gen/Cargo.toml +++ b/compiler/test_gen/Cargo.toml @@ -29,8 +29,6 @@ roc_can = { path = "../can" } roc_parse = { path = "../parse" } roc_build = { path = "../build" } roc_std = { path = "../../roc_std" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } either = "1.6.1" libc = "0.2.106" diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index baf17d2b41..5e0f737e72 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -208,6 +208,46 @@ fn list_take_last() { ); } +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn list_sublist() { + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 0 , len: 2 } ", + RocList::from_slice(&[1, 2]), + RocList + ); + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 1 , len: 2 } ", + RocList::from_slice(&[2, 3]), + RocList + ); + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 2 , len: 2 } ", + RocList::from_slice(&[3]), + RocList + ); + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 3 , len: 2 } ", + RocList::from_slice(&[]), + RocList + ); + assert_evals_to!( + "List.sublist [] { start: 1 , len: 1 } ", + RocList::from_slice(&[]), + RocList + ); + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 1 , len: 0 } ", + RocList::from_slice(&[]), + RocList + ); + assert_evals_to!( + "List.sublist [1, 2, 3] { start: 0 , len: 5 } ", + RocList::from_slice(&[1, 2, 3]), + RocList + ); +} + #[test] #[cfg(any(feature = "gen-llvm"))] fn list_drop() { @@ -2301,7 +2341,7 @@ fn list_any() { #[test] #[cfg(any(feature = "gen-llvm"))] -#[ignore] +#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)] fn list_any_empty_with_unknown_element_type() { // Segfaults with invalid memory reference. Running this as a stand-alone // Roc program, generates the following error message: diff --git a/compiler/test_gen/src/gen_num.rs b/compiler/test_gen/src/gen_num.rs index f1f0bdd27b..adbeac667c 100644 --- a/compiler/test_gen/src/gen_num.rs +++ b/compiler/test_gen/src/gen_num.rs @@ -530,7 +530,7 @@ fn f64_round() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn f64_abs() { assert_evals_to!("Num.abs -4.7", 4.7, f64); assert_evals_to!("Num.abs 5.8", 5.8, f64); @@ -539,7 +539,7 @@ fn f64_abs() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn i64_abs() { assert_evals_to!("Num.abs -6", 6, i64); assert_evals_to!("Num.abs 7", 7, i64); @@ -713,7 +713,7 @@ fn gen_int_eq() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_int_neq() { assert_evals_to!( indoc!( @@ -767,7 +767,7 @@ fn gen_dec_neq() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_wrap_int_neq() { assert_evals_to!( indoc!( @@ -950,14 +950,14 @@ fn gen_rem_div_by_zero_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_zero_i64() { assert_evals_to!("Num.isZero 0", true, bool); assert_evals_to!("Num.isZero 1", false, bool); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_positive_i64() { assert_evals_to!("Num.isPositive 0", false, bool); assert_evals_to!("Num.isPositive 1", true, bool); @@ -965,7 +965,7 @@ fn gen_is_positive_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_negative_i64() { assert_evals_to!("Num.isNegative 0", false, bool); assert_evals_to!("Num.isNegative 3", false, bool); @@ -973,7 +973,7 @@ fn gen_is_negative_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_positive_f64() { assert_evals_to!("Num.isPositive 0.0", false, bool); assert_evals_to!("Num.isPositive 4.7", true, bool); @@ -981,7 +981,7 @@ fn gen_is_positive_f64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_negative_f64() { assert_evals_to!("Num.isNegative 0.0", false, bool); assert_evals_to!("Num.isNegative 9.9", false, bool); @@ -989,7 +989,7 @@ fn gen_is_negative_f64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_zero_f64() { assert_evals_to!("Num.isZero 0", true, bool); assert_evals_to!("Num.isZero 0_0", true, bool); @@ -998,14 +998,14 @@ fn gen_is_zero_f64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_odd() { assert_evals_to!("Num.isOdd 4", false, bool); assert_evals_to!("Num.isOdd 5", true, bool); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gen_is_even() { assert_evals_to!("Num.isEven 6", true, bool); assert_evals_to!("Num.isEven 7", false, bool); @@ -1033,7 +1033,7 @@ fn tan() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn bitwise_and() { assert_evals_to!("Num.bitwiseAnd 20 20", 20, i64); assert_evals_to!("Num.bitwiseAnd 25 10", 8, i64); @@ -1041,7 +1041,7 @@ fn bitwise_and() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn bitwise_xor() { assert_evals_to!("Num.bitwiseXor 20 20", 0, i64); assert_evals_to!("Num.bitwiseXor 15 14", 1, i64); @@ -1050,14 +1050,14 @@ fn bitwise_xor() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn bitwise_or() { assert_evals_to!("Num.bitwiseOr 1 1", 1, i64); assert_evals_to!("Num.bitwiseOr 1 2", 3, i64); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn lt_i64() { assert_evals_to!("1 < 2", true, bool); assert_evals_to!("1 < 1", false, bool); @@ -1066,7 +1066,7 @@ fn lt_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn lte_i64() { assert_evals_to!("1 <= 1", true, bool); assert_evals_to!("2 <= 1", false, bool); @@ -1084,7 +1084,7 @@ fn gt_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gte_i64() { assert_evals_to!("1 >= 1", true, bool); assert_evals_to!("1 >= 2", false, bool); @@ -1093,7 +1093,7 @@ fn gte_i64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn lt_f64() { assert_evals_to!("1.1 < 1.2", true, bool); assert_evals_to!("1.1 < 1.1", false, bool); @@ -1102,7 +1102,7 @@ fn lt_f64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn lte_f64() { assert_evals_to!("1.1 <= 1.1", true, bool); assert_evals_to!("1.2 <= 1.1", false, bool); @@ -1120,7 +1120,7 @@ fn gt_f64() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn gte_f64() { assert_evals_to!("1.1 >= 1.1", true, bool); assert_evals_to!("1.1 >= 1.2", false, bool); @@ -1214,7 +1214,7 @@ fn tail_call_elimination() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-wasm"))] fn int_negate() { assert_evals_to!("Num.neg 123", -123, i64); assert_evals_to!("Num.neg Num.maxInt", -i64::MAX, i64); @@ -1272,19 +1272,19 @@ fn gen_basic_fn() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_to_float() { assert_evals_to!("Num.toFloat 0x9", 9.0, f64); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn num_to_float() { assert_evals_to!("Num.toFloat 9", 9.0, f64); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn float_to_float() { assert_evals_to!("Num.toFloat 0.5", 0.5, f64); } @@ -1312,13 +1312,13 @@ fn pow() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn ceiling() { assert_evals_to!("Num.ceiling 1.1", 2, i64); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn floor() { assert_evals_to!("Num.floor 1.9", 1, i64); } @@ -1379,7 +1379,7 @@ fn int_add_checked() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_add_wrap() { assert_evals_to!( indoc!( @@ -1483,7 +1483,7 @@ fn int_sub_overflow() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_sub_wrap() { assert_evals_to!( indoc!( @@ -1628,7 +1628,7 @@ fn float_negative_mul_overflow() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_mul_wrap() { assert_evals_to!( indoc!( @@ -1698,7 +1698,7 @@ fn float_mul_checked() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn shift_left_by() { assert_evals_to!("Num.shiftLeftBy 0 0b0000_0001", 0b0000_0001, i64); assert_evals_to!("Num.shiftLeftBy 1 0b0000_0001", 0b0000_0010, i64); @@ -1706,7 +1706,7 @@ fn shift_left_by() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[ignore] fn shift_right_by() { // Sign Extended Right Shift @@ -1716,7 +1716,7 @@ fn shift_right_by() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[ignore] fn shift_right_zf_by() { // Logical Right Shift @@ -1921,7 +1921,7 @@ fn bytes_to_u32_random_u8s() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn when_on_i32() { assert_evals_to!( indoc!( @@ -1944,7 +1944,7 @@ fn when_on_i32() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn when_on_i16() { assert_evals_to!( indoc!( diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 3ceb01e33d..69c00607c2 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -32,8 +32,6 @@ roc_unify = { path = "../compiler/unify" } roc_reporting = { path = "../compiler/reporting" } roc_solve = { path = "../compiler/solve" } ven_graph = { path = "../vendor/pathfinding" } -im = "15.0.0" -im-rc = "15.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } arraystring = "0.3.0" libc = "0.2.106"