mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge remote-tracking branch 'origin/trunk' into tag-union-imitate-rust
This commit is contained in:
commit
a9d483cb60
58 changed files with 5821 additions and 843 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,3 +42,6 @@ earthly_log.txt
|
||||||
|
|
||||||
# created to test release
|
# created to test release
|
||||||
roc_linux_x86_64.tar.gz
|
roc_linux_x86_64.tar.gz
|
||||||
|
|
||||||
|
# macOS .DS_Store files
|
||||||
|
.DS_Store
|
||||||
|
|
3
AUTHORS
3
AUTHORS
|
@ -48,3 +48,6 @@ Luiz Carlos L. G. de Oliveira <luizcarlos1405@gmail.com>
|
||||||
Oleksii Skidan <al.skidan@gmail.com>
|
Oleksii Skidan <al.skidan@gmail.com>
|
||||||
Martin Janiczek <martin@janiczek.cz>
|
Martin Janiczek <martin@janiczek.cz>
|
||||||
Eric Newbury <enewbury@users.noreply.github.com>
|
Eric Newbury <enewbury@users.noreply.github.com>
|
||||||
|
Ayaz Hafiz <ayaz.hafiz.1@gmail.com>
|
||||||
|
Johannes Maas <github@j-maas.de>
|
||||||
|
Takeshi Sato <doublequotation@gmail.com>
|
||||||
|
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -3408,8 +3408,6 @@ dependencies = [
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"target-lexicon",
|
|
||||||
"wasmer",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4113,7 +4111,6 @@ dependencies = [
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
"roc_gen_llvm",
|
"roc_gen_llvm",
|
||||||
"roc_gen_wasm",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
|
@ -4127,6 +4124,7 @@ dependencies = [
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"test_wasm_util",
|
||||||
"wasmer",
|
"wasmer",
|
||||||
"wasmer-wasi",
|
"wasmer-wasi",
|
||||||
]
|
]
|
||||||
|
@ -4172,10 +4170,19 @@ dependencies = [
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
|
"test_wasm_util",
|
||||||
"wasmer",
|
"wasmer",
|
||||||
"wasmer-wasi",
|
"wasmer-wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_wasm_util"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std",
|
||||||
|
"wasmer",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -15,7 +15,6 @@ members = [
|
||||||
"compiler/reporting",
|
"compiler/reporting",
|
||||||
"compiler/fmt",
|
"compiler/fmt",
|
||||||
"compiler/mono",
|
"compiler/mono",
|
||||||
"compiler/test_mono_macros",
|
|
||||||
"compiler/test_mono",
|
"compiler/test_mono",
|
||||||
"compiler/load",
|
"compiler/load",
|
||||||
"compiler/gen_llvm",
|
"compiler/gen_llvm",
|
||||||
|
@ -33,14 +32,20 @@ members = [
|
||||||
"editor",
|
"editor",
|
||||||
"ast",
|
"ast",
|
||||||
"cli",
|
"cli",
|
||||||
"cli/cli_utils",
|
|
||||||
"code_markup",
|
"code_markup",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"utils",
|
"utils",
|
||||||
"docs",
|
"docs",
|
||||||
"linker",
|
"linker",
|
||||||
]
|
]
|
||||||
exclude = [ "ci/bench-runner" ]
|
exclude = [
|
||||||
|
"ci/bench-runner",
|
||||||
|
# Ignore building these normally. They are only imported by tests.
|
||||||
|
# The tests will still correctly build them.
|
||||||
|
"cli_utils",
|
||||||
|
"compiler/test_mono_macros",
|
||||||
|
"compiler/test_wasm_util",
|
||||||
|
]
|
||||||
# Needed to be able to run `cargo run -p roc_cli --no-default-features` -
|
# Needed to be able to run `cargo run -p roc_cli --no-default-features` -
|
||||||
# see www/build.sh for more.
|
# see www/build.sh for more.
|
||||||
#
|
#
|
||||||
|
|
|
@ -47,7 +47,7 @@ install-zig-llvm-valgrind-clippy-rustfmt:
|
||||||
|
|
||||||
copy-dirs:
|
copy-dirs:
|
||||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||||
COPY --dir cli compiler docs editor ast code_markup utils roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
COPY --dir cli cli_utils compiler docs editor ast code_markup utils roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
||||||
|
|
||||||
test-zig:
|
test-zig:
|
||||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||||
|
@ -67,7 +67,7 @@ check-rustfmt:
|
||||||
|
|
||||||
check-typos:
|
check-typos:
|
||||||
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
|
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
|
||||||
COPY --dir .github ci cli compiler docs editor examples ast code_markup utils linker nightly_benches packages roc_std www *.md LEGAL_DETAILS shell.nix version.txt ./
|
COPY --dir .github ci cli cli_utils compiler docs editor examples ast code_markup utils linker nightly_benches packages roc_std www *.md LEGAL_DETAILS shell.nix version.txt ./
|
||||||
RUN typos
|
RUN typos
|
||||||
|
|
||||||
test-rust:
|
test-rust:
|
||||||
|
@ -79,7 +79,10 @@ test-rust:
|
||||||
# not pre-compiling the host can cause race conditions
|
# not pre-compiling the host can cause race conditions
|
||||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --features with_sound && sccache --show-stats
|
cargo test --release --features with_sound --workspace --exclude test_wasm && sccache --show-stats
|
||||||
|
# test_wasm has some multithreading problems to do with the wasmer runtime. Run it single-threaded as a separate job
|
||||||
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
|
cargo test --release --package test_wasm -- --test-threads=1 && sccache --show-stats
|
||||||
# run i386 (32-bit linux) cli tests
|
# run i386 (32-bit linux) cli tests
|
||||||
RUN echo "4" | cargo run --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
|
|
|
@ -7,6 +7,8 @@ If you already know [Elm](https://elm-lang.org/), then [Roc for Elm Programmers]
|
||||||
If you're curious about where the language's name and logo came from,
|
If you're curious about where the language's name and logo came from,
|
||||||
[here's an explanation](https://github.com/rtfeldman/roc/blob/trunk/name-and-logo.md).
|
[here's an explanation](https://github.com/rtfeldman/roc/blob/trunk/name-and-logo.md).
|
||||||
|
|
||||||
|
You can get help and discuss with other people on the [Roc Zulip chat](https://roc.zulipchat.com).
|
||||||
|
|
||||||
## State of Roc
|
## State of Roc
|
||||||
|
|
||||||
Roc is not ready for production yet. You are likely to encounter bugs. Publishing packages or documentation is not yet supported.
|
Roc is not ready for production yet. You are likely to encounter bugs. Publishing packages or documentation is not yet supported.
|
||||||
|
@ -105,5 +107,4 @@ never done anything with Rust and also never worked on a compiler, but we've
|
||||||
been able to find beginner-friendly projects to get people up to speed gradually.)
|
been able to find beginner-friendly projects to get people up to speed gradually.)
|
||||||
|
|
||||||
If you're interested in getting involved, check out
|
If you're interested in getting involved, check out
|
||||||
[CONTRIBUTING.md](https://github.com/rtfeldman/roc/blob/trunk/CONTRIBUTING.md) which has more info
|
[CONTRIBUTING.md](https://github.com/rtfeldman/roc/blob/trunk/CONTRIBUTING.md)!
|
||||||
and a link to our Zulip chat!
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::{align_of, size_of, MaybeUninit};
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
|
|
||||||
pub const NODE_BYTES: usize = 32;
|
pub const NODE_BYTES: usize = 32;
|
||||||
|
@ -85,7 +85,7 @@ impl<T> Copy for NodeId<T> {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pool {
|
pub struct Pool {
|
||||||
pub(super) nodes: *mut [u8; NODE_BYTES],
|
pub(super) nodes: *mut [MaybeUninit<u8>; NODE_BYTES],
|
||||||
num_nodes: u32,
|
num_nodes: u32,
|
||||||
capacity: u32,
|
capacity: u32,
|
||||||
// free_1node_slots: Vec<NodeId<T>>,
|
// free_1node_slots: Vec<NodeId<T>>,
|
||||||
|
@ -116,7 +116,7 @@ impl Pool {
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
} as *mut [u8; NODE_BYTES];
|
} as *mut [MaybeUninit<u8>; NODE_BYTES];
|
||||||
|
|
||||||
// This is our actual capacity, in nodes.
|
// This is our actual capacity, in nodes.
|
||||||
// It might be higher than the requested capacity due to rounding up
|
// It might be higher than the requested capacity due to rounding up
|
||||||
|
@ -141,9 +141,10 @@ impl Pool {
|
||||||
);
|
);
|
||||||
|
|
||||||
let node_id = self.reserve(1);
|
let node_id = self.reserve(1);
|
||||||
let node_ptr = unsafe { self.nodes.offset(node_id.index as isize) } as *mut T;
|
|
||||||
|
|
||||||
unsafe { *node_ptr = node };
|
let node_ptr = self.get_ptr(node_id);
|
||||||
|
|
||||||
|
unsafe { node_ptr.write(MaybeUninit::new(node)) };
|
||||||
|
|
||||||
node_id
|
node_id
|
||||||
}
|
}
|
||||||
|
@ -168,7 +169,7 @@ impl Pool {
|
||||||
|
|
||||||
pub fn get<'a, 'b, T>(&'a self, node_id: NodeId<T>) -> &'b T {
|
pub fn get<'a, 'b, T>(&'a self, node_id: NodeId<T>) -> &'b T {
|
||||||
unsafe {
|
unsafe {
|
||||||
let node_ptr = self.nodes.offset(node_id.index as isize) as *const T;
|
let node_ptr = self.get_ptr(node_id) as *const T;
|
||||||
|
|
||||||
&*node_ptr
|
&*node_ptr
|
||||||
}
|
}
|
||||||
|
@ -176,7 +177,7 @@ impl Pool {
|
||||||
|
|
||||||
pub fn get_mut<T>(&mut self, node_id: NodeId<T>) -> &mut T {
|
pub fn get_mut<T>(&mut self, node_id: NodeId<T>) -> &mut T {
|
||||||
unsafe {
|
unsafe {
|
||||||
let node_ptr = self.nodes.offset(node_id.index as isize) as *mut T;
|
let node_ptr = self.get_ptr(node_id) as *mut T;
|
||||||
|
|
||||||
&mut *node_ptr
|
&mut *node_ptr
|
||||||
}
|
}
|
||||||
|
@ -184,12 +185,21 @@ impl Pool {
|
||||||
|
|
||||||
pub fn set<T>(&mut self, node_id: NodeId<T>, element: T) {
|
pub fn set<T>(&mut self, node_id: NodeId<T>, element: T) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let node_ptr = self.nodes.offset(node_id.index as isize) as *mut T;
|
let node_ptr = self.get_ptr(node_id);
|
||||||
|
|
||||||
*node_ptr = element;
|
node_ptr.write(MaybeUninit::new(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_ptr<T>(&self, node_id: NodeId<T>) -> *mut MaybeUninit<T> {
|
||||||
|
let node_offset = unsafe { self.nodes.offset(node_id.index as isize) };
|
||||||
|
|
||||||
|
// This checks if the node_offset is aligned to T
|
||||||
|
assert!(0 == (node_offset as usize) & (align_of::<T>() - 1));
|
||||||
|
|
||||||
|
node_offset as *mut MaybeUninit<T>
|
||||||
|
}
|
||||||
|
|
||||||
// A node is available iff its bytes are all zeroes
|
// A node is available iff its bytes are all zeroes
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn is_available<T>(&self, node_id: NodeId<T>) -> bool {
|
fn is_available<T>(&self, node_id: NodeId<T>) -> bool {
|
||||||
|
|
236
cli/Cargo.lock
generated
236
cli/Cargo.lock
generated
|
@ -1,236 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "ascii"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "combine"
|
|
||||||
version = "3.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fraction"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "im-rc"
|
|
||||||
version = "13.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-bigint"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-complex"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-iter"
|
|
||||||
version = "0.1.39"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"fraction 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc-cli"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"roc 0.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver-parser"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sized-chunks"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unreachable"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "void"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[metadata]
|
|
||||||
"checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335"
|
|
||||||
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
|
||||||
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
|
|
||||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
|
||||||
"checksum combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
|
|
||||||
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
|
|
||||||
"checksum fraction 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1055159ac82fb210c813303f716b6c8db57ace9d5ec2dbbc2e1d7a864c1dd74e"
|
|
||||||
"checksum im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0197597d095c0d11107975d3175173f810ee572c2501ff4de64f4f3f119806"
|
|
||||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
|
||||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
|
||||||
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
|
|
||||||
"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db"
|
|
||||||
"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718"
|
|
||||||
"checksum num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc"
|
|
||||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
|
||||||
"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
|
|
||||||
"checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454"
|
|
||||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
|
||||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
|
||||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|
||||||
"checksum sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a2eb3fe454976eefb479f78f9b394d34d661b647c6326a3a6e66f68bb12c26"
|
|
||||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
|
||||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
|
|
@ -89,7 +89,7 @@ indoc = "1.0.3"
|
||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||||
cli_utils = { path = "cli_utils" }
|
cli_utils = { path = "../cli_utils" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "time_bench"
|
name = "time_bench"
|
||||||
|
|
3898
cli_utils/Cargo.lock
generated
Normal file
3898
cli_utils/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,10 +10,10 @@ description = "Shared code for cli tests and benchmarks"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_cli = { path = "../../cli" }
|
roc_cli = { path = "../cli" }
|
||||||
roc_collections = { path = "../../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
roc_load = { path = "../../compiler/load" }
|
roc_load = { path = "../compiler/load" }
|
||||||
roc_module = { path = "../../compiler/module" }
|
roc_module = { path = "../compiler/module" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||||
serde = { version = "1.0.130", features = ["derive"] }
|
serde = { version = "1.0.130", features = ["derive"] }
|
|
@ -20,68 +20,84 @@ pub fn build(b: *Builder) void {
|
||||||
const test_step = b.step("test", "Run tests");
|
const test_step = b.step("test", "Run tests");
|
||||||
test_step.dependOn(&main_tests.step);
|
test_step.dependOn(&main_tests.step);
|
||||||
|
|
||||||
|
// Targets
|
||||||
|
const host_target = b.standardTargetOptions(.{});
|
||||||
|
const i386_target = makeI386Target();
|
||||||
|
const wasm32_target = makeWasm32Target();
|
||||||
|
|
||||||
// LLVM IR
|
// LLVM IR
|
||||||
const obj_name = "builtins-host";
|
generateLlvmIrFile(b, mode, host_target, main_path, "ir", "builtins-host");
|
||||||
const llvm_obj = b.addObject(obj_name, main_path);
|
generateLlvmIrFile(b, mode, i386_target, main_path, "ir-i386", "builtins-i386");
|
||||||
llvm_obj.setBuildMode(mode);
|
generateLlvmIrFile(b, mode, wasm32_target, main_path, "ir-wasm32", "builtins-wasm32");
|
||||||
llvm_obj.linkSystemLibrary("c");
|
|
||||||
llvm_obj.strip = true;
|
|
||||||
llvm_obj.emit_llvm_ir = true;
|
|
||||||
llvm_obj.emit_bin = false;
|
|
||||||
const ir = b.step("ir", "Build LLVM ir");
|
|
||||||
ir.dependOn(&llvm_obj.step);
|
|
||||||
|
|
||||||
// 32-bit x86, useful for debugging
|
// Generate Object Files
|
||||||
var i386_target = CrossTarget.parse(.{}) catch unreachable;
|
generateObjectFile(b, mode, host_target, main_path, "object", "builtins-host");
|
||||||
|
generateObjectFile(b, mode, wasm32_target, main_path, "wasm32-object", "builtins-wasm32");
|
||||||
|
|
||||||
i386_target.cpu_arch = std.Target.Cpu.Arch.i386;
|
removeInstallSteps(b);
|
||||||
i386_target.os_tag = std.Target.Os.Tag.linux;
|
}
|
||||||
i386_target.abi = std.Target.Abi.musl;
|
|
||||||
|
|
||||||
const obj_name_i386 = "builtins-i386";
|
// TODO zig 0.9 can generate .bc directly, switch to that when it is released!
|
||||||
const llvm_obj_i386 = b.addObject(obj_name_i386, main_path);
|
fn generateLlvmIrFile(
|
||||||
llvm_obj_i386.setBuildMode(mode);
|
b: *Builder,
|
||||||
llvm_obj_i386.strip = true;
|
mode: std.builtin.Mode,
|
||||||
llvm_obj_i386.emit_llvm_ir = true;
|
target: CrossTarget,
|
||||||
llvm_obj_i386.emit_bin = false;
|
main_path: []const u8,
|
||||||
llvm_obj_i386.target = i386_target;
|
step_name: []const u8,
|
||||||
|
object_name: []const u8,
|
||||||
|
) void {
|
||||||
|
const obj = b.addObject(object_name, main_path);
|
||||||
|
obj.setBuildMode(mode);
|
||||||
|
obj.strip = true;
|
||||||
|
obj.emit_llvm_ir = true;
|
||||||
|
obj.emit_bin = false;
|
||||||
|
obj.target = target;
|
||||||
|
|
||||||
const ir_i386 = b.step("ir-i386", "Build LLVM ir for 32-bit targets (x86)");
|
const ir = b.step(step_name, "Build LLVM ir");
|
||||||
ir_i386.dependOn(&llvm_obj_i386.step);
|
ir.dependOn(&obj.step);
|
||||||
|
}
|
||||||
|
|
||||||
// LLVM IR 32-bit (wasm)
|
// Generate Object File
|
||||||
var wasm32_target = CrossTarget.parse(.{}) catch unreachable;
|
// TODO: figure out how to get this to emit symbols that are only scoped to linkage (global but hidden).
|
||||||
|
// Also, zig has -ffunction-sections, but I am not sure how to add it here.
|
||||||
// 32-bit wasm
|
// With both of those changes, unused zig functions will be cleaned up by the linker saving around 100k.
|
||||||
wasm32_target.cpu_arch = std.Target.Cpu.Arch.wasm32;
|
fn generateObjectFile(
|
||||||
wasm32_target.os_tag = std.Target.Os.Tag.freestanding;
|
b: *Builder,
|
||||||
wasm32_target.abi = std.Target.Abi.none;
|
mode: std.builtin.Mode,
|
||||||
|
target: CrossTarget,
|
||||||
const obj_name_wasm32 = "builtins-wasm32";
|
main_path: []const u8,
|
||||||
const llvm_obj_wasm32 = b.addObject(obj_name_wasm32, main_path);
|
step_name: []const u8,
|
||||||
llvm_obj_wasm32.setBuildMode(mode);
|
object_name: []const u8,
|
||||||
llvm_obj_wasm32.strip = true;
|
) void {
|
||||||
llvm_obj_wasm32.emit_llvm_ir = true;
|
const obj = b.addObject(object_name, main_path);
|
||||||
llvm_obj_wasm32.emit_bin = false;
|
|
||||||
llvm_obj_wasm32.target = wasm32_target;
|
|
||||||
|
|
||||||
const ir_wasm32 = b.step("ir-wasm32", "Build LLVM ir for 32-bit targets (wasm)");
|
|
||||||
ir_wasm32.dependOn(&llvm_obj_wasm32.step);
|
|
||||||
|
|
||||||
// Object File
|
|
||||||
// TODO: figure out how to get this to emit symbols that are only scoped to linkage (global but hidden).
|
|
||||||
// Also, zig has -ffunction-sections, but I am not sure how to add it here.
|
|
||||||
// With both of those changes, unused zig functions will be cleaned up by the linker saving around 100k.
|
|
||||||
const obj = b.addObject("builtins-host", main_path);
|
|
||||||
obj.setBuildMode(mode);
|
obj.setBuildMode(mode);
|
||||||
obj.linkSystemLibrary("c");
|
obj.linkSystemLibrary("c");
|
||||||
obj.setOutputDir(".");
|
obj.setOutputDir(".");
|
||||||
obj.strip = true;
|
obj.strip = true;
|
||||||
const obj_step = b.step("object", "Build object file for linking");
|
obj.target = target;
|
||||||
|
const obj_step = b.step(step_name, "Build object file for linking");
|
||||||
obj_step.dependOn(&obj.step);
|
obj_step.dependOn(&obj.step);
|
||||||
|
}
|
||||||
|
|
||||||
b.default_step = ir;
|
fn makeI386Target() CrossTarget {
|
||||||
removeInstallSteps(b);
|
var target = CrossTarget.parse(.{}) catch unreachable;
|
||||||
|
|
||||||
|
target.cpu_arch = std.Target.Cpu.Arch.i386;
|
||||||
|
target.os_tag = std.Target.Os.Tag.linux;
|
||||||
|
target.abi = std.Target.Abi.musl;
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn makeWasm32Target() CrossTarget {
|
||||||
|
var target = CrossTarget.parse(.{}) catch unreachable;
|
||||||
|
|
||||||
|
// 32-bit wasm
|
||||||
|
target.cpu_arch = std.Target.Cpu.Arch.wasm32;
|
||||||
|
target.os_tag = std.Target.Os.Tag.freestanding;
|
||||||
|
target.abi = std.Target.Abi.none;
|
||||||
|
|
||||||
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn removeInstallSteps(b: *Builder) void {
|
fn removeInstallSteps(b: *Builder) void {
|
||||||
|
|
|
@ -1078,6 +1078,36 @@ pub fn listSortWith(
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listAny(
|
||||||
|
list: RocList,
|
||||||
|
caller: Caller1,
|
||||||
|
data: Opaque,
|
||||||
|
inc_n_data: IncN,
|
||||||
|
data_is_owned: bool,
|
||||||
|
element_width: usize,
|
||||||
|
) callconv(.C) bool {
|
||||||
|
if (list.bytes) |source_ptr| {
|
||||||
|
const size = list.len();
|
||||||
|
|
||||||
|
if (data_is_owned) {
|
||||||
|
inc_n_data(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
var satisfied = false;
|
||||||
|
while (i < size) : (i += 1) {
|
||||||
|
const element = source_ptr + i * element_width;
|
||||||
|
caller(data, element, @ptrCast(?[*]u8, &satisfied));
|
||||||
|
|
||||||
|
if (satisfied) {
|
||||||
|
return satisfied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// SWAP ELEMENTS
|
// SWAP ELEMENTS
|
||||||
|
|
||||||
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
||||||
|
|
|
@ -4,6 +4,7 @@ const utils = @import("utils.zig");
|
||||||
|
|
||||||
const ROC_BUILTINS = "roc_builtins";
|
const ROC_BUILTINS = "roc_builtins";
|
||||||
const NUM = "num";
|
const NUM = "num";
|
||||||
|
const STR = "str";
|
||||||
|
|
||||||
// Dec Module
|
// Dec Module
|
||||||
const dec = @import("dec.zig");
|
const dec = @import("dec.zig");
|
||||||
|
@ -49,6 +50,7 @@ comptime {
|
||||||
exportListFn(list.listSet, "set");
|
exportListFn(list.listSet, "set");
|
||||||
exportListFn(list.listSetInPlace, "set_in_place");
|
exportListFn(list.listSetInPlace, "set_in_place");
|
||||||
exportListFn(list.listSwap, "swap");
|
exportListFn(list.listSwap, "swap");
|
||||||
|
exportListFn(list.listAny, "any");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dict Module
|
// Dict Module
|
||||||
|
@ -115,7 +117,6 @@ comptime {
|
||||||
exportStrFn(str.strConcatC, "concat");
|
exportStrFn(str.strConcatC, "concat");
|
||||||
exportStrFn(str.strJoinWithC, "joinWith");
|
exportStrFn(str.strJoinWithC, "joinWith");
|
||||||
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
|
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
|
||||||
exportStrFn(str.strFromIntC, "from_int");
|
|
||||||
exportStrFn(str.strFromFloatC, "from_float");
|
exportStrFn(str.strFromFloatC, "from_float");
|
||||||
exportStrFn(str.strEqual, "equal");
|
exportStrFn(str.strEqual, "equal");
|
||||||
exportStrFn(str.strToUtf8C, "to_utf8");
|
exportStrFn(str.strToUtf8C, "to_utf8");
|
||||||
|
@ -123,6 +124,10 @@ comptime {
|
||||||
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
||||||
exportStrFn(str.repeat, "repeat");
|
exportStrFn(str.repeat, "repeat");
|
||||||
exportStrFn(str.strTrim, "trim");
|
exportStrFn(str.strTrim, "trim");
|
||||||
|
|
||||||
|
inline for (INTEGERS) |T| {
|
||||||
|
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
|
@ -413,9 +413,14 @@ pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str.fromInt
|
// Str.fromInt
|
||||||
pub fn strFromIntC(int: i64) callconv(.C) RocStr {
|
pub fn exportFromInt(comptime T: type, comptime name: []const u8) void {
|
||||||
// prepare for having multiple integer types in the future
|
comptime var f = struct {
|
||||||
return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ i64, int });
|
fn func(int: T) callconv(.C) RocStr {
|
||||||
|
return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ T, int });
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strFromIntHelp(comptime T: type, int: T) RocStr {
|
fn strFromIntHelp(comptime T: type, int: T) RocStr {
|
||||||
|
|
|
@ -8,12 +8,7 @@ use std::process::Command;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
let dest_obj_path = Path::new(&out_dir).join("builtins.o");
|
|
||||||
let dest_obj = dest_obj_path.to_str().expect("Invalid dest object path");
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rustc-env=BUILTINS_O={}", dest_obj);
|
|
||||||
|
|
||||||
// When we build on Netlify, zig is not installed (but also not used,
|
// When we build on Netlify, zig is not installed (but also not used,
|
||||||
// since all we're doing is generating docs), so we can skip the steps
|
// since all we're doing is generating docs), so we can skip the steps
|
||||||
|
@ -28,70 +23,38 @@ fn main() {
|
||||||
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
||||||
let bitcode_path = build_script_dir_path.join("bitcode");
|
let bitcode_path = build_script_dir_path.join("bitcode");
|
||||||
|
|
||||||
let src_obj_path = bitcode_path.join("builtins-host.o");
|
// LLVM .bc FILES
|
||||||
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
|
||||||
|
|
||||||
let dest_ir_path = bitcode_path.join("builtins-wasm32.ll");
|
generate_bc_file(&bitcode_path, &build_script_dir_path, "ir", "builtins-host");
|
||||||
let dest_ir_wasm32 = dest_ir_path.to_str().expect("Invalid dest ir path");
|
|
||||||
|
|
||||||
let dest_ir_path = bitcode_path.join("builtins-i386.ll");
|
generate_bc_file(
|
||||||
let dest_ir_i386 = dest_ir_path.to_str().expect("Invalid dest ir path");
|
|
||||||
|
|
||||||
let dest_ir_path = bitcode_path.join("builtins-host.ll");
|
|
||||||
let dest_ir_host = dest_ir_path.to_str().expect("Invalid dest ir path");
|
|
||||||
|
|
||||||
println!("Compiling zig object to: {}", src_obj);
|
|
||||||
run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]);
|
|
||||||
|
|
||||||
println!("Compiling host ir to: {}", dest_ir_host);
|
|
||||||
run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]);
|
|
||||||
|
|
||||||
println!("Compiling 32-bit i386 ir to: {}", dest_ir_i386);
|
|
||||||
run_command(
|
|
||||||
&bitcode_path,
|
&bitcode_path,
|
||||||
"zig",
|
&build_script_dir_path,
|
||||||
&["build", "ir-i386", "-Drelease=true"],
|
"ir-wasm32",
|
||||||
|
"builtins-wasm32",
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("Compiling 32-bit wasm32 ir to: {}", dest_ir_wasm32);
|
generate_bc_file(
|
||||||
run_command(
|
|
||||||
&bitcode_path,
|
&bitcode_path,
|
||||||
"zig",
|
&build_script_dir_path,
|
||||||
&["build", "ir-wasm32", "-Drelease=true"],
|
"ir-i386",
|
||||||
|
"builtins-i386",
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("Moving zig object to: {}", dest_obj);
|
// OBJECT FILES
|
||||||
|
|
||||||
run_command(&bitcode_path, "mv", &[src_obj, dest_obj]);
|
generate_object_file(
|
||||||
|
&bitcode_path,
|
||||||
let dest_bc_path = bitcode_path.join("builtins-i386.bc");
|
"BUILTINS_HOST_O",
|
||||||
let dest_bc_32bit = dest_bc_path.to_str().expect("Invalid dest bc path");
|
"object",
|
||||||
println!("Compiling 32-bit bitcode to: {}", dest_bc_32bit);
|
"builtins-host.o",
|
||||||
|
|
||||||
run_command(
|
|
||||||
&build_script_dir_path,
|
|
||||||
"llvm-as",
|
|
||||||
&[dest_ir_i386, "-o", dest_bc_32bit],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let dest_bc_path = bitcode_path.join("builtins-wasm32.bc");
|
generate_object_file(
|
||||||
let dest_bc_32bit = dest_bc_path.to_str().expect("Invalid dest bc path");
|
&bitcode_path,
|
||||||
println!("Compiling 32-bit bitcode to: {}", dest_bc_32bit);
|
"BUILTINS_WASM32_O",
|
||||||
|
"wasm32-object",
|
||||||
run_command(
|
"builtins-wasm32.o",
|
||||||
&build_script_dir_path,
|
|
||||||
"llvm-as",
|
|
||||||
&[dest_ir_wasm32, "-o", dest_bc_32bit],
|
|
||||||
);
|
|
||||||
|
|
||||||
let dest_bc_path = bitcode_path.join("builtins-host.bc");
|
|
||||||
let dest_bc_64bit = dest_bc_path.to_str().expect("Invalid dest bc path");
|
|
||||||
println!("Compiling 64-bit bitcode to: {}", dest_bc_64bit);
|
|
||||||
|
|
||||||
run_command(
|
|
||||||
&build_script_dir_path,
|
|
||||||
"llvm-as",
|
|
||||||
&[dest_ir_host, "-o", dest_bc_64bit],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
get_zig_files(bitcode_path.as_path(), &|path| {
|
get_zig_files(bitcode_path.as_path(), &|path| {
|
||||||
|
@ -104,6 +67,69 @@ fn main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_object_file(
|
||||||
|
bitcode_path: &Path,
|
||||||
|
env_var_name: &str,
|
||||||
|
zig_object: &str,
|
||||||
|
object_file_name: &str,
|
||||||
|
) {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
let dest_obj_path = Path::new(&out_dir).join(object_file_name);
|
||||||
|
let dest_obj = dest_obj_path.to_str().expect("Invalid dest object path");
|
||||||
|
|
||||||
|
// set the variable (e.g. BUILTINS_HOST_O) that is later used in
|
||||||
|
// `compiler/builtins/src/bitcode.rs` to load the object file
|
||||||
|
println!("cargo:rustc-env={}={}", env_var_name, dest_obj);
|
||||||
|
|
||||||
|
let src_obj_path = bitcode_path.join(object_file_name);
|
||||||
|
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
||||||
|
|
||||||
|
println!("Compiling zig object `{}` to: {}", zig_object, src_obj);
|
||||||
|
|
||||||
|
run_command(
|
||||||
|
&bitcode_path,
|
||||||
|
"zig",
|
||||||
|
&["build", zig_object, "-Drelease=true"],
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Moving zig object `{}` to: {}", zig_object, dest_obj);
|
||||||
|
|
||||||
|
// we store this .o file in rust's `target` folder
|
||||||
|
run_command(&bitcode_path, "mv", &[src_obj, dest_obj]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_bc_file(
|
||||||
|
bitcode_path: &Path,
|
||||||
|
build_script_dir_path: &Path,
|
||||||
|
zig_object: &str,
|
||||||
|
file_name: &str,
|
||||||
|
) {
|
||||||
|
let mut ll_path = bitcode_path.join(file_name);
|
||||||
|
ll_path.set_extension("ll");
|
||||||
|
let dest_ir_host = ll_path.to_str().expect("Invalid dest ir path");
|
||||||
|
|
||||||
|
println!("Compiling host ir to: {}", dest_ir_host);
|
||||||
|
|
||||||
|
run_command(
|
||||||
|
&bitcode_path,
|
||||||
|
"zig",
|
||||||
|
&["build", zig_object, "-Drelease=true"],
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut bc_path = bitcode_path.join(file_name);
|
||||||
|
bc_path.set_extension("bc");
|
||||||
|
let dest_bc_64bit = bc_path.to_str().expect("Invalid dest bc path");
|
||||||
|
|
||||||
|
println!("Compiling 64-bit bitcode to: {}", dest_bc_64bit);
|
||||||
|
|
||||||
|
run_command(
|
||||||
|
&build_script_dir_path,
|
||||||
|
"llvm-as",
|
||||||
|
&[dest_ir_host, "-o", dest_bc_64bit],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn run_command<S, I, P: AsRef<Path>>(path: P, command_str: &str, args: I)
|
fn run_command<S, I, P: AsRef<Path>>(path: P, command_str: &str, args: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
|
|
|
@ -687,4 +687,6 @@ endsWith : List elem, List elem -> Bool
|
||||||
|
|
||||||
all : List elem, (elem -> Bool) -> Bool
|
all : List elem, (elem -> Bool) -> Bool
|
||||||
|
|
||||||
|
## Run the given predicate on each element of the list, returning `True` if
|
||||||
|
## any of the elements satisfy it.
|
||||||
any : List elem, (elem -> Bool) -> Bool
|
any : List elem, (elem -> Bool) -> Bool
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
pub const OBJ_PATH: &str = env!(
|
pub const OBJ_PATH: &str = env!(
|
||||||
"BUILTINS_O",
|
"BUILTINS_HOST_O",
|
||||||
"Env var BUILTINS_O not found. Is there a problem with the build script?"
|
"Env var BUILTINS_HOST_O not found. Is there a problem with the build script?"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -135,7 +135,7 @@ pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
||||||
pub const STR_STARTS_WITH_CODE_PT: &str = "roc_builtins.str.starts_with_code_point";
|
pub const STR_STARTS_WITH_CODE_PT: &str = "roc_builtins.str.starts_with_code_point";
|
||||||
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
||||||
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
|
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
|
||||||
pub const STR_FROM_INT: &str = "roc_builtins.str.from_int";
|
pub const STR_FROM_INT: IntrinsicName = int_intrinsic!("roc_builtins.str.from_int");
|
||||||
pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
|
pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
|
||||||
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
||||||
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
||||||
|
@ -188,6 +188,7 @@ pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
||||||
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
||||||
pub const LIST_SET: &str = "roc_builtins.list.set";
|
pub const LIST_SET: &str = "roc_builtins.list.set";
|
||||||
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
||||||
|
pub const LIST_ANY: &str = "roc_builtins.list.any";
|
||||||
|
|
||||||
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||||
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
||||||
|
|
|
@ -5,7 +5,7 @@ use roc_region::all::Region;
|
||||||
use roc_types::builtin_aliases::{
|
use roc_types::builtin_aliases::{
|
||||||
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
|
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
|
||||||
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u16_type, u32_type,
|
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u16_type, u32_type,
|
||||||
u64_type, u8_type,
|
u8_type,
|
||||||
};
|
};
|
||||||
use roc_types::solved_types::SolvedType;
|
use roc_types::solved_types::SolvedType;
|
||||||
use roc_types::subs::VarId;
|
use roc_types::subs::VarId;
|
||||||
|
@ -877,6 +877,19 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(list_type(int_type(flex(TVAR1)))),
|
Box::new(list_type(int_type(flex(TVAR1)))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// joinMap : List before, (before -> List after) -> List after
|
||||||
|
{
|
||||||
|
let_tvars! { cvar, before, after }
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_JOIN_MAP,
|
||||||
|
vec![
|
||||||
|
list_type(flex(before)),
|
||||||
|
closure(vec![flex(before)], cvar, Box::new(list_type(flex(after)))),
|
||||||
|
],
|
||||||
|
Box::new(list_type(flex(after))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// map : List before, (before -> after) -> List after
|
// map : List before, (before -> after) -> List after
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::LIST_MAP,
|
Symbol::LIST_MAP,
|
||||||
|
@ -1042,6 +1055,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(bool_type())
|
Box::new(bool_type())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// any: List elem, (elem -> Bool) -> Bool
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_ANY,
|
||||||
|
vec![
|
||||||
|
list_type(flex(TVAR1)),
|
||||||
|
closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())),
|
||||||
|
],
|
||||||
|
Box::new(bool_type()),
|
||||||
|
);
|
||||||
|
|
||||||
// sortWith : List a, (a, a -> Ordering) -> List a
|
// sortWith : List a, (a, a -> Ordering) -> List a
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::LIST_SORT_WITH,
|
Symbol::LIST_SORT_WITH,
|
||||||
|
@ -1058,13 +1081,6 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
// Dict module
|
// Dict module
|
||||||
|
|
||||||
// Dict.hashTestOnly : U64, v -> U64
|
|
||||||
add_top_level_function_type!(
|
|
||||||
Symbol::DICT_TEST_HASH,
|
|
||||||
vec![u64_type(), flex(TVAR2)],
|
|
||||||
Box::new(u64_type())
|
|
||||||
);
|
|
||||||
|
|
||||||
// len : Dict * * -> Nat
|
// len : Dict * * -> Nat
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::DICT_LEN,
|
Symbol::DICT_LEN,
|
||||||
|
|
|
@ -86,6 +86,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_PRODUCT => list_product,
|
LIST_PRODUCT => list_product,
|
||||||
LIST_PREPEND => list_prepend,
|
LIST_PREPEND => list_prepend,
|
||||||
LIST_JOIN => list_join,
|
LIST_JOIN => list_join,
|
||||||
|
LIST_JOIN_MAP => list_join_map,
|
||||||
LIST_MAP => list_map,
|
LIST_MAP => list_map,
|
||||||
LIST_MAP2 => list_map2,
|
LIST_MAP2 => list_map2,
|
||||||
LIST_MAP3 => list_map3,
|
LIST_MAP3 => list_map3,
|
||||||
|
@ -104,7 +105,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_WALK_BACKWARDS => list_walk_backwards,
|
LIST_WALK_BACKWARDS => list_walk_backwards,
|
||||||
LIST_WALK_UNTIL => list_walk_until,
|
LIST_WALK_UNTIL => list_walk_until,
|
||||||
LIST_SORT_WITH => list_sort_with,
|
LIST_SORT_WITH => list_sort_with,
|
||||||
DICT_TEST_HASH => dict_hash_test_only,
|
LIST_ANY => list_any,
|
||||||
DICT_LEN => dict_len,
|
DICT_LEN => dict_len,
|
||||||
DICT_EMPTY => dict_empty,
|
DICT_EMPTY => dict_empty,
|
||||||
DICT_SINGLE => dict_single,
|
DICT_SINGLE => dict_single,
|
||||||
|
@ -2200,6 +2201,83 @@ fn list_walk_until(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_3(symbol, LowLevel::ListWalkUntil, var_store)
|
lowlevel_3(symbol, LowLevel::ListWalkUntil, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.joinMap : List before, (before -> List after) -> List after
|
||||||
|
fn list_join_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let before = var_store.fresh();
|
||||||
|
let list_before = var_store.fresh();
|
||||||
|
let after = var_store.fresh();
|
||||||
|
let list_after = var_store.fresh();
|
||||||
|
let before2list_after = var_store.fresh();
|
||||||
|
let t_concat_clos = var_store.fresh();
|
||||||
|
let mapper_lambda_set = var_store.fresh();
|
||||||
|
|
||||||
|
// \state, elem -> List.concat state (mapper elem)
|
||||||
|
let concat_clos = Closure(ClosureData {
|
||||||
|
function_type: t_concat_clos,
|
||||||
|
closure_type: var_store.fresh(),
|
||||||
|
closure_ext_var: var_store.fresh(),
|
||||||
|
return_type: list_after,
|
||||||
|
name: Symbol::LIST_JOIN_MAP_CONCAT,
|
||||||
|
recursive: Recursive::NotRecursive,
|
||||||
|
captured_symbols: vec![(Symbol::ARG_2, before2list_after)],
|
||||||
|
arguments: vec![
|
||||||
|
(list_after, no_region(Pattern::Identifier(Symbol::ARG_3))),
|
||||||
|
(before, no_region(Pattern::Identifier(Symbol::ARG_4))),
|
||||||
|
],
|
||||||
|
loc_body: {
|
||||||
|
let mapper = Box::new((
|
||||||
|
before2list_after,
|
||||||
|
no_region(Var(Symbol::ARG_2)),
|
||||||
|
mapper_lambda_set,
|
||||||
|
list_after, // return type
|
||||||
|
));
|
||||||
|
// (mapper elem)
|
||||||
|
let mapper_elem = Call(
|
||||||
|
mapper,
|
||||||
|
vec![(before, no_region(Var(Symbol::ARG_4)))],
|
||||||
|
CalledVia::Space,
|
||||||
|
);
|
||||||
|
Box::new(no_region(RunLowLevel {
|
||||||
|
op: LowLevel::ListConcat,
|
||||||
|
args: vec![(list_after, Var(Symbol::ARG_3)), (list_after, mapper_elem)],
|
||||||
|
ret_var: list_after,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// List.joinMap = \input_list, mapper ->
|
||||||
|
// List.walk [] input_list (\state, elem -> List.concat state (mapper elem))
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::ListWalk,
|
||||||
|
args: vec![
|
||||||
|
// input_list : List before
|
||||||
|
(list_before, Var(Symbol::ARG_1)),
|
||||||
|
// [] : List after
|
||||||
|
(
|
||||||
|
list_after,
|
||||||
|
List {
|
||||||
|
elem_var: after,
|
||||||
|
loc_elems: vec![],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// \state, elem -> List.concat state (mapper elem)
|
||||||
|
(t_concat_clos, concat_clos),
|
||||||
|
],
|
||||||
|
ret_var: list_after,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![
|
||||||
|
(list_before, Symbol::ARG_1),
|
||||||
|
(before2list_after, Symbol::ARG_2),
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
list_after,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
// min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
||||||
fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let arg_var = var_store.fresh();
|
let arg_var = var_store.fresh();
|
||||||
|
@ -2617,9 +2695,9 @@ fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
|
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dict.hashTestOnly : k, v -> Nat
|
/// List.any: List elem, (elem -> Bool) -> Bool
|
||||||
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_any(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::Hash, var_store)
|
lowlevel_2(symbol, LowLevel::ListAny, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dict.len : Dict * * -> Nat
|
/// Dict.len : Dict * * -> Nat
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::llvm::build_dict::{
|
||||||
};
|
};
|
||||||
use crate::llvm::build_hash::generic_hash;
|
use crate::llvm::build_hash::generic_hash;
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
|
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat,
|
||||||
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
|
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
|
||||||
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
|
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
|
||||||
list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
|
list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
|
||||||
|
@ -5149,6 +5149,30 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
_ => unreachable!("invalid list layout"),
|
_ => unreachable!("invalid list layout"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListAny { xs } => {
|
||||||
|
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
|
||||||
|
let (function, closure, closure_layout) = function_details!();
|
||||||
|
|
||||||
|
match list_layout {
|
||||||
|
Layout::Builtin(Builtin::EmptyList) => env.context.bool_type().const_zero().into(),
|
||||||
|
Layout::Builtin(Builtin::List(element_layout)) => {
|
||||||
|
let argument_layouts = &[**element_layout];
|
||||||
|
|
||||||
|
let roc_function_call = roc_function_call(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
function,
|
||||||
|
closure,
|
||||||
|
closure_layout,
|
||||||
|
function_owns_closure_data,
|
||||||
|
argument_layouts,
|
||||||
|
);
|
||||||
|
|
||||||
|
list_any(env, roc_function_call, list, element_layout)
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid list layout"),
|
||||||
|
}
|
||||||
|
}
|
||||||
DictWalk { xs, state } => {
|
DictWalk { xs, state } => {
|
||||||
let (dict, dict_layout) = load_symbol_and_layout(scope, &xs);
|
let (dict, dict_layout) = load_symbol_and_layout(scope, &xs);
|
||||||
let (default, default_layout) = load_symbol_and_layout(scope, &state);
|
let (default, default_layout) = load_symbol_and_layout(scope, &state);
|
||||||
|
@ -6000,7 +6024,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||||
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
|
| ListAny | DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6964,39 +6988,6 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_bitcode_int_fn<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
fn_name: &str,
|
|
||||||
args: &[BasicValueEnum<'ctx>],
|
|
||||||
int_width: IntWidth,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
match int_width {
|
|
||||||
IntWidth::U8 => call_bitcode_fn(env, args, &format!("{}_u8", fn_name)),
|
|
||||||
IntWidth::U16 => call_bitcode_fn(env, args, &format!("{}_u16", fn_name)),
|
|
||||||
IntWidth::U32 => call_bitcode_fn(env, args, &format!("{}_u32", fn_name)),
|
|
||||||
IntWidth::U64 => call_bitcode_fn(env, args, &format!("{}_u64", fn_name)),
|
|
||||||
IntWidth::U128 => call_bitcode_fn(env, args, &format!("{}_u128", fn_name)),
|
|
||||||
IntWidth::I8 => call_bitcode_fn(env, args, &format!("{}_i8", fn_name)),
|
|
||||||
IntWidth::I16 => call_bitcode_fn(env, args, &format!("{}_i16", fn_name)),
|
|
||||||
IntWidth::I32 => call_bitcode_fn(env, args, &format!("{}_i32", fn_name)),
|
|
||||||
IntWidth::I64 => call_bitcode_fn(env, args, &format!("{}_i64", fn_name)),
|
|
||||||
IntWidth::I128 => call_bitcode_fn(env, args, &format!("{}_i128", fn_name)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call_bitcode_float_fn<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
fn_name: &str,
|
|
||||||
args: &[BasicValueEnum<'ctx>],
|
|
||||||
float_width: FloatWidth,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
match float_width {
|
|
||||||
FloatWidth::F32 => call_bitcode_fn(env, args, &format!("{}_f32", fn_name)),
|
|
||||||
FloatWidth::F64 => call_bitcode_fn(env, args, &format!("{}_f64", fn_name)),
|
|
||||||
FloatWidth::F128 => todo!("suport 128-bit floats"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_global_str_literal_ptr<'a, 'ctx, 'env>(
|
fn define_global_str_literal_ptr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
message: &str,
|
message: &str,
|
||||||
|
|
|
@ -899,6 +899,27 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.any : List elem, \(elem -> Bool) -> Bool
|
||||||
|
pub fn list_any<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
element_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
pass_list_cc(env, list),
|
||||||
|
roc_function_call.caller.into(),
|
||||||
|
pass_as_opaque(env, roc_function_call.data),
|
||||||
|
roc_function_call.inc_n_data.into(),
|
||||||
|
roc_function_call.data_is_owned.into(),
|
||||||
|
layout_width(env, element_layout),
|
||||||
|
],
|
||||||
|
bitcode::LIST_ANY,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decrementing_elem_loop<'ctx, LoopFn>(
|
pub fn decrementing_elem_loop<'ctx, LoopFn>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use roc_builtins::bitcode;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
use super::build::load_symbol;
|
use super::build::{intwidth_from_builtin, load_symbol, load_symbol_and_layout};
|
||||||
|
|
||||||
pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
|
pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
|
||||||
|
|
||||||
|
@ -265,9 +265,33 @@ pub fn str_from_int<'a, 'ctx, 'env>(
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
int_symbol: Symbol,
|
int_symbol: Symbol,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let int = load_symbol(scope, &int_symbol);
|
let (int, int_layout) = load_symbol_and_layout(scope, &int_symbol);
|
||||||
|
|
||||||
call_bitcode_fn(env, &[int], bitcode::STR_FROM_INT)
|
match int_layout {
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Builtin::Usize
|
||||||
|
| Builtin::Int128
|
||||||
|
| Builtin::Int64
|
||||||
|
| Builtin::Int32
|
||||||
|
| Builtin::Int16
|
||||||
|
| Builtin::Int8 => {
|
||||||
|
let intwidth = intwidth_from_builtin(*builtin, env.ptr_bytes);
|
||||||
|
call_bitcode_fn(env, &[int], &bitcode::STR_FROM_INT[intwidth])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!(
|
||||||
|
"Compiler bug: tried to convert numeric on invalid builtin layout: ({:?})",
|
||||||
|
int_layout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
unreachable!(
|
||||||
|
"Compiler bug: tried to convert numeric on invalid layout: {:?}",
|
||||||
|
int_layout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.toUtf8 : Str -> List U8
|
/// Str.toUtf8 : Str -> List U8
|
||||||
|
|
|
@ -12,11 +12,9 @@ roc_mono = { path = "../mono" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
wasmer = { version = "2.0.0", default-features = false, features = ["default-cranelift", "default-universal"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
target-lexicon = "0.12.2"
|
|
||||||
|
|
|
@ -1,36 +1,42 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::{self, collections::Vec};
|
||||||
|
|
||||||
use code_builder::Align;
|
use code_builder::Align;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::{Layout, LayoutIds};
|
||||||
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::WasmLayout;
|
||||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||||
use crate::wasm_module::linking::{LinkingSection, RelocationSection};
|
use crate::wasm_module::linking::{DataSymbol, LinkingSection, RelocationSection};
|
||||||
use crate::wasm_module::sections::{
|
use crate::wasm_module::sections::{
|
||||||
CodeSection, DataMode, DataSection, DataSegment, ExportSection, FunctionSection, GlobalSection,
|
CodeSection, DataMode, DataSection, DataSegment, ExportSection, FunctionSection, GlobalSection,
|
||||||
ImportSection, MemorySection, TypeSection, WasmModule,
|
ImportSection, MemorySection, TypeSection, WasmModule,
|
||||||
};
|
};
|
||||||
use crate::wasm_module::{
|
use crate::wasm_module::{
|
||||||
code_builder, BlockType, CodeBuilder, ConstExpr, Export, ExportType, Global, GlobalType,
|
code_builder, BlockType, CodeBuilder, ConstExpr, Export, ExportType, Global, GlobalType,
|
||||||
LocalId, Signature, ValueType,
|
LocalId, Signature, SymInfo, ValueType,
|
||||||
};
|
};
|
||||||
use crate::{copy_memory, CopyMemoryConfig, Env, PTR_TYPE};
|
use crate::{copy_memory, CopyMemoryConfig, Env, PTR_TYPE};
|
||||||
|
|
||||||
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
/// The memory address where the constants data will be loaded during module instantiation.
|
||||||
// Follow Emscripten's example by using 1kB (4 bytes would probably do)
|
/// We avoid address zero and anywhere near it. They're valid addresses but maybe bug-prone.
|
||||||
const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
|
/// Follow Emscripten's example by leaving 1kB unused (though 4 bytes would probably do!)
|
||||||
|
const CONST_SEGMENT_BASE_ADDR: u32 = 1024;
|
||||||
|
|
||||||
|
/// Index of the data segment where we store constants
|
||||||
|
const CONST_SEGMENT_INDEX: usize = 0;
|
||||||
|
|
||||||
pub struct WasmBackend<'a> {
|
pub struct WasmBackend<'a> {
|
||||||
env: &'a Env<'a>,
|
env: &'a Env<'a>,
|
||||||
|
|
||||||
// Module-level data
|
// Module-level data
|
||||||
pub module: WasmModule<'a>,
|
pub module: WasmModule<'a>,
|
||||||
next_literal_addr: u32,
|
layout_ids: LayoutIds<'a>,
|
||||||
|
constant_sym_index_map: MutMap<&'a str, usize>,
|
||||||
proc_symbols: Vec<'a, Symbol>,
|
proc_symbols: Vec<'a, Symbol>,
|
||||||
|
pub linker_symbols: Vec<'a, SymInfo>,
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
code_builder: CodeBuilder<'a>,
|
code_builder: CodeBuilder<'a>,
|
||||||
|
@ -42,62 +48,74 @@ pub struct WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmBackend<'a> {
|
impl<'a> WasmBackend<'a> {
|
||||||
pub fn new(env: &'a Env<'a>, proc_symbols: Vec<'a, Symbol>) -> Self {
|
pub fn new(
|
||||||
|
env: &'a Env<'a>,
|
||||||
|
layout_ids: LayoutIds<'a>,
|
||||||
|
proc_symbols: Vec<'a, Symbol>,
|
||||||
|
linker_symbols: Vec<'a, SymInfo>,
|
||||||
|
mut exports: Vec<'a, Export>,
|
||||||
|
) -> Self {
|
||||||
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
||||||
|
let arena = env.arena;
|
||||||
|
let num_procs = proc_symbols.len();
|
||||||
|
|
||||||
let mut module = WasmModule {
|
exports.push(Export {
|
||||||
types: TypeSection::new(env.arena),
|
|
||||||
import: ImportSection::new(env.arena),
|
|
||||||
function: FunctionSection::new(env.arena),
|
|
||||||
table: (), // Unused in Roc (mainly for function pointers)
|
|
||||||
memory: MemorySection::new(MEMORY_INIT_SIZE),
|
|
||||||
global: GlobalSection::new(env.arena),
|
|
||||||
export: ExportSection::new(env.arena),
|
|
||||||
start: (), // Entry function. In Roc this would be part of the platform.
|
|
||||||
element: (), // Unused in Roc (related to table section)
|
|
||||||
code: CodeSection::new(env.arena),
|
|
||||||
data: DataSection::new(env.arena),
|
|
||||||
linking: LinkingSection::new(env.arena),
|
|
||||||
reloc_code: RelocationSection::new(env.arena, "reloc.CODE"),
|
|
||||||
reloc_data: RelocationSection::new(env.arena, "reloc.DATA"),
|
|
||||||
};
|
|
||||||
|
|
||||||
module.export.entries.push(Export {
|
|
||||||
name: "memory".to_string(),
|
name: "memory".to_string(),
|
||||||
ty: ExportType::Mem,
|
ty: ExportType::Mem,
|
||||||
index: 0,
|
index: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
let stack_pointer_global = Global {
|
let stack_pointer = Global {
|
||||||
ty: GlobalType {
|
ty: GlobalType {
|
||||||
value_type: ValueType::I32,
|
value_type: ValueType::I32,
|
||||||
is_mutable: true,
|
is_mutable: true,
|
||||||
},
|
},
|
||||||
init: ConstExpr::I32(MEMORY_INIT_SIZE as i32),
|
init: ConstExpr::I32(MEMORY_INIT_SIZE as i32),
|
||||||
};
|
};
|
||||||
module.global.entries.push(stack_pointer_global);
|
|
||||||
|
|
||||||
let literal_segment = DataSegment {
|
let const_segment = DataSegment {
|
||||||
mode: DataMode::Active {
|
mode: DataMode::Active {
|
||||||
offset: ConstExpr::I32(UNUSED_DATA_SECTION_BYTES as i32),
|
offset: ConstExpr::I32(CONST_SEGMENT_BASE_ADDR as i32),
|
||||||
},
|
},
|
||||||
init: Vec::with_capacity_in(64, env.arena),
|
init: Vec::with_capacity_in(64, arena),
|
||||||
|
};
|
||||||
|
|
||||||
|
let module = WasmModule {
|
||||||
|
types: TypeSection::new(arena, num_procs),
|
||||||
|
import: ImportSection::new(arena),
|
||||||
|
function: FunctionSection::new(arena, num_procs),
|
||||||
|
table: (),
|
||||||
|
memory: MemorySection::new(MEMORY_INIT_SIZE),
|
||||||
|
global: GlobalSection {
|
||||||
|
entries: bumpalo::vec![in arena; stack_pointer],
|
||||||
|
},
|
||||||
|
export: ExportSection { entries: exports },
|
||||||
|
start: (),
|
||||||
|
element: (),
|
||||||
|
code: CodeSection::new(arena),
|
||||||
|
data: DataSection {
|
||||||
|
segments: bumpalo::vec![in arena; const_segment],
|
||||||
|
},
|
||||||
|
linking: LinkingSection::new(arena),
|
||||||
|
relocations: RelocationSection::new(arena, "reloc.CODE"),
|
||||||
};
|
};
|
||||||
module.data.segments.push(literal_segment);
|
|
||||||
|
|
||||||
WasmBackend {
|
WasmBackend {
|
||||||
env,
|
env,
|
||||||
|
|
||||||
// Module-level data
|
// Module-level data
|
||||||
module,
|
module,
|
||||||
next_literal_addr: UNUSED_DATA_SECTION_BYTES,
|
|
||||||
|
layout_ids,
|
||||||
|
constant_sym_index_map: MutMap::default(),
|
||||||
proc_symbols,
|
proc_symbols,
|
||||||
|
linker_symbols,
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
joinpoint_label_map: MutMap::default(),
|
joinpoint_label_map: MutMap::default(),
|
||||||
code_builder: CodeBuilder::new(env.arena),
|
code_builder: CodeBuilder::new(arena),
|
||||||
storage: Storage::new(env.arena),
|
storage: Storage::new(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,17 +415,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let wasm_layout = WasmLayout::new(layout);
|
let wasm_layout = WasmLayout::new(layout);
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal(lit) => self.load_literal(lit, storage),
|
Expr::Literal(lit) => self.load_literal(lit, storage, *sym, layout),
|
||||||
|
|
||||||
Expr::Call(roc_mono::ir::Call {
|
Expr::Call(roc_mono::ir::Call {
|
||||||
call_type,
|
call_type,
|
||||||
arguments,
|
arguments,
|
||||||
}) => match call_type {
|
}) => match call_type {
|
||||||
CallType::ByName { name: func_sym, .. } => {
|
CallType::ByName { name: func_sym, .. } => {
|
||||||
// TODO: See if we can make this more efficient
|
|
||||||
// Recreating the same WasmLayout again, rather than passing it down,
|
|
||||||
// to match signature of Backend::build_expr
|
|
||||||
|
|
||||||
let mut wasm_args_tmp: Vec<Symbol>;
|
let mut wasm_args_tmp: Vec<Symbol>;
|
||||||
let (wasm_args, has_return_val) = match wasm_layout {
|
let (wasm_args, has_return_val) = match wasm_layout {
|
||||||
WasmLayout::StackMemory { .. } => {
|
WasmLayout::StackMemory { .. } => {
|
||||||
|
@ -422,21 +436,22 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
self.storage.load_symbols(&mut self.code_builder, wasm_args);
|
self.storage.load_symbols(&mut self.code_builder, wasm_args);
|
||||||
|
|
||||||
// Index of the called function in the code section
|
// Index of the called function in the code section. Assumes all functions end up in the binary.
|
||||||
// TODO: account for inlined functions when we start doing that (remember we emit procs out of order)
|
// (We may decide to keep all procs even if calls are inlined, in case platform calls them)
|
||||||
let func_index = match self.proc_symbols.iter().position(|s| s == func_sym) {
|
let func_index = match self.proc_symbols.iter().position(|s| s == func_sym) {
|
||||||
Some(i) => i as u32,
|
Some(i) => i as u32,
|
||||||
None => {
|
None => {
|
||||||
// TODO: actually useful linking! Push a relocation for it.
|
// TODO: actually useful linking! Push a relocation for it.
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Not yet supporteed: calling foreign function {:?}",
|
"Not yet supported: calling foreign function {:?}",
|
||||||
func_sym
|
func_sym
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Index of the function's name in the symbol table
|
// Index of the function's name in the symbol table
|
||||||
let symbol_index = func_index; // TODO: update this when we add other things to the symbol table
|
// Same as the function index since those are the first symbols we add
|
||||||
|
let symbol_index = func_index;
|
||||||
|
|
||||||
self.code_builder.call(
|
self.code_builder.call(
|
||||||
func_index,
|
func_index,
|
||||||
|
@ -460,7 +475,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_literal(&mut self, lit: &Literal<'a>, storage: &StoredValue) -> Result<(), String> {
|
fn load_literal(
|
||||||
|
&mut self,
|
||||||
|
lit: &Literal<'a>,
|
||||||
|
storage: &StoredValue,
|
||||||
|
sym: Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String> {
|
||||||
let not_supported_error = || Err(format!("Literal value {:?} is not yet implemented", lit));
|
let not_supported_error = || Err(format!("Literal value {:?} is not yet implemented", lit));
|
||||||
|
|
||||||
match storage {
|
match storage {
|
||||||
|
@ -479,42 +500,33 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::StackMemory { location, .. } => match lit {
|
StoredValue::StackMemory { location, .. } => match lit {
|
||||||
Literal::Str(s) => {
|
Literal::Str(string) => {
|
||||||
// Load the stack memory address where we want to write
|
|
||||||
let (local_id, offset) =
|
let (local_id, offset) =
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||||
self.code_builder.get_local(local_id);
|
|
||||||
self.code_builder.i32_const(offset as i32);
|
|
||||||
self.code_builder.i32_add();
|
|
||||||
|
|
||||||
// For either small or regular strings, we write 8 bytes to stack memory
|
let len = string.len();
|
||||||
let mut stack_mem_bytes = [0; 8];
|
|
||||||
|
|
||||||
let len = s.len();
|
|
||||||
if len < 8 {
|
if len < 8 {
|
||||||
// Small string payload
|
let mut stack_mem_bytes = [0; 8];
|
||||||
stack_mem_bytes[0..len].clone_from_slice(s.as_bytes());
|
stack_mem_bytes[0..len].clone_from_slice(string.as_bytes());
|
||||||
// Small string flag and length
|
|
||||||
stack_mem_bytes[7] = 0x80 | (len as u8);
|
stack_mem_bytes[7] = 0x80 | (len as u8);
|
||||||
} else {
|
let str_as_int = i64::from_le_bytes(stack_mem_bytes);
|
||||||
// Store the bytes in the data section, to be initialised on module load.
|
|
||||||
// Point there instead of to the heap
|
|
||||||
let literal_bytes: &mut Vec<'a, u8> =
|
|
||||||
&mut self.module.data.segments[0].init;
|
|
||||||
literal_bytes.extend_from_slice(s.as_bytes());
|
|
||||||
|
|
||||||
// Calculate bytes for elements and length
|
self.code_builder.get_local(local_id);
|
||||||
let len32 = len as u32;
|
self.code_builder.i64_const(str_as_int);
|
||||||
let elements_addr = self.next_literal_addr;
|
|
||||||
self.next_literal_addr += len32;
|
|
||||||
stack_mem_bytes[0..3].clone_from_slice(&elements_addr.to_le_bytes());
|
|
||||||
stack_mem_bytes[4..8].clone_from_slice(&len32.to_le_bytes());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Since we have 64 bits of data, we can squeeze them into one instruction
|
|
||||||
self.code_builder
|
|
||||||
.i64_const(i64::from_le_bytes(stack_mem_bytes));
|
|
||||||
self.code_builder.i64_store(Align::Bytes4, offset);
|
self.code_builder.i64_store(Align::Bytes4, offset);
|
||||||
|
} else {
|
||||||
|
let (linker_sym_index, elements_addr) =
|
||||||
|
self.lookup_string_constant(string, sym, layout);
|
||||||
|
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder.insert_memory_relocation(linker_sym_index);
|
||||||
|
self.code_builder.i32_const(elements_addr as i32);
|
||||||
|
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||||
|
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder.i32_const(string.len() as i32);
|
||||||
|
self.code_builder.i32_store(Align::Bytes4, offset + 4);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return not_supported_error();
|
return not_supported_error();
|
||||||
|
@ -528,6 +540,63 @@ impl<'a> WasmBackend<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look up a string constant in our internal data structures
|
||||||
|
/// Return the data we need for code gen: linker symbol index and memory address
|
||||||
|
fn lookup_string_constant(
|
||||||
|
&mut self,
|
||||||
|
string: &'a str,
|
||||||
|
sym: Symbol,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) -> (u32, u32) {
|
||||||
|
match self.constant_sym_index_map.get(string) {
|
||||||
|
Some(linker_sym_index) => {
|
||||||
|
// We've seen this string before. The linker metadata has a reference
|
||||||
|
// to its offset in the constants data segment.
|
||||||
|
let syminfo = &self.linker_symbols[*linker_sym_index];
|
||||||
|
match syminfo {
|
||||||
|
SymInfo::Data(DataSymbol::Defined { segment_offset, .. }) => {
|
||||||
|
let elements_addr = *segment_offset + CONST_SEGMENT_BASE_ADDR;
|
||||||
|
(*linker_sym_index as u32, elements_addr)
|
||||||
|
}
|
||||||
|
_ => unreachable!(
|
||||||
|
"Compiler bug: Invalid linker symbol info for string {:?}:\n{:?}",
|
||||||
|
string, syminfo
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
let const_segment_bytes = &mut self.module.data.segments[CONST_SEGMENT_INDEX].init;
|
||||||
|
|
||||||
|
// Store the string in the data section, to be loaded on module instantiation
|
||||||
|
// RocStr `elements` field will point to that constant data, not the heap
|
||||||
|
let segment_offset = const_segment_bytes.len() as u32;
|
||||||
|
let elements_addr = segment_offset + CONST_SEGMENT_BASE_ADDR;
|
||||||
|
const_segment_bytes.extend_from_slice(string.as_bytes());
|
||||||
|
|
||||||
|
// Generate linker info
|
||||||
|
// Just pick the symbol name from the first usage
|
||||||
|
let name = self
|
||||||
|
.layout_ids
|
||||||
|
.get(sym, layout)
|
||||||
|
.to_symbol_string(sym, &self.env.interns);
|
||||||
|
let linker_symbol = SymInfo::Data(DataSymbol::Defined {
|
||||||
|
flags: 0,
|
||||||
|
name,
|
||||||
|
segment_index: CONST_SEGMENT_INDEX as u32,
|
||||||
|
segment_offset,
|
||||||
|
size: string.len() as u32,
|
||||||
|
});
|
||||||
|
|
||||||
|
let linker_sym_index = self.linker_symbols.len();
|
||||||
|
self.constant_sym_index_map.insert(string, linker_sym_index);
|
||||||
|
self.linker_symbols.push(linker_symbol);
|
||||||
|
|
||||||
|
(linker_sym_index as u32, elements_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_struct(
|
fn create_struct(
|
||||||
&mut self,
|
&mut self,
|
||||||
sym: &Symbol,
|
sym: &Symbol,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
pub mod from_wasm32_memory;
|
|
||||||
mod layout;
|
mod layout;
|
||||||
mod storage;
|
mod storage;
|
||||||
pub mod wasm_module;
|
pub mod wasm_module;
|
||||||
|
@ -43,37 +42,44 @@ pub fn build_module_help<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<WasmModule<'a>, String> {
|
) -> Result<WasmModule<'a>, String> {
|
||||||
let proc_symbols = Vec::from_iter_in(procedures.keys().map(|(sym, _)| *sym), env.arena);
|
|
||||||
let mut backend = WasmBackend::new(env, proc_symbols);
|
|
||||||
|
|
||||||
let mut layout_ids = LayoutIds::default();
|
let mut layout_ids = LayoutIds::default();
|
||||||
let mut symbol_table_entries = Vec::with_capacity_in(procedures.len(), env.arena);
|
let mut proc_symbols = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
|
let mut linker_symbols = Vec::with_capacity_in(procedures.len() * 2, env.arena);
|
||||||
|
let mut exports = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
|
|
||||||
for (i, ((sym, layout), proc)) in procedures.into_iter().enumerate() {
|
// Collect the symbols & names for the procedures
|
||||||
let proc_name = layout_ids
|
for (i, (sym, layout)) in procedures.keys().enumerate() {
|
||||||
.get(proc.name, &proc.ret_layout)
|
proc_symbols.push(*sym);
|
||||||
.to_symbol_string(proc.name, &env.interns);
|
|
||||||
symbol_table_entries.push(SymInfo::for_function(i as u32, proc_name));
|
|
||||||
|
|
||||||
backend.build_proc(proc, sym)?;
|
|
||||||
|
|
||||||
if env.exposed_to_host.contains(&sym) {
|
|
||||||
let fn_name = layout_ids
|
let fn_name = layout_ids
|
||||||
.get_toplevel(sym, &layout)
|
.get_toplevel(*sym, layout)
|
||||||
.to_symbol_string(sym, &env.interns);
|
.to_symbol_string(*sym, &env.interns);
|
||||||
|
|
||||||
backend.module.export.entries.push(Export {
|
if env.exposed_to_host.contains(sym) {
|
||||||
name: fn_name,
|
exports.push(Export {
|
||||||
|
name: fn_name.clone(),
|
||||||
ty: ExportType::Func,
|
ty: ExportType::Func,
|
||||||
index: i as u32,
|
index: i as u32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let linker_sym = SymInfo::for_function(i as u32, fn_name);
|
||||||
|
linker_symbols.push(linker_sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol_table = LinkingSubSection::SymbolTable(symbol_table_entries);
|
// Main loop: Build the Wasm module
|
||||||
backend.module.linking.subsections.push(symbol_table);
|
let (mut module, linker_symbols) = {
|
||||||
|
let mut backend = WasmBackend::new(env, layout_ids, proc_symbols, linker_symbols, exports);
|
||||||
|
for ((sym, _), proc) in procedures.into_iter() {
|
||||||
|
backend.build_proc(proc, sym)?;
|
||||||
|
}
|
||||||
|
(backend.module, backend.linker_symbols)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(backend.module)
|
let symbol_table = LinkingSubSection::SymbolTable(linker_symbols);
|
||||||
|
module.linking.subsections.push(symbol_table);
|
||||||
|
|
||||||
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopyMemoryConfig {
|
pub struct CopyMemoryConfig {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use super::linking::{IndexRelocType, RelocationEntry};
|
use super::linking::{IndexRelocType, OffsetRelocType, RelocationEntry};
|
||||||
use super::opcodes::*;
|
use super::opcodes::*;
|
||||||
use super::serialize::{SerialBuffer, Serialize};
|
use super::serialize::{SerialBuffer, Serialize};
|
||||||
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
||||||
|
@ -461,6 +461,16 @@ impl<'a> CodeBuilder<'a> {
|
||||||
self.code.encode_u32(offset);
|
self.code.encode_u32(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a linker relocation for a memory address
|
||||||
|
pub fn insert_memory_relocation(&mut self, symbol_index: u32) {
|
||||||
|
self.relocations.push(RelocationEntry::Offset {
|
||||||
|
type_id: OffsetRelocType::MemoryAddrLeb,
|
||||||
|
offset: self.code.len() as u32,
|
||||||
|
symbol_index,
|
||||||
|
addend: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
|
|
||||||
INSTRUCTION METHODS
|
INSTRUCTION METHODS
|
||||||
|
|
|
@ -283,34 +283,47 @@ pub const WASM_SYM_EXPLICIT_NAME: u32 = 0x40; // use the name from the symbol ta
|
||||||
/// linker output, regardless of whether it is used by the program.
|
/// linker output, regardless of whether it is used by the program.
|
||||||
pub const WASM_SYM_NO_STRIP: u32 = 0x80;
|
pub const WASM_SYM_NO_STRIP: u32 = 0x80;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum WasmObjectSymbol {
|
pub enum WasmObjectSymbol {
|
||||||
Defined { index: u32, name: String },
|
Defined {
|
||||||
Imported { index: u32 },
|
flags: u32,
|
||||||
|
index: u32,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
Imported {
|
||||||
|
flags: u32,
|
||||||
|
index: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for WasmObjectSymbol {
|
impl Serialize for WasmObjectSymbol {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
match self {
|
match self {
|
||||||
Self::Defined { index, name } => {
|
Self::Defined { flags, index, name } => {
|
||||||
|
buffer.encode_u32(*flags);
|
||||||
buffer.encode_u32(*index);
|
buffer.encode_u32(*index);
|
||||||
buffer.encode_u32(name.len() as u32);
|
buffer.encode_u32(name.len() as u32);
|
||||||
buffer.append_slice(name.as_bytes());
|
buffer.append_slice(name.as_bytes());
|
||||||
}
|
}
|
||||||
Self::Imported { index } => {
|
Self::Imported { flags, index } => {
|
||||||
|
buffer.encode_u32(*flags);
|
||||||
buffer.encode_u32(*index);
|
buffer.encode_u32(*index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum DataSymbol {
|
pub enum DataSymbol {
|
||||||
Defined {
|
Defined {
|
||||||
|
flags: u32,
|
||||||
name: String,
|
name: String,
|
||||||
index: u32,
|
segment_index: u32,
|
||||||
offset: u32,
|
segment_offset: u32,
|
||||||
size: u32,
|
size: u32,
|
||||||
},
|
},
|
||||||
Imported {
|
Imported {
|
||||||
|
flags: u32,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -319,18 +332,21 @@ impl Serialize for DataSymbol {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
match self {
|
match self {
|
||||||
Self::Defined {
|
Self::Defined {
|
||||||
|
flags,
|
||||||
name,
|
name,
|
||||||
index,
|
segment_index,
|
||||||
offset,
|
segment_offset,
|
||||||
size,
|
size,
|
||||||
} => {
|
} => {
|
||||||
|
buffer.encode_u32(*flags);
|
||||||
buffer.encode_u32(name.len() as u32);
|
buffer.encode_u32(name.len() as u32);
|
||||||
buffer.append_slice(name.as_bytes());
|
buffer.append_slice(name.as_bytes());
|
||||||
buffer.encode_u32(*index);
|
buffer.encode_u32(*segment_index);
|
||||||
buffer.encode_u32(*offset);
|
buffer.encode_u32(*segment_offset);
|
||||||
buffer.encode_u32(*size);
|
buffer.encode_u32(*size);
|
||||||
}
|
}
|
||||||
Self::Imported { name } => {
|
Self::Imported { flags, name } => {
|
||||||
|
buffer.encode_u32(*flags);
|
||||||
buffer.encode_u32(name.len() as u32);
|
buffer.encode_u32(name.len() as u32);
|
||||||
buffer.append_slice(name.as_bytes());
|
buffer.append_slice(name.as_bytes());
|
||||||
}
|
}
|
||||||
|
@ -339,56 +355,56 @@ impl Serialize for DataSymbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// section index (not section id!)
|
/// section index (not section id!)
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SectionIndex(u32);
|
pub struct SectionSymbol {
|
||||||
|
flags: u32,
|
||||||
|
index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum SymInfoFields {
|
impl Serialize for SectionSymbol {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
self.flags.serialize(buffer);
|
||||||
|
self.index.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SymInfo {
|
||||||
Function(WasmObjectSymbol),
|
Function(WasmObjectSymbol),
|
||||||
Data(DataSymbol),
|
Data(DataSymbol),
|
||||||
Global(WasmObjectSymbol),
|
Global(WasmObjectSymbol),
|
||||||
Section(SectionIndex),
|
Section(SectionSymbol),
|
||||||
Event(WasmObjectSymbol),
|
Event(WasmObjectSymbol),
|
||||||
Table(WasmObjectSymbol),
|
Table(WasmObjectSymbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SymInfo {
|
|
||||||
flags: u32,
|
|
||||||
info: SymInfoFields,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymInfo {
|
impl SymInfo {
|
||||||
pub fn for_function(wasm_function_index: u32, name: String) -> Self {
|
pub fn for_function(wasm_function_index: u32, name: String) -> Self {
|
||||||
let linking_symbol = WasmObjectSymbol::Defined {
|
SymInfo::Function(WasmObjectSymbol::Defined {
|
||||||
|
flags: 0,
|
||||||
index: wasm_function_index,
|
index: wasm_function_index,
|
||||||
name,
|
name,
|
||||||
};
|
})
|
||||||
SymInfo {
|
|
||||||
flags: 0,
|
|
||||||
info: SymInfoFields::Function(linking_symbol),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for SymInfo {
|
impl Serialize for SymInfo {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
buffer.append_u8(match self.info {
|
buffer.append_u8(match self {
|
||||||
SymInfoFields::Function(_) => 0,
|
Self::Function(_) => 0,
|
||||||
SymInfoFields::Data(_) => 1,
|
Self::Data(_) => 1,
|
||||||
SymInfoFields::Global(_) => 2,
|
Self::Global(_) => 2,
|
||||||
SymInfoFields::Section(_) => 3,
|
Self::Section(_) => 3,
|
||||||
SymInfoFields::Event(_) => 4,
|
Self::Event(_) => 4,
|
||||||
SymInfoFields::Table(_) => 5,
|
Self::Table(_) => 5,
|
||||||
});
|
});
|
||||||
buffer.encode_u32(self.flags);
|
match self {
|
||||||
match &self.info {
|
Self::Function(x) => x.serialize(buffer),
|
||||||
SymInfoFields::Function(x) => x.serialize(buffer),
|
Self::Data(x) => x.serialize(buffer),
|
||||||
SymInfoFields::Data(x) => x.serialize(buffer),
|
Self::Global(x) => x.serialize(buffer),
|
||||||
SymInfoFields::Global(x) => x.serialize(buffer),
|
Self::Section(x) => x.serialize(buffer),
|
||||||
SymInfoFields::Section(SectionIndex(x)) => {
|
Self::Event(x) => x.serialize(buffer),
|
||||||
buffer.encode_u32(*x);
|
Self::Table(x) => x.serialize(buffer),
|
||||||
}
|
|
||||||
SymInfoFields::Event(x) => x.serialize(buffer),
|
|
||||||
SymInfoFields::Table(x) => x.serialize(buffer),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,9 +107,9 @@ pub struct TypeSection<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TypeSection<'a> {
|
impl<'a> TypeSection<'a> {
|
||||||
pub fn new(arena: &'a Bump) -> Self {
|
pub fn new(arena: &'a Bump, capacity: usize) -> Self {
|
||||||
TypeSection {
|
TypeSection {
|
||||||
signatures: Vec::with_capacity_in(8, arena),
|
signatures: Vec::with_capacity_in(capacity, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,13 +229,12 @@ pub struct FunctionSection<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionSection<'a> {
|
impl<'a> FunctionSection<'a> {
|
||||||
pub fn new(arena: &'a Bump) -> Self {
|
pub fn new(arena: &'a Bump, capacity: usize) -> Self {
|
||||||
FunctionSection {
|
FunctionSection {
|
||||||
signature_indices: Vec::with_capacity_in(8, arena),
|
signature_indices: Vec::with_capacity_in(capacity, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for FunctionSection<'a> {
|
impl<'a> Serialize for FunctionSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
serialize_vector_section(buffer, SectionId::Function, &self.signature_indices);
|
serialize_vector_section(buffer, SectionId::Function, &self.signature_indices);
|
||||||
|
@ -373,14 +372,6 @@ pub struct GlobalSection<'a> {
|
||||||
pub entries: Vec<'a, Global>,
|
pub entries: Vec<'a, Global>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GlobalSection<'a> {
|
|
||||||
pub fn new(arena: &'a Bump) -> Self {
|
|
||||||
GlobalSection {
|
|
||||||
entries: Vec::with_capacity_in(1, arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Serialize for GlobalSection<'a> {
|
impl<'a> Serialize for GlobalSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
serialize_vector_section(buffer, SectionId::Global, &self.entries);
|
serialize_vector_section(buffer, SectionId::Global, &self.entries);
|
||||||
|
@ -507,12 +498,6 @@ pub struct DataSection<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DataSection<'a> {
|
impl<'a> DataSection<'a> {
|
||||||
pub fn new(arena: &'a Bump) -> Self {
|
|
||||||
DataSection {
|
|
||||||
segments: Vec::with_capacity_in(1, arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.segments.is_empty() || self.segments.iter().all(|seg| seg.init.is_empty())
|
self.segments.is_empty() || self.segments.iter().all(|seg| seg.init.is_empty())
|
||||||
}
|
}
|
||||||
|
@ -614,8 +599,7 @@ pub struct WasmModule<'a> {
|
||||||
pub code: CodeSection<'a>,
|
pub code: CodeSection<'a>,
|
||||||
pub data: DataSection<'a>,
|
pub data: DataSection<'a>,
|
||||||
pub linking: LinkingSection<'a>,
|
pub linking: LinkingSection<'a>,
|
||||||
pub reloc_code: RelocationSection<'a>,
|
pub relocations: RelocationSection<'a>,
|
||||||
pub reloc_data: RelocationSection<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmModule<'a> {
|
impl<'a> WasmModule<'a> {
|
||||||
|
@ -654,22 +638,16 @@ impl<'a> WasmModule<'a> {
|
||||||
let data_count_section = DataCountSection::new(&self.data);
|
let data_count_section = DataCountSection::new(&self.data);
|
||||||
counter.serialize_and_count(buffer, &data_count_section);
|
counter.serialize_and_count(buffer, &data_count_section);
|
||||||
|
|
||||||
// Code section mutates its linker relocation data during serialization
|
// Code section is the only one with relocations so we can stop counting
|
||||||
let code_section_index = counter.section_index;
|
let code_section_index = counter.section_index;
|
||||||
self.code
|
self.code
|
||||||
.serialize_with_relocs(buffer, &mut self.reloc_code.entries);
|
.serialize_with_relocs(buffer, &mut self.relocations.entries);
|
||||||
counter.update(buffer);
|
|
||||||
|
|
||||||
// Data section is the last one before linking, so we can stop counting
|
|
||||||
let data_section_index = counter.section_index;
|
|
||||||
self.data.serialize(buffer);
|
self.data.serialize(buffer);
|
||||||
|
|
||||||
self.linking.serialize(buffer);
|
self.linking.serialize(buffer);
|
||||||
|
|
||||||
self.reloc_code.target_section_index = Some(code_section_index);
|
self.relocations.target_section_index = Some(code_section_index);
|
||||||
self.reloc_code.serialize(buffer);
|
self.relocations.serialize(buffer);
|
||||||
|
|
||||||
self.reloc_data.target_section_index = Some(data_section_index);
|
|
||||||
self.reloc_data.serialize(buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub enum LowLevel {
|
||||||
ListDrop,
|
ListDrop,
|
||||||
ListDropAt,
|
ListDropAt,
|
||||||
ListSwap,
|
ListSwap,
|
||||||
|
ListAny,
|
||||||
DictSize,
|
DictSize,
|
||||||
DictEmpty,
|
DictEmpty,
|
||||||
DictInsert,
|
DictInsert,
|
||||||
|
@ -221,6 +222,7 @@ macro_rules! higher_order {
|
||||||
| ListKeepOks
|
| ListKeepOks
|
||||||
| ListKeepErrs
|
| ListKeepErrs
|
||||||
| ListSortWith
|
| ListSortWith
|
||||||
|
| ListAny
|
||||||
| DictWalk
|
| DictWalk
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -254,6 +256,7 @@ impl LowLevel {
|
||||||
ListKeepOks => 1,
|
ListKeepOks => 1,
|
||||||
ListKeepErrs => 1,
|
ListKeepErrs => 1,
|
||||||
ListSortWith => 1,
|
ListSortWith => 1,
|
||||||
|
ListAny => 1,
|
||||||
DictWalk => 2,
|
DictWalk => 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1062,6 +1062,9 @@ define_builtins! {
|
||||||
39 LIST_MAX_GT: "#maxGt"
|
39 LIST_MAX_GT: "#maxGt"
|
||||||
40 LIST_MAP4: "map4"
|
40 LIST_MAP4: "map4"
|
||||||
41 LIST_DROP_FIRST: "dropFirst"
|
41 LIST_DROP_FIRST: "dropFirst"
|
||||||
|
42 LIST_JOIN_MAP: "joinMap"
|
||||||
|
43 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
|
||||||
|
44 LIST_ANY: "any"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
@ -1081,18 +1084,14 @@ define_builtins! {
|
||||||
7 DICT_INSERT: "insert"
|
7 DICT_INSERT: "insert"
|
||||||
8 DICT_LEN: "len"
|
8 DICT_LEN: "len"
|
||||||
|
|
||||||
// This should not be exposed to users, its for testing the
|
9 DICT_REMOVE: "remove"
|
||||||
// hash function ONLY
|
10 DICT_CONTAINS: "contains"
|
||||||
9 DICT_TEST_HASH: "hashTestOnly"
|
11 DICT_KEYS: "keys"
|
||||||
|
12 DICT_VALUES: "values"
|
||||||
|
|
||||||
10 DICT_REMOVE: "remove"
|
13 DICT_UNION: "union"
|
||||||
11 DICT_CONTAINS: "contains"
|
14 DICT_INTERSECTION: "intersection"
|
||||||
12 DICT_KEYS: "keys"
|
15 DICT_DIFFERENCE: "difference"
|
||||||
13 DICT_VALUES: "values"
|
|
||||||
|
|
||||||
14 DICT_UNION: "union"
|
|
||||||
15 DICT_INTERSECTION: "intersection"
|
|
||||||
16 DICT_DIFFERENCE: "difference"
|
|
||||||
}
|
}
|
||||||
7 SET: "Set" => {
|
7 SET: "Set" => {
|
||||||
0 SET_SET: "Set" imported // the Set.Set type alias
|
0 SET_SET: "Set" imported // the Set.Set type alias
|
||||||
|
|
|
@ -967,7 +967,6 @@ fn call_spec(
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListKeepIf { xs } => {
|
ListKeepIf { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
|
|
||||||
|
@ -1073,6 +1072,25 @@ fn call_spec(
|
||||||
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
}
|
||||||
|
ListAny { xs } => {
|
||||||
|
let list = env.symbols[xs];
|
||||||
|
|
||||||
|
let loop_body = |builder: &mut FuncDefBuilder, block, _state| {
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let new_state = call_function!(builder, block, [element]);
|
||||||
|
|
||||||
|
Ok(new_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::Int1);
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
let init_state = new_num(builder, block)?;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -617,7 +617,8 @@ impl<'a> BorrowInfState<'a> {
|
||||||
ListMap { xs }
|
ListMap { xs }
|
||||||
| ListKeepIf { xs }
|
| ListKeepIf { xs }
|
||||||
| ListKeepOks { xs }
|
| ListKeepOks { xs }
|
||||||
| ListKeepErrs { xs } => {
|
| ListKeepErrs { xs }
|
||||||
|
| ListAny { xs } => {
|
||||||
// own the list if the function wants to own the element
|
// own the list if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
self.own_var(*xs);
|
self.own_var(*xs);
|
||||||
|
@ -949,7 +950,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
||||||
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
||||||
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
|
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
|
||||||
ListKeepIf | ListKeepOks | ListKeepErrs => {
|
ListKeepIf | ListKeepOks | ListKeepErrs | ListAny => {
|
||||||
arena.alloc_slice_copy(&[owned, function, closure_data])
|
arena.alloc_slice_copy(&[owned, function, closure_data])
|
||||||
}
|
}
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
|
|
|
@ -530,7 +530,8 @@ impl<'a> Context<'a> {
|
||||||
ListMap { xs }
|
ListMap { xs }
|
||||||
| ListKeepIf { xs }
|
| ListKeepIf { xs }
|
||||||
| ListKeepOks { xs }
|
| ListKeepOks { xs }
|
||||||
| ListKeepErrs { xs } => {
|
| ListKeepErrs { xs }
|
||||||
|
| ListAny { xs } => {
|
||||||
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
|
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
|
||||||
|
|
|
@ -4113,6 +4113,11 @@ pub fn with_hole<'a>(
|
||||||
let xs = arg_symbols[0];
|
let xs = arg_symbols[0];
|
||||||
match_on_closure_argument!(ListKeepIf, [xs])
|
match_on_closure_argument!(ListKeepIf, [xs])
|
||||||
}
|
}
|
||||||
|
ListAny => {
|
||||||
|
debug_assert_eq!(arg_symbols.len(), 2);
|
||||||
|
let xs = arg_symbols[0];
|
||||||
|
match_on_closure_argument!(ListAny, [xs])
|
||||||
|
}
|
||||||
ListKeepOks => {
|
ListKeepOks => {
|
||||||
debug_assert_eq!(arg_symbols.len(), 2);
|
debug_assert_eq!(arg_symbols.len(), 2);
|
||||||
let xs = arg_symbols[0];
|
let xs = arg_symbols[0];
|
||||||
|
|
|
@ -47,6 +47,9 @@ pub enum HigherOrder {
|
||||||
ListSortWith {
|
ListSortWith {
|
||||||
xs: Symbol,
|
xs: Symbol,
|
||||||
},
|
},
|
||||||
|
ListAny {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
DictWalk {
|
DictWalk {
|
||||||
xs: Symbol,
|
xs: Symbol,
|
||||||
state: Symbol,
|
state: Symbol,
|
||||||
|
@ -69,6 +72,7 @@ impl HigherOrder {
|
||||||
HigherOrder::ListKeepErrs { .. } => 1,
|
HigherOrder::ListKeepErrs { .. } => 1,
|
||||||
HigherOrder::ListSortWith { .. } => 2,
|
HigherOrder::ListSortWith { .. } => 2,
|
||||||
HigherOrder::DictWalk { .. } => 2,
|
HigherOrder::DictWalk { .. } => 2,
|
||||||
|
HigherOrder::ListAny { .. } => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[[test]]
|
||||||
|
name = "test_dev"
|
||||||
|
path = "src/tests.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dev-dependencies]
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_build = { path = "../build" }
|
roc_build = { path = "../build" }
|
||||||
|
|
|
@ -5,7 +5,11 @@ authors = ["The Roc Contributors"]
|
||||||
license = "UPL-1.0"
|
license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[[test]]
|
||||||
|
name = "test_gen"
|
||||||
|
path = "src/tests.rs"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
roc_gen_llvm = { path = "../gen_llvm" }
|
roc_gen_llvm = { path = "../gen_llvm" }
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
|
@ -23,7 +27,7 @@ roc_can = { path = "../can" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_build = { path = "../build" }
|
roc_build = { path = "../build" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
roc_gen_wasm = { path = "../gen_wasm" }
|
test_wasm_util = { path = "../test_wasm_util" }
|
||||||
im = "15.0.0"
|
im = "15.0.0"
|
||||||
im-rc = "15.0.0"
|
im-rc = "15.0.0"
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
@ -35,9 +39,6 @@ libloading = "0.7.1"
|
||||||
wasmer = { version = "2.0.0", default-features = false, features = ["default-cranelift", "default-universal"] }
|
wasmer = { version = "2.0.0", default-features = false, features = ["default-cranelift", "default-universal"] }
|
||||||
wasmer-wasi = "2.0.0"
|
wasmer-wasi = "2.0.0"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use crate::assert_evals_to;
|
|
||||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
|
||||||
use indoc::indoc;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_hash() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Dict.hashTestOnly 0 0
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
9718519427346233646,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_str_with_seed() {
|
|
||||||
assert_evals_to!("Dict.hashTestOnly 1 \"a\"", 0xbed235177f41d328, u64);
|
|
||||||
assert_evals_to!("Dict.hashTestOnly 2 \"abc\"", 0xbe348debe59b27c3, u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_record() {
|
|
||||||
assert_evals_to!("Dict.hashTestOnly 1 { x: \"a\" } ", 0xbed235177f41d328, u64);
|
|
||||||
assert_evals_to!(
|
|
||||||
"Dict.hashTestOnly 1 { x: 42, y: 3.14 } ",
|
|
||||||
5348189196103430707,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_result() {
|
|
||||||
assert_evals_to!(
|
|
||||||
"Dict.hashTestOnly 0 (List.get [ 0x1 ] 0) ",
|
|
||||||
2878521786781103245,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_linked_list() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
|
||||||
|
|
||||||
input : LinkedList I64
|
|
||||||
input = Nil
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 input
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
|
||||||
|
|
||||||
input : LinkedList I64
|
|
||||||
input = Cons 4 (Cons 3 Nil)
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 input
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
8287696503006938486,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_expr() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Expr : [ Add Expr Expr, Mul Expr Expr, Val I64, Var I64 ]
|
|
||||||
|
|
||||||
x : Expr
|
|
||||||
x = Val 1
|
|
||||||
|
|
||||||
add : Expr
|
|
||||||
add = Add x x
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 add
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
10825806964604997723,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_nullable_expr() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Expr : [ Add Expr Expr, Mul Expr Expr, Val I64, Empty ]
|
|
||||||
|
|
||||||
x : Expr
|
|
||||||
x = Val 1
|
|
||||||
|
|
||||||
add : Expr
|
|
||||||
add = Add x x
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 add
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
1907558799788307114,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_rosetree() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Rose a : [ Rose (List (Rose a)) ]
|
|
||||||
|
|
||||||
x : Rose I64
|
|
||||||
x = Rose []
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 x
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_union_same_content() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Foo : [ A I64, B I64 ]
|
|
||||||
|
|
||||||
a : Foo
|
|
||||||
a = A 42
|
|
||||||
|
|
||||||
b : Foo
|
|
||||||
b = B 42
|
|
||||||
|
|
||||||
{ a: Dict.hashTestOnly 0 a, b : Dict.hashTestOnly 0 b }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
(i64, i64),
|
|
||||||
|(a, b)| a != b
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_recursive_union_same_content() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Expr : [ Add Expr Expr, Mul Expr Expr, Val1 I64, Val2 I64 ]
|
|
||||||
|
|
||||||
v1 : Expr
|
|
||||||
v1 = Val1 42
|
|
||||||
|
|
||||||
v2 : Expr
|
|
||||||
v2 = Val2 42
|
|
||||||
|
|
||||||
{ a: Dict.hashTestOnly 0 v1, b : Dict.hashTestOnly 0 v2 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
(i64, i64),
|
|
||||||
|(a, b)| a != b
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_nullable_recursive_union_same_content() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
Expr : [ Add Expr Expr, Mul Expr Expr, Val1 I64, Val2 I64, Empty ]
|
|
||||||
|
|
||||||
v1 : Expr
|
|
||||||
v1 = Val1 42
|
|
||||||
|
|
||||||
v2 : Expr
|
|
||||||
v2 = Val2 42
|
|
||||||
|
|
||||||
{ a: Dict.hashTestOnly 0 v1, b : Dict.hashTestOnly 0 v2 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
(i64, i64),
|
|
||||||
|(a, b)| a != b
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_list() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
x : List Str
|
|
||||||
x = [ "foo", "bar", "baz" ]
|
|
||||||
|
|
||||||
Dict.hashTestOnly 0 x
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
10731521034618280801,
|
|
||||||
u64
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -2160,6 +2160,25 @@ fn list_sort_with() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_any() {
|
||||||
|
assert_evals_to!("List.any [] (\\e -> e > 3)", false, bool);
|
||||||
|
assert_evals_to!("List.any [ 1, 2, 3 ] (\\e -> e > 3)", false, bool);
|
||||||
|
assert_evals_to!("List.any [ 1, 2, 4 ] (\\e -> e > 3)", true, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
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:
|
||||||
|
//
|
||||||
|
// Application crashed with message
|
||||||
|
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||||
|
// Shutting down
|
||||||
|
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
|
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
|
||||||
fn lists_with_incompatible_type_param_in_if() {
|
fn lists_with_incompatible_type_param_in_if() {
|
||||||
|
@ -2221,3 +2240,35 @@ fn empty_list_of_function_type() {
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_join_map() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.joinMap ["guava,apple,pear", "bailey,cyrus"] (\s -> Str.split s ",")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[
|
||||||
|
RocStr::from_slice("guava".as_bytes()),
|
||||||
|
RocStr::from_slice("apple".as_bytes()),
|
||||||
|
RocStr::from_slice("pear".as_bytes()),
|
||||||
|
RocStr::from_slice("bailey".as_bytes()),
|
||||||
|
RocStr::from_slice("cyrus".as_bytes()),
|
||||||
|
]),
|
||||||
|
RocList<RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_join_map_empty() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.joinMap [] (\s -> Str.split s ",")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[]),
|
||||||
|
RocList<RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@ use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_can::def::Def;
|
use roc_can::def::Def;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use roc_types::subs::VarStore;
|
use roc_types::subs::VarStore;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
use test_wasm_util::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
|
||||||
fn promote_expr_to_module(src: &str) -> String {
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
pub mod gen_compare;
|
pub mod gen_compare;
|
||||||
pub mod gen_dict;
|
pub mod gen_dict;
|
||||||
pub mod gen_hash;
|
|
||||||
pub mod gen_list;
|
pub mod gen_list;
|
||||||
pub mod gen_num;
|
pub mod gen_num;
|
||||||
pub mod gen_primitives;
|
pub mod gen_primitives;
|
|
@ -5,6 +5,10 @@ authors = ["The Roc Contributors"]
|
||||||
license = "UPL-1.0"
|
license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "test_mono"
|
||||||
|
path = "src/tests.rs"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
|
|
47
compiler/test_mono_macros/Cargo.lock
generated
Normal file
47
compiler/test_mono_macros/Cargo.lock
generated
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_mono_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|
@ -4,8 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[[test]]
|
||||||
|
name = "test_wasm"
|
||||||
|
path = "src/tests.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dev-dependencies]
|
||||||
# roc_module = { path = "../module" }
|
# roc_module = { path = "../module" }
|
||||||
# roc_mono = { path = "../mono" }
|
# roc_mono = { path = "../mono" }
|
||||||
|
|
||||||
|
@ -21,6 +24,7 @@ roc_builtins = { path = "../builtins" }
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
|
test_wasm_util = { path = "../test_wasm_util" }
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
libc = "0.2.106"
|
libc = "0.2.106"
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.2"
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::hash::{Hash, Hasher};
|
||||||
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
||||||
use roc_can::builtins::builtin_defs_map;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
use test_wasm_util::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
|
||||||
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
|
||||||
use roc_gen_wasm::wasm_module::opcodes;
|
use roc_gen_wasm::wasm_module::opcodes;
|
||||||
use roc_gen_wasm::wasm_module::{
|
use roc_gen_wasm::wasm_module::{
|
||||||
Align, CodeBuilder, Export, ExportType, LocalId, Signature, ValueType, WasmModule,
|
Align, CodeBuilder, Export, ExportType, LocalId, Signature, ValueType, WasmModule,
|
||||||
};
|
};
|
||||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||||
|
use test_wasm_util::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
|
||||||
pub trait Wasm32TestResult {
|
pub trait Wasm32TestResult {
|
||||||
fn insert_test_wrapper<'a>(
|
fn insert_test_wrapper<'a>(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod helpers;
|
pub mod helpers;
|
||||||
pub mod wasm_num;
|
pub mod wasm_num;
|
||||||
pub mod wasm_records;
|
pub mod wasm_records;
|
||||||
pub mod wasm_str;
|
pub mod wasm_str;
|
1055
compiler/test_wasm_util/Cargo.lock
generated
Normal file
1055
compiler/test_wasm_util/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
compiler/test_wasm_util/Cargo.toml
Normal file
13
compiler/test_wasm_util/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "test_wasm_util"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[dependencies]
|
||||||
|
# roc_module = { path = "../module" }
|
||||||
|
# roc_mono = { path = "../mono" }
|
||||||
|
|
||||||
|
wasmer = { version = "2.0.0", default-features = false, features = ["default-cranelift", "default-universal"] }
|
||||||
|
|
||||||
|
roc_std = { path = "../../roc_std" }
|
1
compiler/test_wasm_util/src/lib.rs
Normal file
1
compiler/test_wasm_util/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod from_wasm32_memory;
|
|
@ -38,6 +38,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
arraystring = "0.3.0"
|
arraystring = "0.3.0"
|
||||||
libc = "0.2.106"
|
libc = "0.2.106"
|
||||||
page_size = "0.4.2"
|
page_size = "0.4.2"
|
||||||
|
# once winit 0.26 is out, check if copypasta can be updated simultaneously so they use the same versions for their dependencies. This will save build time.
|
||||||
winit = "0.25.0"
|
winit = "0.25.0"
|
||||||
wgpu = "0.11.0"
|
wgpu = "0.11.0"
|
||||||
wgpu_glyph = "0.15.1"
|
wgpu_glyph = "0.15.1"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue