Merge remote-tracking branch 'origin/trunk' into tag-union-imitate-rust

This commit is contained in:
Folkert 2021-11-08 22:31:17 +01:00
commit a9d483cb60
58 changed files with 5821 additions and 843 deletions

3
.gitignore vendored
View file

@ -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

View file

@ -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
View file

@ -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"

View file

@ -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.
# #

View file

@ -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 \

View file

@ -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!

View file

@ -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
View file

@ -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"

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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"] }

View file

@ -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;
// 32-bit wasm
wasm32_target.cpu_arch = std.Target.Cpu.Arch.wasm32;
wasm32_target.os_tag = std.Target.Os.Tag.freestanding;
wasm32_target.abi = std.Target.Abi.none;
const obj_name_wasm32 = "builtins-wasm32";
const llvm_obj_wasm32 = b.addObject(obj_name_wasm32, main_path);
llvm_obj_wasm32.setBuildMode(mode);
llvm_obj_wasm32.strip = true;
llvm_obj_wasm32.emit_llvm_ir = true;
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). // 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. // 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. // 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); fn generateObjectFile(
b: *Builder,
mode: std.builtin.Mode,
target: CrossTarget,
main_path: []const u8,
step_name: []const u8,
object_name: []const u8,
) void {
const obj = b.addObject(object_name, 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 {

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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>,

View file

@ -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

View file

@ -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";

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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"

View file

@ -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,

View file

@ -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 {

View file

@ -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

View file

@ -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),
}; };
} }
} }

View file

@ -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);
} }
} }

View file

@ -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,
} }
} }

View file

@ -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

View file

@ -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)
} }
} }

View file

@ -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]),

View file

@ -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);

View file

@ -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];

View file

@ -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,
} }
} }
} }

View file

@ -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" }

View file

@ -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]

View file

@ -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
);
}

View file

@ -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>
);
}

View file

@ -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");

View file

@ -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;

View file

@ -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
View 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"

View file

@ -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"

View file

@ -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";

View file

@ -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>(

View file

@ -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

File diff suppressed because it is too large Load diff

View 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" }

View file

@ -0,0 +1 @@
pub mod from_wasm32_memory;

View file

@ -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"