mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge remote-tracking branch 'origin/trunk' into tag-union-imitate-rust
This commit is contained in:
commit
c827256e47
41 changed files with 957 additions and 532 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!(),
|
||||
}?;
|
||||
|
||||
|
|
|
@ -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 = '─';
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -992,6 +992,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
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,
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -94,6 +94,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
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();
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -952,7 +952,7 @@ pub fn local_successors<'a>(
|
|||
references: &'a References,
|
||||
closures: &'a MutMap<Symbol, References>,
|
||||
) -> ImSet<Symbol> {
|
||||
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<Symbol, References>) -> ImSet<Symbol> {
|
||||
let mut answer = im_rc::hashset::HashSet::default();
|
||||
let mut answer = ImSet::default();
|
||||
let mut seen = MutSet::default();
|
||||
let mut queue = vec![call_symbol];
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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::<u64>();
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum RocCallResult<T> {
|
||||
Success(T),
|
||||
Failure(*mut c_char),
|
||||
#[repr(C)]
|
||||
pub struct RocCallResult<T> {
|
||||
tag: u64,
|
||||
error_msg: *mut c_char,
|
||||
value: MaybeUninit<T>,
|
||||
}
|
||||
|
||||
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
||||
fn from(call_result: RocCallResult<T>) -> 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::<RocCallResult<()>>() + $bytes;
|
||||
let layout = std::alloc::Layout::array::<u8>(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::<RocCallResult<()>>()) as *const u8)
|
||||
} else {
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
|
|
|
@ -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<ValueType>,
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 { .. })
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
360
compiler/gen_wasm/src/low_level.rs
Normal file
360
compiler/gen_wasm/src/low_level.rs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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<T: SerialBuffer>(&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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<T, Proc<'a>>,
|
||||
// ) {
|
||||
// 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};
|
||||
|
||||
|
|
|
@ -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<T, Proc<'a>>,
|
||||
) {
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [1, 2, 3] { start: 1 , len: 2 } ",
|
||||
RocList::from_slice(&[2, 3]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [1, 2, 3] { start: 2 , len: 2 } ",
|
||||
RocList::from_slice(&[3]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [1, 2, 3] { start: 3 , len: 2 } ",
|
||||
RocList::from_slice(&[]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [] { start: 1 , len: 1 } ",
|
||||
RocList::from_slice(&[]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [1, 2, 3] { start: 1 , len: 0 } ",
|
||||
RocList::from_slice(&[]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.sublist [1, 2, 3] { start: 0 , len: 5 } ",
|
||||
RocList::from_slice(&[1, 2, 3]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
#[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:
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue