diff --git a/.earthignore b/.earthignore new file mode 100644 index 0000000000..a291dd2885 --- /dev/null +++ b/.earthignore @@ -0,0 +1,4 @@ +AUTHORS +nix +.envrc +.gitignore \ No newline at end of file diff --git a/.envrc b/.envrc index 45097a203e..1d953f4bd7 100644 --- a/.envrc +++ b/.envrc @@ -1,154 +1 @@ -# Load environment variables from `nix-shell` and export it out. -# -# Usage: use_nix [-s ] [-w ] [-w ] ... -# -s nix-expression: The nix expression to use for building the shell environment. -# -w path: watch a file for changes. It can be specified multiple times. The -# shell specified with -s is automatically watched. -# -# If no nix-expression were given with -s, it will attempt to find and load -# the shell using the following files in order: shell.nix and default.nix. -# -# Example: -# - use_nix -# - use_nix -s shell.nix -w .nixpkgs-version.json -# -# The dependencies pulled by nix-shell are added to Nix's garbage collector -# roots, such that the environment remains persistent. -# -# Nix-shell is invoked only once per environment, and the output is cached for -# better performance. If any of the watched files change, then the environment -# is rebuilt. -# -# To remove old environments, and allow the GC to collect their dependencies: -# rm -f .direnv -# -use_nix() { - if ! validate_version; then - echo "This .envrc requires direnv version 2.18.2 or above." - exit 1 - fi - - # define all local variables - local shell - local files_to_watch=() - - local opt OPTARG OPTIND # define vars used by getopts locally - while getopts ":n:s:w:" opt; do - case "${opt}" in - s) - shell="${OPTARG}" - files_to_watch=("${files_to_watch[@]}" "${shell}") - ;; - w) - files_to_watch=("${files_to_watch[@]}" "${OPTARG}") - ;; - :) - fail "Invalid option: $OPTARG requires an argument" - ;; - \?) - fail "Invalid option: $OPTARG" - ;; - esac - done - shift $((OPTIND -1)) - - if [[ -z "${shell}" ]]; then - if [[ -f shell.nix ]]; then - shell=shell.nix - files_to_watch=("${files_to_watch[@]}" shell.nix) - elif [[ -f default.nix ]]; then - shell=default.nix - files_to_watch=("${files_to_watch[@]}" default.nix) - else - fail "ERR: no shell was given" - fi - fi - - local f - for f in "${files_to_watch[@]}"; do - if ! [[ -f "${f}" ]]; then - fail "cannot watch file ${f} because it does not exist" - fi - done - - # compute the hash of all the files that makes up the development environment - local env_hash="$(hash_contents "${files_to_watch[@]}")" - - # define the paths - local dir="$(direnv_layout_dir)" - local wd="${dir}/wd-${env_hash}" - local drv="${wd}/env.drv" - local dump="${wd}/dump.env" - - # Generate the environment if we do not have one generated already. - if [[ ! -f "${drv}" ]]; then - mkdir -p "${wd}" - - log_status "use nix: deriving new environment" - IN_NIX_SHELL=1 nix-instantiate --add-root "${drv}" --indirect "${shell}" > /dev/null - nix-store -r $(nix-store --query --references "${drv}") --add-root "${wd}/dep" --indirect > /dev/null - if [[ "${?}" -ne 0 ]] || [[ ! -f "${drv}" ]]; then - rm -rf "${wd}" - fail "use nix: was not able to derive the new environment. Please run 'direnv reload' to try again." - fi - - log_status "use nix: updating cache" - nix-shell --pure "${drv}" --show-trace --run "$(join_args "$direnv" dump bash)" > "${dump}" - if [[ "${?}" -ne 0 ]] || [[ ! -f "${dump}" ]] || ! grep -q IN_NIX_SHELL "${dump}"; then - rm -rf "${wd}" - fail "use nix: was not able to update the cache of the environment. Please run 'direnv reload' to try again." - fi - fi - - # evaluate the dump created by nix-shell earlier, but have to merge the PATH - # with the current PATH - # NOTE: we eval the dump here as opposed to direnv_load it because we don't - # want to persist environment variables coming from the shell at the time of - # the dump. See https://github.com/direnv/direnv/issues/405 for context. - local path_backup="${PATH}" - eval $(cat "${dump}") - export PATH="${PATH}:${path_backup}" - - # cleanup the environment of variables that are not requried, or are causing issues. - unset shellHook # when shellHook is present, then any nix-shell'd script will execute it! - - # watch all the files we were asked to watch for the environment - for f in "${files_to_watch[@]}"; do - watch_file "${f}" - done -} - -fail() { - log_error "${@}" - exit 1 -} - -hash_contents() { - if has md5sum; then - cat "${@}" | md5sum | cut -c -32 - elif has md5; then - cat "${@}" | md5 -q - fi -} - -hash_file() { - if has md5sum; then - md5sum "${@}" | cut -c -32 - elif has md5; then - md5 -q "${@}" - fi -} - -validate_version() { - local version="$("${direnv}" version)" - local major="$(echo "${version}" | cut -d. -f1)" - local minor="$(echo "${version}" | cut -d. -f2)" - local patch="$(echo "${version}" | cut -d. -f3)" - - if [[ "${major}" -gt 2 ]]; then return 0; fi - if [[ "${major}" -eq 2 ]] && [[ "${minor}" -gt 18 ]]; then return 0; fi - if [[ "${major}" -eq 2 ]] && [[ "${minor}" -eq 18 ]] && [[ "${patch}" -ge 2 ]]; then return 0; fi - return 1 -} - -use_nix -s shell.nix +use nix diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 0000000000..3549a505f8 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,38 @@ +on: + pull_request: + paths-ignore: + - '**.md' + +name: Benchmarks + +env: + RUST_BACKTRACE: 1 + +jobs: + prep-dependency-container: + name: benchmark roc programs + runs-on: [self-hosted, i7-6700K] + timeout-minutes: 60 + env: + FORCE_COLOR: 1 + steps: + - uses: actions/checkout@v2 + with: + ref: "trunk" + clean: "true" + + - name: Earthly version + run: earthly --version + + - name: on trunk; prepare a self-contained benchmark folder + run: ./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=trunk +prep-bench-folder + + - uses: actions/checkout@v2 + with: + clean: "false" # we want to keep the benchmark folder + + - name: on current branch; prepare a self-contained benchmark folder + run: ./ci/safe-earthly.sh +prep-bench-folder + + - name: execute benchmarks with regression check + run: ./ci/bench-runner.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c83ca27c3..f3e6f6051a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,7 @@ -on: [pull_request] +on: + pull_request: + paths-ignore: + - '**.md' name: CI @@ -21,5 +24,4 @@ jobs: run: earthly --version - name: install dependencies, build, run zig tests, rustfmt, clippy, cargo test --release - run: ./ci/safe-earthly-test-all.sh - + run: ./ci/safe-earthly.sh +test-all diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 934baf2e8f..9f3791e4ee 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -on: +on: schedule: - cron: '0 0 * * *' diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml new file mode 100644 index 0000000000..fd319f4e65 --- /dev/null +++ b/.github/workflows/spellcheck.yml @@ -0,0 +1,24 @@ +on: [pull_request] + +name: SpellCheck + +env: + RUST_BACKTRACE: 1 + +jobs: + spell-check: + name: spell check + runs-on: [self-hosted] + timeout-minutes: 10 + env: + FORCE_COLOR: 1 + steps: + - uses: actions/checkout@v2 + with: + clean: "true" + + - name: Earthly version + run: earthly --version + + - name: install spell checker, do spell check + run: ./ci/safe-earthly.sh +check-typos diff --git a/.github/workflows/www.yml b/.github/workflows/www.yml new file mode 100644 index 0000000000..308cc4dd0e --- /dev/null +++ b/.github/workflows/www.yml @@ -0,0 +1,22 @@ +name: deploy www.roc-lang.org + +# Whenever a commit lands on trunk, deploy the site +on: + push: + branches: + - deploy-www # TODO change to trunk + +jobs: + deploy: + name: 'Deploy to Netlify' + runs-on: [self-hosted] + steps: + - uses: jsmrcaga/action-netlify-deploy@v1.6.0 + with: + install_command: 'pwd; cd ../../www' + build_command: 'bash build.sh' + build_directory: 'build' + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + NETLIFY_DEPLOY_MESSAGE: "Deploy git ref ${{ github.ref }}" + NETLIFY_DEPLOY_TO_PROD: true diff --git a/.gitignore b/.gitignore index 10d627c503..d0ccfa7943 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ vgcore.* #editors .idea/ .vscode/ +.ignore #files too big to track in git editor/benches/resources/100000_lines.roc @@ -27,3 +28,5 @@ editor/benches/resources/500_lines.roc # rust cache (sccache folder) sccache_dir +# self-contained benchmark folder +bench-folder* diff --git a/.llvmenv b/.llvmenv index 95c4e8d271..4044f90867 100644 --- a/.llvmenv +++ b/.llvmenv @@ -1 +1 @@ -10.0.0 \ No newline at end of file +12.0.0 diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index b2f92c10cf..c40a02e40d 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -20,7 +20,7 @@ For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). ### libunwind & libc++-dev -MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be donw with `sudo apt-get install libunwind-dev`). +MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be done with `sudo apt-get install libunwind-dev`). Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.) ### libcxb libraries @@ -40,23 +40,43 @@ sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev ``` ### Zig -**version: 0.7.x** +**version: 0.8.0** -If you're on MacOS, you can install with `brew install zig` -If you're on Ubuntu and use Snap, you can install with `snap install zig --classic --beta` -For any other OS, checkout the [Zig installation page](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager) +For any OS, you can use [`zigup`](https://github.com/marler8997/zigup) to manage zig installations. + +If you prefer a package manager, you can try the following: +- For MacOS, you can install with `brew install zig` +- For, Ubuntu, you can use Snap, you can install with `snap install zig --classic --beta` +- For other systems, checkout this [page](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager) + +If you want to install it manually, you can also download Zig directly [here](https://ziglang.org/download/). Just make sure you download the right version, the bleeding edge master build is the first download link on this page. ### LLVM -**version: 10.0.x** +**version: 12.0.x** + +For macOS, you can install LLVM 12 using `brew install llvm@12` and then adding +`/usr/local/opt/llvm/bin` to your `PATH`. You can confirm this worked by +running `llc --version` - it should mention "LLVM version 12.0.0" at the top. For Ubuntu and Debian, you can use the `Automatic installation script` at [apt.llvm.org](https://apt.llvm.org): ``` sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ``` -For macOS, check the troubleshooting section below. +If you use this script, you'll need to add `clang` and `llvm-as` to your `PATH`. +By default, the script installs them as `llvm-as-12` and `clang-12`, +respectively. You can address this with symlinks like so: -There are also plenty of alternative options at http://releases.llvm.org/download.html +``` +sudo ln -s /usr/bin/clang-12 /usr/bin/clang +``` +``` +sudo ln -s /usr/bin/llvm-as-12 /usr/bin/llvm-as +```` + +There are also alternative installation options at http://releases.llvm.org/download.html + +[Troubleshooting](#troubleshooting) ## Using Nix @@ -94,6 +114,10 @@ You should be in a shell with everything needed to build already installed. Next You should be in a repl now. Have fun! +### Extra tips + +If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/target/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependecies into your current shell, so you never have to run nix-shell directly! + ### Editor When you want to run the editor from Ubuntu inside nix you need to install [nixGL](https://github.com/guibou/nixGL) as well: @@ -132,33 +156,24 @@ On Ubuntu, running `sudo apt install pkg-config cmake libx11-dev` fixed this. If you encounter `cannot find -lz` run `sudo apt install zlib1g-dev`. +If you encounter: +``` +error: No suitable version of LLVM was found system-wide or pointed + to by LLVM_SYS_120_PREFIX. +``` +Add `export LLVM_SYS_120_PREFIX=/usr/lib/llvm-12` to your `~/.bashrc` or equivalent file for your shell. + ### LLVM installation on macOS -By default homebrew will try to install llvm 11, which is currently -unsupported. You need to install an older version (10.0.0_3) by doing: - -``` -$ brew edit llvm - -# Replace the contents of the file with https://raw.githubusercontent.com/Homebrew/homebrew-core/6616d50fb0b24dbe30f5e975210bdad63257f517/Formula/llvm.rb - -# we expect llvm-as-10 to be present -$ ln -s /usr/local/opt/llvm/bin/{llvm-as,llvm-as-10} - -# "pinning" ensures that homebrew doesn't update it automatically -$ brew pin llvm -``` +If installing LLVM fails, it might help to run `sudo xcode-select -r` before installing again. It might also be useful to add these exports to your shell: ``` -export PATH="/usr/local/opt/llvm/bin:$PATH" export LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" export CPPFLAGS="-I/usr/local/opt/llvm/include" ``` -If installing LLVM still fails, it might help to run `sudo xcode-select -r` before installing again. - ### LLVM installation on Windows Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys` crate that Roc depends on, so I had to build LLVM from source diff --git a/Cargo.lock b/Cargo.lock index 17ae544bde..f0b1a3fe97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "ab_glyph" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a933731feda8b460bdad9a9e43bb386baba6ec593d2bc19716ef3c75c09085c" +checksum = "af0ac006645f86f20f6c6fa4dcaef920bf803df819123626f9440e35835e7d80" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser 0.12.0", @@ -18,9 +18,9 @@ checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" [[package]] name = "addr2line" -version = "0.14.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" dependencies = [ "gimli", ] @@ -39,20 +39,20 @@ checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" [[package]] name = "ahash" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f200cbb1e856866d9eade941cf3aa0c5d7dd36f74311c4273b494f4ef036957" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] @@ -97,6 +97,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "approx" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e" +dependencies = [ + "num-traits", +] + [[package]] name = "arena-pool" version = "0.1.0" @@ -153,15 +162,16 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.56" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" +checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282" dependencies = [ "addr2line", + "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.23.0", + "object 0.25.2", "rustc-demangle", ] @@ -233,9 +243,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" dependencies = [ "lazy_static", "memchr", @@ -245,9 +255,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.6.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" [[package]] name = "byte-tools" @@ -270,7 +280,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -299,18 +309,18 @@ dependencies = [ [[package]] name = "cast" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75" +checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" dependencies = [ "rustc_version", ] [[package]] name = "cc" -version = "1.0.67" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" dependencies = [ "jobserver", ] @@ -378,11 +388,28 @@ source = "git+https://github.com/rtfeldman/clap?branch=master#e1d83a78804a271b05 dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] +[[package]] +name = "cli_utils" +version = "0.1.0" +dependencies = [ + "bumpalo", + "criterion", + "inlinable_string", + "roc_cli", + "roc_collections", + "roc_load", + "roc_module", + "serde", + "serde-xml-rs", + "strip-ansi-escapes", + "tempfile", +] + [[package]] name = "clipboard-win" version = "3.1.1" @@ -457,7 +484,7 @@ dependencies = [ [[package]] name = "confy" version = "0.4.0" -source = "git+https://github.com/rust-cli/confy#0f0c452d652e85674348d9ad6e54bbdebbbd848a" +source = "git+https://github.com/rust-cli/confy#6ae700bb0e6e2f9f7138d0c1871f604013c8f59f" dependencies = [ "directories-next", "serde", @@ -479,9 +506,9 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c36c619c422113552db4eb28cddba8faa757e33f758cc3415bd2885977b591" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", - "unicode-xid 0.2.1", + "unicode-xid 0.2.2", ] [[package]] @@ -588,9 +615,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec1028182c380cc45a2e2c5ec841134f2dfd0f8f5f0a5bcd68004f81b5efdf4" +checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" dependencies = [ "libc", ] @@ -606,9 +633,8 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +version = "0.3.5" +source = "git+https://github.com/Anton-4/criterion.rs#9bd532e35486a3b321d4012534d3e97751a53f85" dependencies = [ "atty", "cast", @@ -633,8 +659,7 @@ dependencies = [ [[package]] name = "criterion-plot" version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" +source = "git+https://github.com/Anton-4/criterion.rs#9bd532e35486a3b321d4012534d3e97751a53f85" dependencies = [ "cast", "itertools 0.9.0", @@ -671,7 +696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.3", + "crossbeam-utils 0.8.5", ] [[package]] @@ -692,8 +717,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if 1.0.0", - "crossbeam-epoch 0.9.3", - "crossbeam-utils 0.8.3", + "crossbeam-epoch 0.9.5", + "crossbeam-utils 0.8.5", ] [[package]] @@ -713,14 +738,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.3", + "crossbeam-utils 0.8.5", "lazy_static", - "memoffset 0.6.3", + "memoffset 0.6.4", "scopeguard", ] @@ -748,11 +773,10 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "autocfg 1.0.1", "cfg-if 1.0.0", "lazy_static", ] @@ -818,7 +842,7 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "strsim", "syn 1.0.72", @@ -841,7 +865,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -943,6 +967,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "drm-fourcc" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf" +dependencies = [ + "serde", +] + [[package]] name = "dtoa" version = "0.4.8" @@ -984,6 +1017,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "external-memory" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39" +dependencies = [ + "bitflags", + "drm-fourcc", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1053,9 +1096,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" dependencies = [ "futures-channel", "futures-core", @@ -1068,9 +1111,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" dependencies = [ "futures-core", "futures-sink", @@ -1078,15 +1121,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" [[package]] name = "futures-executor" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" +checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" dependencies = [ "futures-core", "futures-task", @@ -1095,40 +1138,42 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" [[package]] name = "futures-macro" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" +checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" dependencies = [ + "autocfg 1.0.1", "proc-macro-hack", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] [[package]] name = "futures-sink" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" [[package]] name = "futures-task" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" [[package]] name = "futures-util" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" dependencies = [ + "autocfg 1.0.1", "futures-channel", "futures-core", "futures-io", @@ -1184,9 +1229,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1195,9 +1240,9 @@ dependencies = [ [[package]] name = "gfx-auxil" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccf8711c9994dfa34337466bee3ae1462e172874c432ce4eb120ab2e98d39cf" +checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c" dependencies = [ "fxhash", "gfx-hal", @@ -1206,14 +1251,15 @@ dependencies = [ [[package]] name = "gfx-backend-dx11" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f839f27f8c8a6dc553ccca7f5b35a42009432bc25db9688bba7061cd394161f" +checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae" dependencies = [ "arrayvec", "bitflags", "gfx-auxil", "gfx-hal", + "gfx-renderdoc", "libloading 0.7.0", "log", "parking_lot", @@ -1228,9 +1274,9 @@ dependencies = [ [[package]] name = "gfx-backend-dx12" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3937738b0da5839bba4e33980d29f9a06dbce184d04a3a08c9a949e7953700e3" +checksum = "61f09e9d8c2aa69e9a21eb83c0f5d1a286c6d37da011f796e550d180b08090ce" dependencies = [ "arrayvec", "bit-set", @@ -1238,6 +1284,7 @@ dependencies = [ "d3d12", "gfx-auxil", "gfx-hal", + "gfx-renderdoc", "log", "parking_lot", "range-alloc", @@ -1250,9 +1297,9 @@ dependencies = [ [[package]] name = "gfx-backend-empty" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac55ada4bfcd35479b3421eea324d36d7da5f724e2f66ecb36d4efdb7041a5e" +checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4" dependencies = [ "gfx-hal", "log", @@ -1261,9 +1308,9 @@ dependencies = [ [[package]] name = "gfx-backend-gl" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0caa03d6e0b7b4f202aea1f20c3f3288cfa06d92d24cea9d69c9a7627967244a" +checksum = "6bae057fc3a0ab23ecf97ae51d4017d27d5ddf0aab16ee6dcb58981af88c3152" dependencies = [ "arrayvec", "bitflags", @@ -1283,15 +1330,16 @@ dependencies = [ [[package]] name = "gfx-backend-metal" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340895ad544ba46433acb3bdabece0ef16f2dbedc030adbd7c9eaf2839fbed41" +checksum = "0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517" dependencies = [ "arrayvec", "bitflags", "block", "cocoa-foundation", "copyless", + "core-graphics-types", "foreign-types", "fxhash", "gfx-hal", @@ -1308,15 +1356,16 @@ dependencies = [ [[package]] name = "gfx-backend-vulkan" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a353fc6fdb42ec646de49bbb74e4870e37a7e680caf33f3ac0615c30b1146d94" +checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0" dependencies = [ "arrayvec", "ash", "byteorder", "core-graphics-types", "gfx-hal", + "gfx-renderdoc", "inplace_it", "log", "naga", @@ -1329,21 +1378,33 @@ dependencies = [ [[package]] name = "gfx-hal" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d285bfd566f6b9134af908446ca350c0a1047495dfb9bbd826e701e8ee1d259" +checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd" dependencies = [ "bitflags", + "external-memory", "naga", "raw-window-handle", "thiserror", ] [[package]] -name = "gimli" -version = "0.23.0" +name = "gfx-renderdoc" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +checksum = "c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a" +dependencies = [ + "libloading 0.7.0", + "log", + "renderdoc-sys", +] + +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" [[package]] name = "glow" @@ -1387,20 +1448,20 @@ dependencies = [ [[package]] name = "glyph_brush_layout" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bc06d530bf20c1902f1b02799ab7372ff43f6119770c49b0bc3f21bd148820" +checksum = "15cf18cf985bd942f05e14552b63c9d08f7d0ed1ec79a977eb9747c9e065f497" dependencies = [ "ab_glyph", - "approx 0.4.0", + "approx 0.5.0", "xi-unicode", ] [[package]] name = "gpu-alloc" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76088804bb65a6f3b880bea9306fdaeffb25ebb453105fafa691282ee9fdba" +checksum = "cbc1b6ca374e81862526786d9cb42357ce03706ed1b8761730caafd02ab91f3a" dependencies = [ "bitflags", "gpu-alloc-types", @@ -1456,15 +1517,15 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.2", + "ahash 0.7.4", "bumpalo", ] [[package]] name = "heck" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] @@ -1513,7 +1574,7 @@ dependencies = [ "bitmaps", "rand_core 0.5.1", "rand_xoshiro", - "sized-chunks 0.6.4", + "sized-chunks 0.6.5", "typenum", "version_check", ] @@ -1541,7 +1602,7 @@ dependencies = [ "bitmaps", "rand_core 0.5.1", "rand_xoshiro", - "sized-chunks 0.6.4", + "sized-chunks 0.6.5", "typenum", "version_check", ] @@ -1582,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", "unindent", @@ -1591,7 +1652,14 @@ dependencies = [ [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/rtfeldman/inkwell?tag=llvm10-0.release4#9ae45f072645165885b2f347a4c0cc5ce9e22c80" +dependencies = [ + "inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release5)", +] + +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release5#b5aa51313d4cfa4e1b52ad630cb786140671478b" dependencies = [ "either", "inkwell_internals", @@ -1605,9 +1673,9 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.3.0" -source = "git+https://github.com/rtfeldman/inkwell?tag=llvm10-0.release4#9ae45f072645165885b2f347a4c0cc5ce9e22c80" +source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release5#b5aa51313d4cfa4e1b52ad630cb786140671478b" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -1674,9 +1742,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" dependencies = [ "libc", ] @@ -1730,9 +1798,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.93" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc" [[package]] name = "libloading" @@ -1762,9 +1830,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "llvm-sys" -version = "100.2.1" +version = "120.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d9c00ce56221b2150e2d4d51887ff139fce5a0e50346c744861d1e66d2f7c4" +checksum = "7b883556196140c6b6e7a18b19236b9a699c8611aad2e48a0a6403cf1123945a" dependencies = [ "cc", "lazy_static", @@ -1775,9 +1843,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" dependencies = [ "scopeguard", ] @@ -1814,9 +1882,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memmap2" @@ -1838,22 +1906,22 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg 1.0.1", ] [[package]] name = "metal" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777" +checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450" dependencies = [ "bitflags", "block", - "cocoa-foundation", + "core-graphics-types", "foreign-types", "log", "objc", @@ -1917,14 +1985,15 @@ name = "morphic_lib" version = "0.1.0" dependencies = [ "sha2", + "smallvec", "thiserror", ] [[package]] name = "naga" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f470a97eafcdd0dbea43d5e1a8ef3557aa31f49ba643d9430dbbf911c162b24c" +checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64" dependencies = [ "bit-set", "bitflags", @@ -1933,6 +2002,7 @@ dependencies = [ "log", "num-traits", "petgraph", + "rose_tree", "spirv_headers", "thiserror", ] @@ -1971,7 +2041,7 @@ checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" dependencies = [ "darling", "proc-macro-crate", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -2082,7 +2152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -2126,12 +2196,6 @@ dependencies = [ "objc", ] -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - [[package]] name = "object" version = "0.24.0" @@ -2143,6 +2207,15 @@ dependencies = [ "indexmap", ] +[[package]] +name = "object" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bc1d42047cf336f0f939c99e97183cf31551bf0f2865a2ec9c8d91fd4ffb5e" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.7.2" @@ -2169,9 +2242,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "ordered-float" -version = "2.1.1" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218" +checksum = "f100fcfb41e5385e0991f74981732049f9b896821542a219420491046baafdc2" dependencies = [ "num-traits", ] @@ -2200,7 +2273,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3c7a20e3f122223e68eef6ca58e39bc1ea8a1d83418ba4c2c1ba189d2ee355" dependencies = [ - "ttf-parser 0.12.0", + "ttf-parser 0.12.1", ] [[package]] @@ -2232,7 +2305,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -2259,7 +2332,7 @@ dependencies = [ "instant", "libc", "petgraph", - "redox_syscall 0.2.5", + "redox_syscall 0.2.8", "smallvec", "thread-id", "winapi 0.3.9", @@ -2298,7 +2371,7 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -2388,9 +2461,8 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "plotters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" +version = "0.3.1" +source = "git+https://github.com/Anton-4/plotters#d043988179b61db714ad60f678637ee145e363d3" dependencies = [ "num-traits", "plotters-backend", @@ -2458,7 +2530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", "version_check", @@ -2470,7 +2542,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", "syn-mid", @@ -2500,18 +2572,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ - "unicode-xid 0.2.1", + "unicode-xid 0.2.2", ] [[package]] name = "profiling" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a66d5e88679f2720126c11ee29da07a08f094eac52306b066edd7d393752d6" +checksum = "0a7c000c0ce9d9bb94c0fbacdf20e5087fbe652c556ffb2c9387d980e17d51fb" [[package]] name = "pulldown-cmark" @@ -2564,7 +2636,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -2584,7 +2656,7 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", ] [[package]] @@ -2692,7 +2764,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", ] [[package]] @@ -2810,9 +2882,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg 1.0.1", "crossbeam-deque 0.8.0", @@ -2822,13 +2894,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel 0.5.1", "crossbeam-deque 0.8.0", - "crossbeam-utils 0.8.3", + "crossbeam-utils 0.8.5", "lazy_static", "num_cpus", ] @@ -2850,9 +2922,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" dependencies = [ "bitflags", ] @@ -2863,15 +2935,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "getrandom 0.2.2", - "redox_syscall 0.2.5", + "getrandom 0.2.3", + "redox_syscall 0.2.8", ] [[package]] name = "regex" -version = "1.4.5" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", @@ -2880,18 +2952,15 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "remove_dir_all" @@ -2902,6 +2971,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "renderdoc-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" + [[package]] name = "roc_build" version = "0.1.0" @@ -2910,7 +2985,7 @@ dependencies = [ "im 14.3.0", "im-rc 14.3.0", "indoc 0.3.6", - "inkwell", + "inkwell 0.1.0", "inlinable_string", "libloading 0.6.7", "maplit", @@ -2921,7 +2996,7 @@ dependencies = [ "roc_can", "roc_collections", "roc_constrain", - "roc_gen", + "roc_gen_llvm", "roc_load", "roc_module", "roc_mono", @@ -2981,11 +3056,13 @@ version = "0.1.0" dependencies = [ "bumpalo", "clap 3.0.0-beta.1", + "cli_utils", "const_format", + "criterion", "im 14.3.0", "im-rc 14.3.0", "indoc 0.3.6", - "inkwell", + "inkwell 0.1.0", "inlinable_string", "libc", "libloading 0.6.7", @@ -3001,7 +3078,7 @@ dependencies = [ "roc_docs", "roc_editor", "roc_fmt", - "roc_gen", + "roc_gen_llvm", "roc_load", "roc_module", "roc_mono", @@ -3014,10 +3091,7 @@ dependencies = [ "roc_unify", "rustyline", "rustyline-derive", - "serde", - "serde-xml-rs", "serial_test", - "strip-ansi-escapes", "target-lexicon", "tempfile", ] @@ -3065,6 +3139,7 @@ dependencies = [ "roc_load", "roc_module", "roc_region", + "roc_types", ] [[package]] @@ -3078,7 +3153,6 @@ dependencies = [ "colored", "confy", "copypasta", - "criterion", "env_logger 0.8.3", "futures", "glyph_brush", @@ -3138,41 +3212,6 @@ dependencies = [ "roc_region", ] -[[package]] -name = "roc_gen" -version = "0.1.0" -dependencies = [ - "bumpalo", - "either", - "im 14.3.0", - "im-rc 14.3.0", - "indoc 0.3.6", - "inkwell", - "inlinable_string", - "libc", - "maplit", - "pretty_assertions 0.5.1", - "quickcheck 0.8.5", - "quickcheck_macros 0.8.0", - "roc_build", - "roc_builtins", - "roc_can", - "roc_collections", - "roc_load", - "roc_module", - "roc_mono", - "roc_parse", - "roc_problem", - "roc_region", - "roc_reporting", - "roc_solve", - "roc_std", - "roc_types", - "roc_unify", - "target-lexicon", - "tokio", -] - [[package]] name = "roc_gen_dev" version = "0.1.0" @@ -3211,6 +3250,41 @@ dependencies = [ "tokio", ] +[[package]] +name = "roc_gen_llvm" +version = "0.1.0" +dependencies = [ + "bumpalo", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", + "inkwell 0.1.0", + "inlinable_string", + "libc", + "maplit", + "morphic_lib", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", + "roc_build", + "roc_builtins", + "roc_can", + "roc_collections", + "roc_load", + "roc_module", + "roc_mono", + "roc_parse", + "roc_problem", + "roc_region", + "roc_reporting", + "roc_solve", + "roc_std", + "roc_types", + "roc_unify", + "target-lexicon", + "tokio", +] + [[package]] name = "roc_load" version = "0.1.0" @@ -3220,6 +3294,7 @@ dependencies = [ "indoc 0.3.6", "inlinable_string", "maplit", + "morphic_lib", "num_cpus", "parking_lot", "pretty_assertions 0.5.1", @@ -3419,10 +3494,19 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.18" +name = "rose_tree" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +checksum = "284de9dae38774e2813aaabd7e947b4a6fe9b8c58c2309f754a487cdd50de1c2" +dependencies = [ + "petgraph", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce" [[package]] name = "rustc-hash" @@ -3432,9 +3516,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.2.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ "semver", ] @@ -3505,24 +3589,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ "semver-parser", ] [[package]] name = "semver-parser" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "serde" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] @@ -3551,11 +3638,11 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -3600,7 +3687,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -3619,9 +3706,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f6b75b17576b792bef0db1bcc4b8b8bcdf9506744cf34b974195487af6cff2" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -3648,9 +3735,9 @@ dependencies = [ [[package]] name = "sized-chunks" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e65d6a9f13cd78f361ea5a2cf53a45d67cdda421ba0316b9be101560f3d207" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" dependencies = [ "bitmaps", "typenum", @@ -3658,9 +3745,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" [[package]] name = "slotmap" @@ -3720,7 +3807,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -3793,9 +3880,9 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", - "unicode-xid 0.2.1", + "unicode-xid 0.2.2", ] [[package]] @@ -3804,7 +3891,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baa8e7560a164edb1621a55d18a0c59abf49d360f47aa7b821061dd7eea7fac9" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -3815,10 +3902,10 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", - "unicode-xid 0.2.1", + "unicode-xid 0.2.2", ] [[package]] @@ -3836,7 +3923,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall 0.2.5", + "redox_syscall 0.2.8", "remove_dir_all", "winapi 0.3.9", ] @@ -3859,7 +3946,7 @@ dependencies = [ "im 14.3.0", "im-rc 14.3.0", "indoc 0.3.6", - "inkwell", + "inkwell 0.1.0", "inlinable_string", "libc", "libloading 0.6.7", @@ -3871,7 +3958,7 @@ dependencies = [ "roc_can", "roc_collections", "roc_constrain", - "roc_gen", + "roc_gen_llvm", "roc_load", "roc_module", "roc_mono", @@ -3887,6 +3974,50 @@ dependencies = [ "tokio", ] +[[package]] +name = "test_mono" +version = "0.1.0" +dependencies = [ + "bumpalo", + "either", + "im 14.3.0", + "im-rc 14.3.0", + "indoc 0.3.6", + "inlinable_string", + "libc", + "libloading 0.6.7", + "pretty_assertions 0.5.1", + "quickcheck 0.8.5", + "quickcheck_macros 0.8.0", + "roc_build", + "roc_builtins", + "roc_can", + "roc_collections", + "roc_constrain", + "roc_load", + "roc_module", + "roc_mono", + "roc_parse", + "roc_problem", + "roc_region", + "roc_reporting", + "roc_solve", + "roc_types", + "roc_unify", + "target-lexicon", + "test_mono_macros", +] + +[[package]] +name = "test_mono_macros" +version = "0.1.0" +dependencies = [ + "darling", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -3898,20 +4029,20 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", ] @@ -3974,9 +4105,9 @@ checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] name = "ttf-parser" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e00391c1f3d171490a3f8bd79999b0002ae38d3da0d6a3a306c754b053d71b" +checksum = "2fc71742ead70703a55d184f82087302f2f9ffa3793e64db46a78bf75dd723f4" [[package]] name = "twox-hash" @@ -4036,9 +4167,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "unindent" @@ -4153,7 +4284,7 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", "wasm-bindgen-shared", @@ -4187,7 +4318,7 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "syn 1.0.72", "wasm-bindgen-backend", @@ -4257,7 +4388,7 @@ version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389d680d7bd67512dc9c37f39560224327038deb0f0e8d33f870900441b68720" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "quote 1.0.9", "xml-rs", ] @@ -4285,9 +4416,9 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215fd50e66f794bd16683e7e0e0b9b53be265eb10fdf02276caf5de3e5743fcf" +checksum = "bd247f8b26fd3d42ef2f320d378025cd6e84d782ef749fab45cc3b981fbe3275" dependencies = [ "arrayvec", "js-sys", @@ -4305,9 +4436,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d56c368fc0e6f3927c711d2b55a51ad4321218efc0239c4acf69e456ab70399" +checksum = "2af5c8acd3ae5781a277cdf65a17f3a7135de5ae782775620e74ea16c9d47770" dependencies = [ "arrayvec", "bitflags", @@ -4335,18 +4466,18 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa248d90c8e6832269b8955bf800e8241f942c25e18a235b7752226804d21556" +checksum = "4f5c9678cd533558e28b416d66947b099742df1939307478db54f867137f1b60" dependencies = [ "bitflags", ] [[package]] name = "wgpu_glyph" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634570b440f4c24c2e6049ed01ec832c23d338dea3eca654d5760838017a1c8b" +checksum = "0fee8c96eda18195a7ad9989737183e0a357f14b15e98838c76abbcf56a5f970" dependencies = [ "bytemuck", "glyph_brush", @@ -4458,9 +4589,9 @@ dependencies = [ [[package]] name = "x11-clipboard" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e937afd03b64b7be4f959cc044e09260a47241b71e56933f37db097bf7859d" +checksum = "b397ace6e980510de59a4fe3d4c758dffab231d6d747ce9fa1aba6b6035d5f32" dependencies = [ "xcb", ] @@ -4539,7 +4670,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" dependencies = [ - "proc-macro2 1.0.26", + "proc-macro2 1.0.27", "syn 1.0.72", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 8343f413ed..06f10ff1fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,20 +14,30 @@ members = [ "compiler/reporting", "compiler/fmt", "compiler/mono", + "compiler/test_mono_macros", + "compiler/test_mono", "compiler/load", - "compiler/gen", + "compiler/gen_llvm", "compiler/gen_dev", "compiler/build", "compiler/arena_pool", "compiler/test_gen", "vendor/ena", + "vendor/inkwell", "vendor/pathfinding", "vendor/pretty", "editor", "cli", + "cli/cli_utils", "roc_std", - "docs" + "docs", ] +# Needed to be able to run `cargo run -p roc_cli --no-default-features` - +# see www/build.sh for more. +# +# Without the `-p` flag, cargo ignores `--no-default-features` when you have a +# workspace, and without `resolver = "2"` here, you can't use `-p` like this. +resolver = "2" # Optimizations based on https://deterministic.space/high-performance-rust.html [profile.release] diff --git a/Earthfile b/Earthfile index 8d7be65f9e..3415c05a3b 100644 --- a/Earthfile +++ b/Earthfile @@ -1,4 +1,4 @@ -FROM rust:1.52-slim-buster +FROM rust:1.53-slim-buster WORKDIR /earthbuild prep-debian: @@ -13,17 +13,18 @@ install-other-libs: install-zig-llvm-valgrind-clippy-rustfmt: FROM +install-other-libs # zig - RUN wget -c https://ziglang.org/download/0.7.1/zig-linux-x86_64-0.7.1.tar.xz --no-check-certificate - RUN tar -xf zig-linux-x86_64-0.7.1.tar.xz - RUN ln -s /earthbuild/zig-linux-x86_64-0.7.1/zig /usr/bin/zig + RUN wget -c https://ziglang.org/download/0.8.0/zig-linux-x86_64-0.8.0.tar.xz --no-check-certificate + RUN tar -xf zig-linux-x86_64-0.8.0.tar.xz + RUN ln -s /earthbuild/zig-linux-x86_64-0.8.0/zig /usr/bin/zig # llvm RUN apt -y install lsb-release software-properties-common gnupg RUN wget https://apt.llvm.org/llvm.sh RUN chmod +x llvm.sh - RUN ./llvm.sh 10 - RUN ln -s /usr/bin/clang-10 /usr/bin/clang + RUN ./llvm.sh 12 + RUN ln -s /usr/bin/clang-12 /usr/bin/clang + RUN ln -s /usr/bin/llvm-as-12 /usr/bin/llvm-as # use lld as linker - RUN ln -s /usr/bin/lld-10 /usr/bin/ld.lld + RUN ln -s /usr/bin/lld-12 /usr/bin/ld.lld ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native" # valgrind RUN apt -y install autotools-dev cmake automake libc6-dbg @@ -38,6 +39,8 @@ install-zig-llvm-valgrind-clippy-rustfmt: RUN rustup component add clippy # rustfmt RUN rustup component add rustfmt + # criterion + RUN cargo install --git https://github.com/Anton-4/cargo-criterion --branch main # sccache RUN apt -y install libssl-dev RUN cargo install sccache @@ -97,15 +100,49 @@ check-rustfmt: RUN cargo fmt --version RUN cargo fmt --all -- --check +check-typos: + RUN cargo install typos-cli + COPY --dir .github ci cli compiler docs editor examples nightly_benches packages roc_std www *.md LEGAL_DETAILS shell.nix ./ + RUN typos + test-rust: FROM +copy-dirs-and-cache ENV RUST_BACKTRACE=1 RUN --mount=type=cache,target=$SCCACHE_DIR \ cargo test --release && sccache --show-stats +verify-no-git-changes: + FROM +test-rust + # If running tests caused anything to be changed or added (without being + # included in a .gitignore somewhere), fail the build! + # + # How it works: the `git ls-files` command lists all the modified or + # uncommitted files in the working tree, the `| grep -E .` command returns a + # zero exit code if it listed any files and nonzero otherwise (which is the + # opposite of what we want), and the `!` at the start inverts the exit code. + RUN ! git ls-files --deleted --modified --others --exclude-standard | grep -E . + test-all: BUILD +test-zig BUILD +check-rustfmt BUILD +check-clippy + BUILD +check-typos BUILD +test-rust - + BUILD +verify-no-git-changes + +# compile everything needed for benchmarks and output a self-contained folder +prep-bench-folder: + FROM +copy-dirs-and-cache + ARG BENCH_SUFFIX=branch + RUN cargo criterion -V + RUN --mount=type=cache,target=$SCCACHE_DIR cd cli && cargo criterion --no-run + RUN mkdir -p bench-folder/compiler/builtins/bitcode/src + RUN mkdir -p bench-folder/target/release/deps + RUN mkdir -p bench-folder/examples/benchmarks + RUN cp examples/benchmarks/*.roc bench-folder/examples/benchmarks/ + RUN cp -r examples/benchmarks/platform bench-folder/examples/benchmarks/ + RUN cp compiler/builtins/bitcode/src/str.zig bench-folder/compiler/builtins/bitcode/src + RUN cp target/release/roc bench-folder/target/release + # copy the most recent time bench to bench-folder + RUN cp target/release/deps/`ls -t target/release/deps/ | grep time_bench | head -n 1` bench-folder/target/release/deps/time_bench + SAVE ARTIFACT bench-folder AS LOCAL bench-folder-$BENCH_SUFFIX diff --git a/README.md b/README.md index 925ff3944e..001b4fbe66 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ By using systems-level programming languages like C and C++, platform authors sa Roc is designed to make the "systems-level platform, higher-level application" experience as nice as possible. * **Application** authors code exclusively in Roc. It's a language designed for nice ergonomics. The syntax resembles Ruby or CoffeeScript, and it has a fast compiler with full type inference. -* **Platform** authors code almost exclusively in a systems-level langauge like C, C++, Rust, or [Zig](https://ziglang.org/), except for the thin Roc API they expose to application authors. Roc application code compiles to machine code, and production builds of Roc apps benefit from the same [LLVM](https://llvm.org/) optimizations that C++, Rust, and Zig do. Roc application authors do not need to know this lower-level code exists; all they have to interact with is the platform's API, which is exposed as an ordinary Roc API. +* **Platform** authors code almost exclusively in a systems-level language like C, C++, Rust, or [Zig](https://ziglang.org/), except for the thin Roc API they expose to application authors. Roc application code compiles to machine code, and production builds of Roc apps benefit from the same [LLVM](https://llvm.org/) optimizations that C++, Rust, and Zig do. Roc application authors do not need to know this lower-level code exists; all they have to interact with is the platform's API, which is exposed as an ordinary Roc API. Every Roc application is built on top of exactly one Roc platform. There is no such thing as a Roc application that runs without a platform, and there is no default platform. You must choose one! diff --git a/ci/bench-runner.sh b/ci/bench-runner.sh new file mode 100755 index 0000000000..22ce98d951 --- /dev/null +++ b/ci/bench-runner.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# script to return exit code 1 if benchmarks have regressed + +# benchmark trunk +ulimit -s unlimited +cd bench-folder-trunk +./target/release/deps/time_bench --bench +cd .. + +# move benchmark results so they can be compared later +cp -r bench-folder-trunk/target/criterion bench-folder-branch/target/ + +cd bench-folder-branch + +LOG_FILE="bench_log.txt" +touch $LOG_FILE + +FULL_CMD=" ./target/release/deps/time_bench --bench" +echo $FULL_CMD + +script -efq $LOG_FILE -c "$FULL_CMD" +EXIT_CODE=$? + +if cat $LOG_FILE | grep -q "regressed"; then + + grep -B3 "regressed" $LOG_FILE | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' | grep -o "\".*\"" | rev | cut -d' ' -f1 | rev > slow_benches_1.txt + echo "regression(s) detected in:" + cat slow_benches_1.txt + echo "" + echo "" + echo "------<<<<<<>>>>>>------" + echo "Benchmark detected regression. Running benchmark again to confirm..." + echo "------<<<<<<>>>>>>------" + echo "" + echo "" + + # delete criterion folder to remove old benchmark data + rm -rf ./target/criterion + + # benchmark trunk again + cd ../bench-folder-trunk + rm -rf target/criterion + ./target/release/deps/time_bench --bench + + cd ../bench-folder-branch + cp -r ../bench-folder-trunk/target/criterion ./target + + rm $LOG_FILE + touch $LOG_FILE + + script -efq $LOG_FILE -c "$FULL_CMD" + EXIT_CODE=$? + + if cat $LOG_FILE | grep -q "regressed"; then + + grep -B3 "regressed" $LOG_FILE | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' | grep -o "\".*\"" | rev | cut -d' ' -f1 | rev > slow_benches_2.txt + echo "regression(s) detected in:" + cat slow_benches_2.txt + + if [[ $(grep -Fxf slow_benches_1.txt slow_benches_2.txt | wc -l) -gt 0 ]]; then + echo "" + echo "" + echo "------<<<<<>>>>>------" + echo "Benchmarks were run twice and a regression was detected both times for the following benchmarks:" + grep -Fxf slow_benches_1.txt slow_benches_2.txt + echo "------<<<<<>>>>>------" + echo "" + echo "" + exit 1 + else + echo "Benchmarks were run twice and a regression was detected on one run. We assume this was a fluke." + exit 0 + fi + else + echo "Benchmarks were run twice and a regression was detected on one run. We assume this was a fluke." + exit 0 + fi +else + echo "" + echo "Benchmark execution finished with exit code: $EXIT_CODE." + echo "" + exit $EXIT_CODE +fi \ No newline at end of file diff --git a/ci/safe-earthly-test-all.sh b/ci/safe-earthly.sh similarity index 62% rename from ci/safe-earthly-test-all.sh rename to ci/safe-earthly.sh index dbd03d6c79..0ddc13c5c0 100755 --- a/ci/safe-earthly-test-all.sh +++ b/ci/safe-earthly.sh @@ -1,9 +1,12 @@ #!/usr/bin/env bash - LOG_FILE="earthly_log.txt" touch $LOG_FILE -script -efq $LOG_FILE -c "earthly --config ci/earthly-conf.yml +test-all" +# first arg + everything after +ARGS=${@:1} +FULL_CMD="earthly --config ci/earthly-conf.yml $ARGS" +echo $FULL_CMD +script -efq $LOG_FILE -c "$FULL_CMD" EXIT_CODE=$? if grep -q "failed to mount" "$LOG_FILE"; then @@ -14,7 +17,7 @@ if grep -q "failed to mount" "$LOG_FILE"; then echo "------<<<<<>>>>>------" echo "" echo "" - earthly --config ci/earthly-conf.yml --no-cache +test-all + earthly --config ci/earthly-conf.yml --no-cache $ARGS else exit $EXIT_CODE fi diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 007d67b699..531e9a76ee 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -15,7 +15,12 @@ test = false bench = false [features] -default = ["target-x86"] +default = ["target-x86", "llvm", "editor"] + +# This is a separate feature because when we generate docs on Netlify, +# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.) +llvm = ["inkwell", "roc_gen_llvm", "roc_build/llvm"] +editor = ["roc_editor"] target-x86 = [] @@ -45,11 +50,11 @@ roc_unify = { path = "../compiler/unify" } roc_solve = { path = "../compiler/solve" } roc_mono = { path = "../compiler/mono" } roc_load = { path = "../compiler/load" } -roc_gen = { path = "../compiler/gen" } -roc_build = { path = "../compiler/build" } +roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true } +roc_build = { path = "../compiler/build", default-features = false } roc_fmt = { path = "../compiler/fmt" } roc_reporting = { path = "../compiler/reporting" } -roc_editor = { path = "../editor" } +roc_editor = { path = "../editor", optional = true } # TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it clap = { git = "https://github.com/rtfeldman/clap", branch = "master" } const_format = "0.2.8" @@ -62,24 +67,7 @@ inlinable_string = "0.1" libc = "0.2" libloading = "0.6" -# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. -# -# The reason for this fork is that the way Inkwell is designed, you have to use -# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that -# breaking changes get pushed directly to that branch, which breaks our build -# without warning. -# -# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch), -# but although that worked locally, it did not work on GitHub Actions. (After a few -# hours of investigation, gave up trying to figure out why.) So this is the workaround: -# having an immutable tag on the rtfeldman/inkwell fork which points to -# a particular "release" of Inkwell. -# -# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest -# commit of TheDan64/inkwell, push a new tag which points to the latest commit, -# change the tag value in this Cargo.toml to point to that tag, and `cargo update`. -# This way, GitHub Actions works and nobody's builds get broken. -inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] } +inkwell = { path = "../vendor/inkwell", optional = true } target-lexicon = "0.10" tempfile = "3.1.0" @@ -89,8 +77,12 @@ maplit = "1.0.1" indoc = "0.3.3" quickcheck = "0.8" quickcheck_macros = "0.8" -strip-ansi-escapes = "0.1" -serde = { version = "1.0", features = ["derive"] } -serde-xml-rs = "0.4" serial_test = "0.5" tempfile = "3.1.0" +criterion = { git = "https://github.com/Anton-4/criterion.rs"} +cli_utils = { path = "cli_utils" } + +[[bench]] +name = "time_bench" +harness = false + diff --git a/cli/benches/README.md b/cli/benches/README.md new file mode 100644 index 0000000000..0c91904cfa --- /dev/null +++ b/cli/benches/README.md @@ -0,0 +1,17 @@ + +# Running the benchmarks + +Install cargo criterion: +``` +cargo install cargo-criterion +``` + +To prevent stack overflow on the `CFold` benchmark: +``` +ulimit -s unlimited +``` + +In the `cli` folder execute: +``` +cargo criterion +``` \ No newline at end of file diff --git a/cli/benches/time_bench.rs b/cli/benches/time_bench.rs new file mode 100644 index 0000000000..d86230a669 --- /dev/null +++ b/cli/benches/time_bench.rs @@ -0,0 +1,31 @@ +use cli_utils::bench_utils::{ + bench_cfold, bench_deriv, bench_nqueens, bench_quicksort, bench_rbtree_ck, bench_rbtree_delete, +}; +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode, +}; + +fn bench_group_wall_time(c: &mut Criterion) { + let mut group = c.benchmark_group("bench-group_wall-time"); + // calculate statistics based on a fixed(flat) 300 runs + group.sampling_mode(SamplingMode::Flat); + group.sample_size(300); + + let bench_funcs: Vec>) -> ()> = vec![ + bench_nqueens, // queens 11 + bench_cfold, // e = mkExpr 17 1 + bench_deriv, // nest deriv 8 f + bench_rbtree_ck, // ms = makeMap 5 80000 + bench_rbtree_delete, // m = makeMap 100000 + bench_quicksort, // list size 10000 + ]; + + for bench_func in bench_funcs.iter() { + bench_func(Some(&mut group)) + } + + group.finish(); +} + +criterion_group!(benches, bench_group_wall_time); +criterion_main!(benches); diff --git a/cli/cli_utils/Cargo.toml b/cli/cli_utils/Cargo.toml new file mode 100644 index 0000000000..d80d17e3c8 --- /dev/null +++ b/cli/cli_utils/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cli_utils" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +repository = "https://github.com/rtfeldman/roc" +edition = "2018" +description = "Shared code for cli tests and benchmarks" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +roc_cli = { path = "../../cli" } +roc_collections = { path = "../../compiler/collections" } +roc_load = { path = "../../compiler/load" } +roc_module = { path = "../../compiler/module" } +bumpalo = { version = "3.6.1", features = ["collections"] } +criterion = { git = "https://github.com/Anton-4/criterion.rs"} +inlinable_string = "0.1" +serde = { version = "1.0", features = ["derive"] } +serde-xml-rs = "0.4" +strip-ansi-escapes = "0.1" +tempfile = "3.1.0" diff --git a/cli/cli_utils/src/bench_utils.rs b/cli/cli_utils/src/bench_utils.rs new file mode 100644 index 0000000000..f60fc89a3e --- /dev/null +++ b/cli/cli_utils/src/bench_utils.rs @@ -0,0 +1,135 @@ +use crate::helpers::{example_file, run_cmd, run_roc}; +use criterion::{black_box, measurement::Measurement, BenchmarkGroup}; +use std::path::Path; + +fn exec_bench_w_input( + file: &Path, + stdin_str: &str, + executable_filename: &str, + expected_ending: &str, + bench_group_opt: Option<&mut BenchmarkGroup>, +) { + let flags: &[&str] = &["--optimize"]; + + let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat()); + + if !compile_out.stderr.is_empty() { + panic!("{}", compile_out.stderr); + } + + assert!( + compile_out.status.success(), + "build ended with bad status {:?}", + compile_out + ); + + check_cmd_output(file, stdin_str, executable_filename, expected_ending); + + bench_cmd(file, stdin_str, executable_filename, bench_group_opt); +} + +fn check_cmd_output( + file: &Path, + stdin_str: &str, + executable_filename: &str, + expected_ending: &str, +) { + let out = run_cmd( + file.with_file_name(executable_filename).to_str().unwrap(), + &[stdin_str], + &[], + ); + + if !&out.stdout.ends_with(expected_ending) { + panic!( + "expected output to end with {:?} but instead got {:#?}", + expected_ending, out + ); + } + assert!(out.status.success()); +} + +fn bench_cmd( + file: &Path, + stdin_str: &str, + executable_filename: &str, + bench_group_opt: Option<&mut BenchmarkGroup>, +) { + if let Some(bench_group) = bench_group_opt { + bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| { + b.iter(|| { + run_cmd( + black_box(file.with_file_name(executable_filename).to_str().unwrap()), + black_box(&[stdin_str]), + &[], + ) + }) + }); + } else { + run_cmd( + black_box(file.with_file_name(executable_filename).to_str().unwrap()), + black_box(&[stdin_str]), + &[], + ); + } +} + +pub fn bench_nqueens(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "NQueens.roc"), + "11", + "nqueens", + "2680\n", + bench_group_opt, + ); +} + +pub fn bench_cfold(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "CFold.roc"), + "17", + "cfold", + "396354 & 396354\n", + bench_group_opt, + ); +} + +pub fn bench_deriv(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "Deriv.roc"), + "8", + "deriv", + "1 count: 6\n2 count: 22\n3 count: 90\n4 count: 420\n5 count: 2202\n6 count: 12886\n7 count: 83648\n8 count: 598592\n", + bench_group_opt, + ); +} + +pub fn bench_rbtree_ck(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "RBTreeCk.roc"), + "80000", + "rbtree-ck", + "8000\n", + bench_group_opt, + ); +} + +pub fn bench_rbtree_delete(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "RBTreeDel.roc"), + "100000", + "rbtree-del", + "7000\n", + bench_group_opt, + ); +} + +pub fn bench_quicksort(bench_group_opt: Option<&mut BenchmarkGroup>) { + exec_bench_w_input( + &example_file("benchmarks", "QuicksortApp.roc"), + "1", // 1 for sorting large list, 0 for a small list + "quicksortapp", + "[ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 18, 18, 19, 19, 19, 20, 21, 21, 21, 21, 22, 23, 23, 23, 25, 26, 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 31, 32, 33, 34, 35, 35, 35, 36, 36, 36, 37, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 41, 42, 42, 42, 42, 42, 43, 43, 44, 46, 47, 47, 47, 48, 50, 51, 51, 52, 52, 52, 53, 54, 54, 55, 55, 55, 56, 57, 57, 58, 58, 58, 58, 58, 59, 59, 60, 60, 61, 62, 63, 63, 63, 63, 64, 65, 65, 65, 66, 66, 66, 66, 67, 67, 68, 69, 69, 70, 70, 71, 71, 71, 72, 72, 73, 73, 73, 74, 75, 75, 75, 76, 78, 79, 79, 80, 81, 81, 82, 82, 83, 83, 84, 84, 86, 86, 87, 87, 88, 88, 88, 89, 89, 90, 90, 90, 91, 92, 92, 92, 93, 93, 93, 94, 95, 95, 96, 97, 98, 99, 100, 100, 101, 102, 102, 102, 104, 104, 105, 106, 106, 106, 106, 106, 106, 107, 107, 108, 108, 108, 109, 109, 109, 109, 110, 112, 112, 112, 113, 113, 113, 113, 113, 114, 115, 117, 117, 117, 118, 119, 119, 119, 120, 120, 121, 123, 124, 125, 125, 126, 126, 126, 126, 127, 129, 131, 131, 131, 131, 131, 131, 131, 132, 133, 133, 134, 134, 134, 135, 135, 135, 135, 135, 137, 138, 138, 138, 139, 139, 140, 141, 142, 142, 142, 144, 144, 145, 145, 145, 147, 147, 147, 147, 148, 149, 149, 149, 150, 150, 151, 151, 151, 151, 153, 155, 156, 159, 160, 160, 160, 161, 161, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 167, 167, 167, 167, 168, 169, 170, 170, 170, 170, 172, 172, 172, 173, 173, 173, 174, 175, 176, 177, 177, 178, 178, 178, 178, 179, 179, 180, 180, 181, 181, 182, 183, 183, 185, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 188, 188, 188, 190, 190, 190, 190, 190, 192, 193, 194, 194, 194, 195, 195, 196, 197, 198, 198, 198, 199, 199, 199, 200, 200, 201, 201, 201, 204, 205, 205, 205, 207, 207, 207, 208, 208, 208, 208, 210, 210, 210, 210, 211, 211, 213, 214, 214, 214, 218, 218, 218, 218, 218, 218, 219, 221, 222, 223, 223, 223, 224, 224, 224, 224, 224, 224, 224, 225, 226, 226, 226, 226, 226, 227, 227, 228, 228, 229, 229, 229, 229, 230, 230, 230, 230, 232, 233, 233, 234, 235, 236, 236, 237, 237, 238, 240, 240, 242, 242, 243, 244, 246, 247, 247, 247, 247, 248, 248, 248, 249, 249, 249, 249, 249, 250, 250, 250, 251, 251, 252, 252, 253, 255, 255, 256, 256, 256, 257, 257, 257, 258, 258, 258, 258, 258, 259, 259, 260, 260, 260, 261, 261, 261, 262, 263, 265, 265, 266, 267, 267, 267, 268, 268, 268, 270, 270, 270, 271, 271, 273, 274, 274, 274, 275, 277, 277, 279, 279, 280, 281, 281, 282, 283, 283, 285, 286, 288, 288, 289, 289, 290, 290, 290, 290, 290, 291, 291, 291, 291, 292, 292, 292, 293, 294, 294, 295, 295, 295, 295, 295, 298, 298, 301, 301, 301, 302, 302, 303, 304, 305, 305, 306, 307, 307, 308, 308, 309, 309, 309, 309, 310, 310, 311, 311, 311, 312, 313, 313, 313, 314, 315, 316, 316, 316, 316, 317, 318, 318, 319, 319, 319, 320, 321, 321, 322, 322, 322, 322, 323, 323, 323, 324, 324, 324, 325, 326, 326, 328, 329, 329, 330, 330, 330, 331, 331, 331, 331, 332, 332, 333, 333, 333, 333, 334, 334, 334, 335, 336, 336, 337, 337, 337, 337, 339, 339, 340, 341, 341, 343, 344, 344, 345, 345, 345, 346, 346, 347, 348, 348, 348, 349, 350, 351, 351, 351, 352, 353, 354, 354, 354, 355, 356, 356, 357, 358, 358, 358, 359, 359, 360, 361, 361, 362, 362, 363, 364, 364, 365, 365, 365, 366, 366, 367, 367, 368, 368, 369, 369, 369, 370, 370, 370, 370, 370, 371, 372, 373, 373, 374, 374, 375, 375, 376, 377, 377, 378, 379, 381, 381, 383, 384, 385, 385, 385, 385, 386, 386, 387, 388, 388, 388, 389, 389, 390, 391, 391, 391, 392, 392, 393, 393, 394, 394, 394, 395, 395, 396, 396, 397, 397, 398, 399, 400, 400, 401, 401, 402, 402, 403, 404, 404, 405, 406, 406, 407, 407, 407, 408, 408, 408, 408, 408, 409, 409, 409, 411, 411, 412, 412, 413, 413, 413, 413, 413, 414, 414, 414, 415, 416, 416, 416, 416, 417, 417, 418, 418, 418, 418, 419, 420, 420, 420, 421, 421, 422, 422, 423, 423, 423, 424, 424, 424, 424, 425, 425, 425, 426, 426, 427, 427, 427, 428, 428, 429, 429, 429, 430, 430, 431, 432, 433, 433, 433, 434, 434, 434, 434, 437, 438, 438, 438, 438, 438, 439, 440, 441, 441, 442, 442, 443, 444, 444, 444, 445, 445, 445, 447, 447, 447, 448, 448, 449, 449, 450, 450, 450, 451, 452, 453, 453, 453, 453, 455, 455, 456, 456, 457, 458, 459, 459, 460, 460, 461, 461, 464, 465, 465, 465, 466, 466, 467, 467, 467, 467, 468, 469, 469, 470, 470, 471, 471, 471, 472, 473, 473, 473, 473, 474, 475, 475, 475, 476, 476, 476, 477, 477, 477, 478, 478, 479, 481, 481, 481, 482, 482, 482, 483, 483, 483, 484, 484, 485, 488, 488, 488, 488, 489, 490, 491, 491, 491, 492, 492, 493, 493, 493, 493, 493, 495, 495, 496, 496, 496, 496, 496, 496, 497, 497, 498, 498, 498, 498, 498, 499, 500, 500, 501, 501, 501, 502, 502, 502, 502, 503, 503, 503, 505, 505, 506, 507, 507, 507, 507, 508, 508, 510, 510, 510, 511, 511, 512, 512, 513, 513, 513, 513, 514, 514, 515, 516, 517, 518, 519, 519, 519, 520, 521, 521, 522, 522, 523, 523, 523, 525, 525, 526, 527, 527, 527, 528, 528, 528, 530, 531, 532, 532, 532, 532, 532, 535, 535, 537, 538, 538, 538, 540, 540, 540, 541, 541, 541, 541, 541, 542, 543, 543, 543, 543, 544, 544, 545, 545, 545, 546, 547, 547, 547, 548, 549, 549, 551, 552, 552, 553, 553, 553, 554, 554, 554, 555, 556, 557, 557, 557, 558, 558, 558, 559, 559, 559, 560, 560, 560, 561, 561, 561, 561, 562, 562, 562, 563, 563, 565, 566, 566, 567, 568, 569, 570, 570, 571, 571, 571, 571, 572, 572, 572, 574, 575, 576, 576, 577, 580, 581, 581, 582, 582, 582, 583, 583, 584, 585, 585, 585, 586, 587, 587, 588, 588, 588, 589, 591, 591, 591, 592, 592, 592, 593, 593, 593, 594, 594, 594, 594, 595, 595, 595, 596, 596, 596, 596, 596, 597, 597, 599, 599, 600, 600, 601, 601, 601, 602, 602, 603, 603, 604, 605, 605, 605, 606, 607, 608, 610, 612, 612, 613, 613, 614, 614, 615, 615, 615, 616, 616, 616, 617, 617, 619, 619, 619, 619, 620, 621, 621, 622, 624, 624, 624, 624, 625, 625, 628, 628, 628, 629, 629, 630, 630, 630, 630, 632, 633, 633, 634, 635, 638, 638, 639, 640, 641, 641, 643, 643, 644, 644, 644, 645, 645, 645, 646, 646, 646, 647, 647, 647, 647, 648, 648, 649, 650, 650, 650, 650, 650, 650, 651, 652, 652, 652, 653, 653, 653, 653, 654, 655, 655, 655, 655, 656, 657, 657, 657, 658, 658, 659, 659, 659, 659, 659, 660, 660, 661, 662, 663, 664, 665, 666, 666, 666, 667, 667, 667, 667, 667, 668, 668, 669, 670, 670, 670, 671, 672, 672, 672, 672, 672, 673, 673, 674, 674, 674, 675, 676, 676, 677, 678, 678, 679, 679, 680, 681, 681, 682, 683, 683, 684, 684, 685, 686, 686, 686, 686, 687, 687, 688, 690, 690, 691, 691, 693, 693, 694, 694, 697, 697, 698, 700, 701, 702, 702, 703, 703, 703, 704, 705, 706, 706, 707, 708, 708, 709, 709, 710, 710, 711, 712, 712, 712, 712, 712, 712, 713, 713, 714, 714, 716, 716, 716, 717, 717, 717, 718, 718, 718, 718, 719, 719, 719, 720, 720, 721, 721, 722, 723, 724, 725, 726, 726, 727, 729, 729, 729, 730, 730, 731, 731, 732, 732, 734, 734, 734, 735, 735, 736, 736, 736, 737, 737, 738, 739, 740, 740, 740, 741, 741, 742, 742, 742, 742, 744, 744, 744, 744, 745, 745, 745, 745, 746, 748, 749, 749, 749, 750, 750, 751, 751, 751, 752, 752, 753, 753, 754, 755, 756, 756, 756, 757, 757, 757, 757, 757, 761, 761, 762, 762, 762, 763, 763, 763, 763, 763, 764, 764, 764, 764, 765, 765, 766, 766, 766, 766, 767, 767, 767, 770, 770, 770, 770, 770, 771, 772, 772, 772, 773, 774, 775, 775, 775, 775, 776, 778, 778, 779, 779, 780, 780, 780, 781, 784, 784, 784, 786, 786, 786, 786, 787, 788, 789, 789, 789, 790, 791, 791, 792, 793, 793, 793, 794, 794, 795, 796, 797, 797, 798, 799, 799, 799, 800, 800, 800, 800, 801, 802, 802, 802, 802, 804, 806, 806, 806, 807, 807, 807, 807, 808, 809, 810, 810, 811, 812, 812, 812, 812, 812, 813, 813, 813, 814, 814, 814, 815, 816, 816, 817, 817, 817, 818, 818, 818, 819, 820, 820, 820, 820, 820, 821, 821, 823, 824, 824, 824, 825, 826, 826, 826, 826, 828, 828, 829, 829, 829, 829, 829, 830, 831, 831, 831, 831, 831, 832, 832, 833, 833, 833, 834, 834, 835, 835, 835, 835, 835, 836, 836, 836, 837, 839, 839, 839, 839, 839, 840, 840, 840, 841, 841, 842, 843, 844, 844, 844, 845, 845, 845, 845, 845, 846, 846, 846, 847, 847, 848, 848, 848, 849, 849, 850, 850, 851, 852, 852, 852, 852, 853, 855, 856, 857, 857, 858, 858, 858, 859, 860, 861, 861, 861, 861, 862, 863, 863, 863, 865, 865, 865, 866, 867, 867, 867, 868, 868, 870, 871, 872, 872, 873, 873, 873, 874, 874, 874, 875, 875, 875, 876, 877, 878, 878, 878, 878, 878, 879, 879, 879, 879, 880, 881, 881, 881, 882, 883, 885, 886, 886, 887, 887, 888, 888, 889, 889, 890, 890, 890, 892, 892, 892, 892, 893, 893, 894, 894, 894, 895, 896, 896, 896, 897, 899, 899, 900, 901, 901, 901, 901, 905, 905, 905, 905, 906, 907, 907, 907, 908, 908, 908, 908, 908, 908, 909, 909, 910, 910, 910, 912, 913, 913, 914, 914, 914, 915, 916, 916, 916, 916, 917, 917, 918, 919, 919, 919, 920, 920, 920, 920, 921, 921, 922, 923, 923, 923, 923, 923, 924, 925, 927, 927, 927, 928, 928, 929, 929, 929, 929, 930, 930, 931, 932, 932, 932, 933, 933, 934, 934, 935, 935, 936, 937, 937, 937, 939, 940, 940, 941, 941, 941, 941, 942, 942, 943, 943, 945, 946, 946, 946, 948, 949, 949, 951, 953, 953, 954, 954, 954, 954, 954, 955, 956, 956, 956, 957, 957, 957, 957, 959, 960, 960, 961, 961, 963, 963, 963, 964, 964, 964, 964, 965, 966, 967, 968, 969, 969, 970, 972, 972, 973, 973, 974, 975, 975, 975, 976, 977, 978, 978, 979, 979, 980, 980, 980, 980, 981, 982, 982, 984, 986, 986, 986, 986, 986, 987, 988, 988, 990, 990, 990, 990, 990, 991, 991, 991, 991, 991, 991, 992, 992, 992, 992, 992, 993, 993, 993, 993, 995, 996, 996, 996, 997, 997, 997, 997, 997, 998, 998, 998, 999, 999, 1000, 1001, 1001, 1002, 1003, 1003, 1004, 1004, 1004, 1006, 1007, 1007, 1007, 1008, 1008, 1008, 1009, 1010, 1010, 1011, 1011, 1012, 1012, 1012, 1013, 1013, 1013, 1014, 1014, 1014, 1016, 1016, 1016, 1017, 1017, 1017, 1018, 1018, 1018, 1019, 1019, 1020, 1020, 1021, 1021, 1021, 1022, 1023, 1023, 1023, 1024, 1024, 1024, 1025, 1026, 1026, 1027, 1028, 1028, 1028, 1028, 1029, 1029, 1029, 1030, 1031, 1031, 1032, 1033, 1034, 1034, 1035, 1035, 1036, 1038, 1039, 1039, 1040, 1040, 1040, 1040, 1040, 1040, 1042, 1042, 1043, 1043, 1043, 1043, 1044, 1045, 1045, 1045, 1045, 1047, 1047, 1048, 1048, 1049, 1049, 1050, 1050, 1051, 1051, 1053, 1053, 1053, 1054, 1054, 1055, 1055, 1056, 1056, 1057, 1057, 1058, 1058, 1058, 1058, 1059, 1059, 1059, 1061, 1061, 1061, 1061, 1062, 1062, 1062, 1063, 1063, 1063, 1063, 1064, 1064, 1064, 1064, 1064, 1065, 1065, 1066, 1066, 1067, 1067, 1069, 1069, 1069, 1070, 1071, 1071, 1072, 1072, 1072, 1073, 1073, 1074, 1074, 1074, 1075, 1076, 1077, 1077, 1078, 1078, 1078, 1079, 1079, 1079, 1081, 1082, 1082, 1083, 1084, 1084, 1084, 1084, 1085, 1085, 1086, 1086, 1087, 1087, 1088, 1088, 1089, 1089, 1090, 1090, 1090, 1091, 1093, 1093, 1093, 1094, 1094, 1094, 1094, 1095, 1095, 1095, 1095, 1095, 1095, 1096, 1097, 1098, 1098, 1098, 1098, 1100, 1102, 1102, 1103, 1103, 1103, 1104, 1104, 1105, 1105, 1105, 1105, 1106, 1106, 1106, 1106, 1107, 1107, 1107, 1108, 1110, 1111, 1111, 1112, 1113, 1113, 1113, 1113, 1115, 1115, 1115, 1115, 1115, 1116, 1116, 1117, 1117, 1119, 1119, 1119, 1121, 1122, 1122, 1122, 1122, 1123, 1124, 1124, 1125, 1125, 1127, 1127, 1127, 1128, 1129, 1129, 1129, 1130, 1130, 1131, 1131, 1132, 1132, 1132, 1132, 1134, 1135, 1137, 1137, 1138, 1138, 1138, 1138, 1139, 1140, 1140, 1140, 1140, 1142, 1142, 1142, 1142, 1142, 1142, 1143, 1143, 1145, 1145, 1148, 1148, 1150, 1150, 1151, 1151, 1151, 1152, 1152, 1152, 1153, 1153, 1154, 1155, 1156, 1156, 1156, 1156, 1157, 1158, 1158, 1158, 1159, 1159, 1159, 1160, 1160, 1161, 1161, 1161, 1162, 1162, 1163, 1163, 1163, 1164, 1164, 1165, 1165, 1167, 1167, 1167, 1168, 1168, 1168, 1169, 1170, 1170, 1171, 1171, 1171, 1172, 1172, 1172, 1173, 1173, 1173, 1174, 1174, 1174, 1174, 1176, 1176, 1176, 1176, 1176, 1177, 1178, 1178, 1178, 1179, 1179, 1179, 1180, 1180, 1181, 1181, 1182, 1182, 1182, 1183, 1183, 1184, 1184, 1184, 1184, 1184, 1185, 1186, 1186, 1188, 1188, 1189, 1189, 1190, 1190, 1191, 1191, 1191, 1192, 1192, 1193, 1193, 1195, 1197, 1197, 1198, 1198, 1198, 1199, 1199, 1199, 1200, 1201, 1201, 1201, 1202, 1202, 1202, 1202, 1204, 1204, 1205, 1205, 1205, 1205, 1205, 1206, 1206, 1206, 1207, 1207, 1207, 1207, 1207, 1207, 1209, 1210, 1210, 1211, 1212, 1213, 1213, 1214, 1214, 1215, 1215, 1216, 1216, 1217, 1217, 1217, 1219, 1219, 1219, 1219, 1220, 1220, 1222, 1222, 1223, 1224, 1224, 1225, 1225, 1226, 1226, 1226, 1227, 1227, 1227, 1227, 1227, 1227, 1228, 1228, 1228, 1229, 1230, 1230, 1232, 1232, 1232, 1232, 1232, 1232, 1233, 1234, 1235, 1235, 1235, 1236, 1237, 1238, 1239, 1240, 1240, 1240, 1240, 1240, 1240, 1241, 1241, 1242, 1243, 1243, 1243, 1243, 1244, 1244, 1246, 1246, 1247, 1247, 1249, 1250, 1251, 1251, 1252, 1252, 1252, 1252, 1252, 1252, 1253, 1253, 1253, 1253, 1254, 1254, 1255, 1256, 1257, 1257, 1257, 1259, 1259, 1261, 1261, 1262, 1263, 1263, 1264, 1265, 1265, 1265, 1266, 1266, 1268, 1268, 1269, 1270, 1270, 1270, 1270, 1271, 1271, 1271, 1271, 1272, 1272, 1273, 1273, 1274, 1274, 1274, 1274, 1275, 1275, 1275, 1275, 1276, 1276, 1276, 1276, 1276, 1277, 1278, 1279, 1279, 1280, 1280, 1281, 1282, 1282, 1283, 1283, 1284, 1284, 1284, 1286, 1286, 1289, 1290, 1290, 1290, 1291, 1292, 1292, 1293, 1293, 1294, 1296, 1296, 1296, 1296, 1297, 1297, 1297, 1298, 1299, 1300, 1300, 1301, 1302, 1303, 1304, 1304, 1305, 1305, 1306, 1306, 1307, 1307, 1307, 1307, 1307, 1308, 1308, 1308, 1308, 1309, 1309, 1310, 1311, 1312, 1312, 1313, 1313, 1313, 1314, 1315, 1316, 1316, 1316, 1317, 1319, 1320, 1320, 1320, 1320, 1321, 1322, 1322, 1323, 1323, 1323, 1324, 1324, 1325, 1327, 1328, 1329, 1329, 1330, 1330, 1330, 1330, 1332, 1332, 1332, 1333, 1333, 1334, 1335, 1335, 1336, 1336, 1336, 1338, 1338, 1338, 1339, 1339, 1340, 1340, 1340, 1341, 1341, 1341, 1342, 1343, 1343, 1345, 1345, 1345, 1346, 1346, 1346, 1346, 1346, 1346, 1347, 1348, 1349, 1349, 1349, 1349, 1351, 1352, 1353, 1353, 1353, 1354, 1354, 1355, 1355, 1356, 1356, 1356, 1356, 1358, 1358, 1359, 1359, 1359, 1359, 1359, 1360, 1360, 1360, 1361, 1361, 1361, 1362, 1362, 1363, 1363, 1363, 1365, 1365, 1366, 1367, 1367, 1370, 1371, 1371, 1372, 1372, 1373, 1373, 1373, 1374, 1375, 1375, 1375, 1377, 1377, 1378, 1378, 1378, 1380, 1380, 1381, 1381, 1381, 1382, 1382, 1382, 1382, 1382, 1382, 1383, 1383, 1383, 1384, 1384, 1384, 1385, 1385, 1385, 1385, 1386, 1386, 1387, 1387, 1388, 1388, 1388, 1389, 1389, 1389, 1392, 1393, 1393, 1394, 1394, 1395, 1395, 1395, 1396, 1397, 1398, 1398, 1398, 1399, 1399, 1399, 1400, 1401, 1402, 1402, 1402, 1403, 1404, 1405, 1406, 1406, 1406, 1406, 1407, 1407, 1407, 1407, 1409, 1409, 1409, 1410, 1410, 1410, 1410, 1410, 1411, 1411, 1412, 1413, 1413, 1413, 1414, 1414, 1415, 1415, 1415, 1416, 1416, 1416, 1417, 1417, 1417, 1417, 1417, 1419, 1420, 1420, 1420, 1421, 1422, 1422, 1422, 1422, 1425, 1426, 1427, 1427, 1428, 1428, 1430, 1431, 1431, 1432, 1432, 1432, 1433, 1433, 1434, 1434, 1434, 1434, 1434, 1435, 1436, 1436, 1436, 1436, 1436, 1437, 1438, 1438, 1438, 1438, 1439, 1439, 1440, 1440, 1440, 1440, 1441, 1441, 1442, 1443, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1445, 1446, 1446, 1446, 1447, 1448, 1449, 1449, 1450, 1450, 1450, 1451, 1451, 1452, 1452, 1452, 1453, 1454, 1455, 1456, 1458, 1459, 1459, 1459, 1459, 1460, 1460, 1461, 1461, 1461, 1462, 1462, 1462, 1462, 1462, 1462, 1463, 1463, 1465, 1465, 1465, 1466, 1467, 1468, 1469, 1470, 1472, 1472, 1473, 1474, 1474, 1474, 1474, 1475, 1476, 1477, 1477, 1477, 1477, 1478, 1478, 1480, 1481, 1481, 1481, 1481, 1481, 1481, 1482, 1482, 1482, 1483, 1484, 1485, 1485, 1486, 1486, 1486, 1488, 1488, 1489, 1489, 1489, 1491, 1491, 1492, 1492, 1493, 1495, 1495, 1495, 1496, 1496, 1497, 1497, 1497, 1497, 1497, 1498, 1498, 1499, 1500, 1500, 1501, 1501, 1501, 1501, 1502, 1503, 1503, 1503, 1503, 1503, 1503, 1504, 1505, 1505, 1505, 1506, 1506, 1506, 1506, 1509, 1509, 1509, 1510, 1510, 1511, 1511, 1511, 1511, 1512, 1513, 1513, 1513, 1514, 1514, 1515, 1516, 1516, 1517, 1517, 1518, 1518, 1519, 1519, 1520, 1521, 1522, 1522, 1524, 1525, 1525, 1525, 1525, 1526, 1526, 1526, 1526, 1526, 1526, 1528, 1528, 1528, 1529, 1532, 1532, 1532, 1534, 1534, 1535, 1536, 1536, 1536, 1537, 1537, 1538, 1538, 1538, 1539, 1539, 1539, 1539, 1540, 1541, 1542, 1542, 1543, 1544, 1544, 1544, 1545, 1545, 1545, 1546, 1547, 1547, 1547, 1547, 1547, 1548, 1550, 1551, 1551, 1551, 1551, 1552, 1552, 1552, 1552, 1553, 1554, 1554, 1554, 1555, 1555, 1555, 1555, 1556, 1556, 1557, 1558, 1559, 1559, 1559, 1560, 1560, 1560, 1560, 1561, 1561, 1562, 1562, 1563, 1564, 1564, 1565, 1565, 1565, 1566, 1567, 1567, 1568, 1568, 1569, 1569, 1570, 1570, 1570, 1571, 1571, 1571, 1571, 1572, 1572, 1572, 1573, 1573, 1573, 1573, 1574, 1574, 1575, 1575, 1575, 1575, 1575, 1576, 1576, 1576, 1576, 1576, 1578, 1578, 1578, 1579, 1579, 1579, 1580, 1581, 1581, 1581, 1581, 1581, 1582, 1582, 1582, 1582, 1583, 1583, 1586, 1586, 1586, 1586, 1586, 1587, 1588, 1589, 1590, 1591, 1591, 1591, 1594, 1595, 1595, 1595, 1596, 1598, 1598, 1599, 1600, 1600, 1601, 1601, 1601, 1602, 1602, 1602, 1603, 1603, 1605, 1605, 1606, 1607, 1608, 1608, 1608, 1609, 1609, 1609, 1609, 1611, 1611, 1612, 1612, 1612, 1612, 1612, 1612, 1614, 1615, 1615, 1615, 1615, 1616, 1618, 1618, 1619, 1620, 1621, 1621, 1621, 1622, 1623, 1623, 1624, 1624, 1624, 1624, 1625, 1625, 1625, 1626, 1626, 1627, 1627, 1627, 1629, 1629, 1630, 1630, 1631, 1631, 1634, 1634, 1634, 1634, 1634, 1634, 1635, 1636, 1639, 1639, 1640, 1641, 1641, 1641, 1642, 1642, 1643, 1645, 1645, 1645, 1646, 1647, 1647, 1647, 1648, 1649, 1649, 1649, 1649, 1649, 1651, 1652, 1653, 1653, 1655, 1655, 1655, 1655, 1655, 1655, 1657, 1657, 1657, 1658, 1658, 1659, 1659, 1659, 1659, 1660, 1660, 1660, 1660, 1662, 1663, 1663, 1664, 1664, 1666, 1666, 1666, 1666, 1668, 1669, 1669, 1669, 1671, 1671, 1672, 1672, 1673, 1673, 1673, 1673, 1674, 1674, 1675, 1675, 1675, 1677, 1677, 1677, 1677, 1678, 1678, 1678, 1679, 1679, 1679, 1679, 1680, 1680, 1680, 1681, 1681, 1681, 1682, 1682, 1682, 1683, 1683, 1683, 1684, 1684, 1684, 1685, 1685, 1686, 1687, 1688, 1688, 1688, 1689, 1689, 1691, 1691, 1691, 1692, 1693, 1693, 1693, 1696, 1697, 1697, 1698, 1699, 1700, 1700, 1701, 1702, 1703, 1703, 1705, 1705, 1705, 1707, 1708, 1708, 1708, 1709, 1711, 1712, 1712, 1712, 1714, 1714, 1714, 1714, 1715, 1716, 1716, 1717, 1718, 1718, 1719, 1719, 1719, 1720, 1720, 1720, 1721, 1721, 1722, 1722, 1722, 1722, 1722, 1723, 1723, 1724, 1724, 1725, 1726, 1726, 1727, 1727, 1728, 1728, 1730, 1731, 1731, 1734, 1735, 1735, 1735, 1736, 1737, 1737, 1738, 1738, 1738, 1739, 1739, 1739, 1739, 1739, 1740, 1740, 1740, 1740, 1740, 1741, 1741, 1741, 1741, 1741, 1742, 1743, 1744, 1744, 1744, 1745, 1746, 1746, 1747, 1748, 1749, 1749, 1749, 1749, 1751, 1751, 1751, 1752, 1752, 1752, 1752, 1753, 1754, 1755, 1755, 1755, 1756, 1756, 1757, 1757, 1757, 1757, 1758, 1759, 1759, 1759, 1760, 1760, 1762, 1764, 1766, 1766, 1767, 1767, 1768, 1769, 1769, 1770, 1770, 1770, 1771, 1772, 1773, 1774, 1775, 1775, 1775, 1776, 1776, 1776, 1777, 1777, 1778, 1778, 1779, 1779, 1780, 1780, 1781, 1782, 1784, 1784, 1784, 1785, 1785, 1785, 1785, 1787, 1788, 1789, 1789, 1789, 1790, 1790, 1790, 1791, 1791, 1791, 1791, 1791, 1792, 1792, 1793, 1793, 1793, 1793, 1794, 1794, 1795, 1795, 1796, 1796, 1797, 1797, 1798, 1798, 1798, 1799, 1799, 1800, 1800, 1800, 1801, 1801, 1802, 1802, 1804, 1804, 1804, 1806, 1806, 1808, 1809, 1810, 1810, 1811, 1811, 1814, 1814, 1814, 1815, 1815, 1816, 1816, 1816, 1816, 1817, 1817, 1818, 1819, 1819, 1819, 1820, 1820, 1820, 1821, 1821, 1822, 1823, 1823, 1824, 1824, 1824, 1825, 1825, 1825, 1826, 1826, 1826, 1827, 1827, 1827, 1828, 1828, 1830, 1831, 1832, 1832, 1832, 1832, 1833, 1833, 1833, 1833, 1835, 1837, 1838, 1839, 1840, 1840, 1840, 1840, 1840, 1840, 1841, 1842, 1842, 1843, 1843, 1844, 1844, 1844, 1844, 1844, 1845, 1846, 1847, 1847, 1847, 1848, 1849, 1849, 1849, 1850, 1850, 1850, 1851, 1851, 1851, 1852, 1852, 1853, 1853, 1853, 1854, 1854, 1855, 1855, 1855, 1855, 1855, 1855, 1856, 1856, 1856, 1856, 1857, 1857, 1857, 1857, 1858, 1859, 1859, 1860, 1860, 1860, 1860, 1861, 1861, 1863, 1863, 1865, 1865, 1866, 1866, 1866, 1866, 1866, 1867, 1867, 1867, 1867, 1867, 1868, 1869, 1869, 1869, 1869, 1869, 1869, 1870, 1870, 1870, 1870, 1871, 1872, 1873, 1874, 1875, 1875, 1876, 1876, 1876, 1876, 1877, 1877, 1878, 1878, 1878, 1879, 1879, 1880, 1880, 1880, 1881, 1881, 1883, 1883, 1885, 1885, 1885, 1885, 1885, 1885, 1886, 1886, 1886, 1887, 1887, 1887, 1887, 1888, 1888, 1890, 1891, 1891, 1891, 1892, 1894, 1894, 1894, 1894, 1896, 1896, 1896, 1896, 1897, 1899, 1899, 1900, 1900, 1901, 1901, 1902, 1903, 1904, 1905, 1905, 1905, 1906, 1906, 1906, 1907, 1907, 1908, 1908, 1909, 1910, 1910, 1911, 1912, 1912, 1912, 1913, 1914, 1914, 1914, 1915, 1915, 1915, 1916, 1916, 1916, 1917, 1918, 1918, 1919, 1919, 1920, 1920, 1920, 1920, 1921, 1921, 1922, 1923, 1925, 1925, 1925, 1925, 1926, 1928, 1929, 1929, 1930, 1930, 1931, 1931, 1931, 1931, 1932, 1932, 1932, 1932, 1932, 1933, 1933, 1933, 1933, 1934, 1934, 1934, 1934, 1934, 1935, 1935, 1935, 1936, 1937, 1938, 1938, 1938, 1938, 1940, 1941, 1941, 1941, 1942, 1942, 1943, 1943, 1944, 1944, 1944, 1944, 1945, 1946, 1946, 1947, 1948, 1948, 1948, 1949, 1949, 1949, 1949, 1949, 1950, 1950, 1950, 1951, 1951, 1951, 1951, 1951, 1952, 1953, 1955, 1955, 1956, 1956, 1956, 1957, 1957, 1957, 1958, 1958, 1960, 1960, 1960, 1960, 1961, 1963, 1965, 1965, 1965, 1967, 1967, 1968, 1968, 1969, 1969, 1969, 1969, 1970, 1970, 1971, 1971, 1971, 1972, 1972, 1973, 1973, 1973, 1973, 1973, 1974, 1974, 1975, 1975, 1976, 1976, 1976, 1976, 1977, 1978, 1978, 1979, 1979, 1979, 1980, 1980, 1981, 1981, 1982, 1982, 1982, 1983, 1983, 1983, 1984, 1984, 1986, 1986, 1987, 1989, 1989, 1989, 1989, 1989, 1990, 1990, 1990, 1991, 1991, 1991, 1991, 1992, 1992, 1994, 1994, 1994, 1995, 1995, 1995, 1995, 1995, 1996, 1996, 1996, 1997, 1997, 1998, 1998, 1998, 1998, 1999, 1999, 2000, 2000, 2002, 2003, 2003, 2005, 2009, 2010, 2010, 2011, 2012, 2013, 2014, 2014, 2015, 2016, 2016, 2016, 2016, 2016, 2017, 2018, 2019, 2020, 2020, 2021, 2021, 2021, 2021, 2024, 2026, 2027, 2027, 2028, 2028, 2029, 2029, 2030, 2031, 2032, 2032, 2033, 2034, 2035, 2035, 2036, 2036, 2036, 2036, 2036, 2037, 2037, 2037, 2037, 2038, 2038, 2039, 2039, 2039, 2040, 2041, 2041, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2043, 2044, 2044, 2045, 2045, 2045, 2046, 2047, 2047, 2048, 2048, 2049, 2051, 2051, 2052, 2052, 2054, 2054, 2054, 2054, 2055, 2056, 2056, 2057, 2058, 2058, 2059, 2059, 2062, 2063, 2063, 2063, 2063, 2063, 2063, 2064, 2064, 2065, 2065, 2065, 2065, 2066, 2066, 2067, 2067, 2068, 2068, 2068, 2068, 2068, 2069, 2070, 2070, 2071, 2071, 2071, 2072, 2073, 2073, 2073, 2075, 2075, 2075, 2076, 2077, 2077, 2078, 2078, 2079, 2079, 2079, 2079, 2080, 2080, 2080, 2081, 2082, 2082, 2082, 2082, 2083, 2083, 2083, 2084, 2084, 2084, 2085, 2085, 2086, 2086, 2086, 2087, 2087, 2087, 2088, 2088, 2088, 2088, 2088, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2090, 2091, 2091, 2091, 2091, 2092, 2093, 2093, 2094, 2094, 2095, 2096, 2096, 2097, 2097, 2097, 2097, 2098, 2098, 2098, 2098, 2099, 2100, 2102, 2102, 2102, 2102, 2102, 2104, 2104, 2104, 2105, 2105, 2106, 2106, 2107, 2108, 2109, 2109, 2110, 2110, 2111, 2111, 2112, 2114, 2115, 2115, 2116, 2117, 2117, 2118, 2119, 2119, 2119, 2120, 2121, 2121, 2121, 2122, 2122, 2122, 2123, 2124, 2124, 2125, 2125, 2125, 2125, 2127, 2127, 2127, 2127, 2128, 2128, 2128, 2128, 2128, 2129, 2129, 2130, 2131, 2131, 2131, 2132, 2132, 2132, 2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133, 2134, 2135, 2136, 2137, 2137, 2137, 2138, 2138, 2139, 2140, 2140, 2140, 2140, 2142, 2143, 2144, 2144, 2145, 2145, 2145, 2145, 2146, 2146, 2146, 2147, 2147, 2147, 2147, 2147, 2148, 2148, 2148, 2148, 2149, 2149, 2149, 2150, 2151, 2151, 2153, 2153, 2153, 2153, 2154, 2154, 2154, 2155, 2155, 2156, 2157, 2157, 2157, 2157, 2158, 2158, 2158, 2158, 2158, 2159, 2159, 2160, 2160, 2160, 2160, 2161, 2162, 2162, 2162, 2162, 2162, 2163, 2164, 2164, 2167, 2168, 2169, 2169, 2169, 2170, 2172, 2172, 2172, 2172, 2172, 2173, 2173, 2174, 2174, 2175, 2175, 2176, 2176, 2176, 2176, 2177, 2177, 2179, 2179, 2180, 2180, 2180, 2183, 2183, 2183, 2183, 2184, 2185, 2185, 2185, 2185, 2186, 2186, 2186, 2187, 2187, 2188, 2189, 2189, 2189, 2190, 2190, 2191, 2191, 2191, 2191, 2191, 2192, 2193, 2194, 2194, 2195, 2195, 2195, 2195, 2196, 2196, 2197, 2197, 2197, 2198, 2198, 2198, 2199, 2199, 2199, 2200, 2200, 2201, 2201, 2202, 2202, 2202, 2203, 2203, 2204, 2205, 2205, 2205, 2205, 2205, 2206, 2206, 2206, 2207, 2207, 2207, 2210, 2210, 2212, 2213, 2214, 2214, 2215, 2216, 2216, 2216, 2217, 2217, 2219, 2219, 2219, 2219, 2220, 2220, 2221, 2221, 2222, 2222, 2223, 2223, 2224, 2224, 2225, 2225, 2226, 2226, 2226, 2226, 2227, 2228, 2228, 2228, 2229, 2229, 2229, 2230, 2230, 2231, 2231, 2232, 2232, 2232, 2234, 2234, 2234, 2235, 2235, 2236, 2237, 2237, 2238, 2238, 2239, 2239, 2239, 2240, 2240, 2241, 2241, 2241, 2242, 2244, 2244, 2245, 2245, 2245, 2245, 2246, 2248, 2249, 2250, 2251, 2251, 2251, 2251, 2252, 2252, 2253, 2254, 2254, 2255, 2256, 2256, 2256, 2258, 2258, 2258, 2259, 2259, 2259, 2259, 2260, 2260, 2261, 2261, 2262, 2262, 2262, 2263, 2265, 2265, 2265, 2265, 2266, 2266, 2267, 2268, 2269, 2269, 2270, 2270, 2271, 2271, 2272, 2273, 2273, 2273, 2275, 2275, 2276, 2276, 2277, 2277, 2278, 2278, 2280, 2280, 2281, 2282, 2282, 2282, 2282, 2284, 2284, 2284, 2284, 2285, 2285, 2286, 2287, 2287, 2288, 2288, 2289, 2291, 2292, 2292, 2293, 2294, 2295, 2296, 2296, 2297, 2298, 2298, 2299, 2299, 2299, 2300, 2300, 2301, 2301, 2301, 2302, 2302, 2302, 2302, 2303, 2303, 2303, 2304, 2304, 2306, 2306, 2307, 2307, 2307, 2307, 2309, 2309, 2309, 2310, 2310, 2310, 2310, 2311, 2311, 2311, 2312, 2312, 2312, 2313, 2313, 2316, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2318, 2318, 2319, 2319, 2319, 2320, 2322, 2323, 2323, 2324, 2324, 2324, 2325, 2326, 2327, 2327, 2328, 2329, 2330, 2331, 2332, 2333, 2333, 2334, 2334, 2336, 2336, 2337, 2337, 2338, 2338, 2339, 2339, 2339, 2340, 2340, 2340, 2341, 2342, 2343, 2344, 2345, 2345, 2345, 2345, 2346, 2346, 2347, 2347, 2347, 2347, 2349, 2349, 2349, 2350, 2350, 2351, 2351, 2351, 2351, 2352, 2352, 2353, 2354, 2355, 2356, 2356, 2358, 2359, 2360, 2361, 2362, 2362, 2362, 2363, 2363, 2363, 2364, 2365, 2365, 2365, 2365, 2366, 2367, 2367, 2367, 2367, 2368, 2370, 2370, 2370, 2372, 2372, 2372, 2372, 2372, 2373, 2373, 2373, 2374, 2374, 2375, 2375, 2375, 2376, 2376, 2377, 2377, 2377, 2377, 2378, 2379, 2379, 2380, 2380, 2380, 2381, 2382, 2382, 2382, 2382, 2384, 2384, 2384, 2385, 2387, 2387, 2387, 2388, 2389, 2389, 2389, 2389, 2389, 2390, 2391, 2391, 2392, 2392, 2392, 2394, 2394, 2395, 2395, 2395, 2396, 2396, 2397, 2397, 2397, 2397, 2398, 2398, 2398, 2399, 2400, 2401, 2402, 2404, 2404, 2405, 2405, 2405, 2407, 2408, 2409, 2409, 2409, 2409, 2410, 2410, 2410, 2410, 2410, 2410, 2410, 2411, 2411, 2412, 2412, 2414, 2414, 2415, 2415, 2416, 2416, 2417, 2417, 2418, 2418, 2420, 2421, 2422, 2424, 2424, 2424, 2425, 2425, 2426, 2426, 2426, 2426, 2427, 2427, 2427, 2427, 2427, 2428, 2430, 2432, 2432, 2432, 2432, 2433, 2433, 2433, 2433, 2433, 2434, 2435, 2435, 2435, 2435, 2436, 2437, 2437, 2437, 2437, 2438, 2438, 2439, 2439, 2439, 2440, 2440, 2441, 2441, 2441, 2442, 2443, 2443, 2444, 2444, 2444, 2444, 2447, 2447, 2448, 2448, 2448, 2449, 2449, 2449, 2450, 2451, 2451, 2451, 2453, 2453, 2454, 2454, 2454, 2454, 2455, 2456, 2456, 2457, 2457, 2457, 2458, 2458, 2458, 2459, 2459, 2459, 2459, 2460, 2460, 2461, 2461, 2462, 2463, 2463, 2463, 2463, 2464, 2464, 2464, 2464, 2464, 2465, 2465, 2465, 2466, 2467, 2467, 2467, 2467, 2469, 2470, 2471, 2471, 2472, 2472, 2473, 2473, 2473, 2474, 2474, 2474, 2474, 2475, 2475, 2476, 2476, 2477, 2478, 2479, 2482, 2482, 2483, 2483, 2485, 2485, 2485, 2485, 2486, 2487, 2488, 2489, 2489, 2490, 2490, 2491, 2491, 2491, 2493, 2494, 2494, 2495, 2495, 2495, 2495, 2495, 2495, 2496, 2496, 2496, 2496, 2497, 2497, 2497, 2498, 2498, 2499, 2501, 2502, 2503, 2504, 2504, 2505, 2506, 2506, 2507, 2508, 2508, 2508, 2509, 2509, 2513, 2513, 2513, 2513, 2514, 2514, 2515, 2515, 2516, 2516, 2516, 2518, 2518, 2519, 2519, 2519, 2519, 2520, 2520, 2520, 2520, 2521, 2521, 2521, 2522, 2523, 2523, 2523, 2524, 2524, 2524, 2524, 2525, 2525, 2527, 2527, 2527, 2527, 2527, 2528, 2528, 2529, 2531, 2531, 2532, 2532, 2532, 2533, 2534, 2534, 2535, 2535, 2535, 2536, 2537, 2537, 2537, 2538, 2538, 2539, 2539, 2539, 2539, 2539, 2541, 2541, 2541, 2542, 2542, 2543, 2544, 2544, 2544, 2544, 2545, 2545, 2545, 2546, 2546, 2546, 2546, 2547, 2547, 2547, 2548, 2548, 2548, 2550, 2550, 2550, 2550, 2550, 2551, 2552, 2552, 2553, 2554, 2554, 2554, 2555, 2555, 2556, 2556, 2557, 2557, 2557, 2558, 2560, 2561, 2561, 2561, 2561, 2562, 2563, 2563, 2564, 2564, 2564, 2566, 2566, 2566, 2566, 2566, 2566, 2567, 2567, 2567, 2568, 2569, 2569, 2569, 2571, 2572, 2573, 2573, 2574, 2574, 2576, 2576, 2577, 2577, 2578, 2580, 2580, 2581, 2581, 2581, 2581, 2584, 2584, 2585, 2586, 2587, 2587, 2588, 2588, 2588, 2589, 2589, 2590, 2590, 2591, 2591, 2591, 2592, 2592, 2592, 2593, 2593, 2593, 2594, 2594, 2594, 2596, 2596, 2597, 2598, 2599, 2599, 2599, 2600, 2601, 2601, 2602, 2603, 2603, 2604, 2604, 2604, 2605, 2607, 2608, 2608, 2609, 2609, 2609, 2609, 2611, 2611, 2612, 2612, 2613, 2613, 2613, 2613, 2613, 2614, 2614, 2615, 2615, 2615, 2615, 2615, 2616, 2616, 2617, 2617, 2617, 2618, 2619, 2619, 2620, 2621, 2622, 2622, 2622, 2623, 2624, 2625, 2627, 2628, 2628, 2628, 2628, 2629, 2630, 2630, 2630, 2630, 2631, 2632, 2632, 2632, 2632, 2633, 2633, 2633, 2633, 2633, 2634, 2634, 2635, 2636, 2636, 2636, 2636, 2637, 2637, 2637, 2637, 2637, 2638, 2638, 2638, 2638, 2640, 2640, 2644, 2646, 2646, 2647, 2648, 2649, 2650, 2650, 2650, 2651, 2651, 2651, 2651, 2652, 2652, 2653, 2654, 2654, 2654, 2654, 2655, 2655, 2656, 2656, 2657, 2657, 2657, 2659, 2659, 2660, 2660, 2660, 2660, 2661, 2661, 2662, 2662, 2663, 2663, 2663, 2664, 2665, 2665, 2665, 2666, 2667, 2668, 2670, 2670, 2670, 2670, 2672, 2672, 2673, 2673, 2674, 2674, 2675, 2676, 2676, 2676, 2676, 2677, 2677, 2677, 2677, 2677, 2677, 2679, 2680, 2681, 2683, 2683, 2684, 2684, 2684, 2684, 2685, 2686, 2687, 2688, 2688, 2688, 2689, 2689, 2689, 2689, 2690, 2690, 2690, 2690, 2691, 2691, 2692, 2692, 2692, 2692, 2693, 2693, 2694, 2694, 2694, 2694, 2694, 2695, 2695, 2695, 2696, 2697, 2698, 2699, 2700, 2700, 2701, 2702, 2702, 2704, 2704, 2704, 2705, 2705, 2705, 2705, 2706, 2707, 2707, 2708, 2708, 2710, 2710, 2710, 2711, 2711, 2711, 2711, 2711, 2711, 2712, 2713, 2713, 2714, 2715, 2716, 2717, 2717, 2718, 2718, 2718, 2718, 2719, 2719, 2720, 2722, 2723, 2723, 2724, 2724, 2725, 2726, 2726, 2727, 2728, 2729, 2729, 2729, 2729, 2730, 2731, 2732, 2733, 2734, 2734, 2734, 2735, 2735, 2736, 2736, 2736, 2737, 2738, 2739, 2739, 2740, 2740, 2741, 2741, 2742, 2742, 2743, 2743, 2743, 2744, 2744, 2746, 2747, 2748, 2748, 2748, 2748, 2749, 2749, 2749, 2750, 2750, 2750, 2752, 2752, 2754, 2754, 2754, 2755, 2755, 2756, 2756, 2757, 2757, 2758, 2758, 2759, 2759, 2759, 2759, 2761, 2762, 2762, 2762, 2762, 2762, 2763, 2763, 2763, 2764, 2764, 2764, 2765, 2766, 2766, 2766, 2766, 2767, 2767, 2768, 2769, 2770, 2770, 2770, 2770, 2771, 2771, 2771, 2772, 2772, 2772, 2772, 2774, 2776, 2776, 2776, 2776, 2776, 2777, 2778, 2779, 2779, 2779, 2780, 2780, 2780, 2781, 2781, 2782, 2783, 2783, 2784, 2784, 2784, 2785, 2785, 2786, 2786, 2786, 2787, 2787, 2787, 2787, 2788, 2788, 2789, 2789, 2789, 2789, 2790, 2790, 2790, 2790, 2791, 2791, 2791, 2791, 2792, 2792, 2792, 2792, 2792, 2793, 2793, 2794, 2795, 2795, 2796, 2796, 2797, 2797, 2798, 2800, 2800, 2801, 2801, 2801, 2802, 2802, 2803, 2803, 2804, 2804, 2805, 2805, 2805, 2805, 2805, 2805, 2806, 2806, 2806, 2807, 2808, 2809, 2809, 2809, 2809, 2809, 2809, 2810, 2810, 2811, 2811, 2811, 2812, 2812, 2812, 2813, 2816, 2816, 2816, 2817, 2817, 2818, 2818, 2818, 2818, 2818, 2819, 2819, 2819, 2820, 2820, 2820, 2821, 2821, 2821, 2822, 2823, 2823, 2823, 2824, 2824, 2824, 2825, 2826, 2826, 2826, 2827, 2827, 2827, 2827, 2827, 2827, 2828, 2828, 2830, 2830, 2830, 2831, 2831, 2833, 2833, 2833, 2833, 2835, 2836, 2838, 2838, 2838, 2839, 2839, 2840, 2840, 2841, 2842, 2842, 2843, 2844, 2845, 2845, 2846, 2846, 2848, 2848, 2848, 2849, 2850, 2851, 2852, 2852, 2852, 2853, 2853, 2853, 2854, 2854, 2855, 2855, 2856, 2856, 2857, 2857, 2857, 2857, 2858, 2858, 2859, 2859, 2859, 2860, 2861, 2861, 2861, 2862, 2862, 2863, 2863, 2863, 2864, 2865, 2868, 2868, 2868, 2868, 2868, 2869, 2869, 2870, 2870, 2870, 2871, 2871, 2871, 2872, 2873, 2874, 2875, 2875, 2876, 2876, 2877, 2877, 2878, 2879, 2880, 2880, 2881, 2882, 2884, 2884, 2884, 2885, 2885, 2886, 2887, 2887, 2887, 2887, 2887, 2888, 2888, 2888, 2888, 2889, 2889, 2889, 2890, 2890, 2890, 2891, 2893, 2894, 2895, 2896, 2896, 2897, 2897, 2898, 2898, 2898, 2900, 2900, 2901, 2901, 2902, 2902, 2902, 2902, 2903, 2904, 2904, 2904, 2904, 2905, 2905, 2905, 2906, 2907, 2907, 2908, 2908, 2908, 2908, 2909, 2909, 2910, 2911, 2911, 2911, 2912, 2913, 2914, 2915, 2916, 2916, 2918, 2918, 2919, 2919, 2919, 2920, 2921, 2921, 2922, 2922, 2922, 2923, 2923, 2923, 2924, 2925, 2926, 2926, 2926, 2927, 2927, 2927, 2928, 2929, 2930, 2931, 2931, 2932, 2932, 2932, 2934, 2934, 2934, 2935, 2935, 2935, 2936, 2937, 2938, 2939, 2940, 2940, 2941, 2942, 2942, 2943, 2943, 2943, 2944, 2944, 2944, 2944, 2944, 2945, 2946, 2946, 2947, 2947, 2948, 2949, 2950, 2950, 2951, 2952, 2954, 2954, 2954, 2955, 2955, 2956, 2957, 2958, 2958, 2959, 2959, 2960, 2960, 2960, 2962, 2962, 2964, 2964, 2965, 2965, 2965, 2966, 2966, 2967, 2967, 2968, 2969, 2969, 2969, 2970, 2970, 2971, 2972, 2972, 2972, 2972, 2972, 2974, 2974, 2974, 2976, 2976, 2977, 2978, 2979, 2980, 2980, 2980, 2980, 2981, 2981, 2982, 2982, 2983, 2984, 2984, 2986, 2987, 2987, 2988, 2988, 2988, 2989, 2989, 2989, 2990, 2990, 2991, 2991, 2991, 2992, 2993, 2994, 2995, 2995, 2995, 2995, 2996, 2996, 2997, 2997, 2997, 2998, 2999, 2999, 2999, 2999, 2999, 2999, 3000, 3000, 3000, 3000, 3001, 3001, 3002, 3003, 3003, 3004, 3005, 3005, 3005, 3007, 3007, 3008, 3008, 3009, 3009, 3009, 3010, 3010, 3010, 3010, 3011, 3011, 3013, 3013, 3014, 3015, 3015, 3016, 3016, 3016, 3016, 3017, 3018, 3018, 3018, 3018, 3019, 3020, 3020, 3021, 3021, 3021, 3022, 3024, 3026, 3026, 3026, 3026, 3027, 3028, 3028, 3028, 3028, 3030, 3030, 3031, 3035, 3036, 3036, 3036, 3037, 3037, 3038, 3038, 3039, 3039, 3041, 3041, 3041, 3042, 3043, 3043, 3044, 3044, 3045, 3045, 3045, 3045, 3045, 3046, 3047, 3048, 3048, 3048, 3049, 3049, 3049, 3050, 3050, 3051, 3051, 3051, 3051, 3052, 3052, 3052, 3053, 3054, 3054, 3054, 3054, 3055, 3055, 3055, 3055, 3057, 3057, 3057, 3058, 3059, 3060, 3060, 3060, 3060, 3061, 3062, 3063, 3063, 3063, 3064, 3065, 3065, 3066, 3068, 3068, 3068, 3068, 3068, 3068, 3069, 3071, 3072, 3072, 3072, 3073, 3074, 3074, 3074, 3075, 3077, 3077, 3078, 3078, 3079, 3079, 3079, 3079, 3081, 3081, 3081, 3082, 3082, 3082, 3082, 3083, 3083, 3084, 3084, 3084, 3086, 3086, 3087, 3087, 3087, 3087, 3088, 3089, 3089, 3090, 3091, 3092, 3092, 3093, 3093, 3094, 3094, 3094, 3095, 3095, 3096, 3097, 3097, 3098, 3099, 3100, 3101, 3101, 3102, 3102, 3104, 3104, 3105, 3107, 3108, 3108, 3109, 3109, 3109, 3110, 3110, 3111, 3111, 3111, 3112, 3112, 3112, 3112, 3112, 3113, 3113, 3113, 3113, 3113, 3114, 3115, 3116, 3116, 3116, 3117, 3117, 3117, 3118, 3118, 3119, 3119, 3119, 3120, 3120, 3120, 3121, 3121, 3121, 3122, 3122, 3122, 3122, 3123, 3123, 3124, 3126, 3127, 3127, 3127, 3127, 3128, 3128, 3128, 3128, 3129, 3130, 3130, 3131, 3131, 3131, 3131, 3131, 3132, 3132, 3132, 3133, 3133, 3134, 3135, 3136, 3136, 3136, 3137, 3138, 3140, 3140, 3141, 3142, 3142, 3143, 3143, 3143, 3143, 3143, 3144, 3145, 3146, 3146, 3146, 3147, 3148, 3149, 3149, 3150, 3150, 3150, 3150, 3150, 3150, 3151, 3151, 3152, 3152, 3154, 3154, 3155, 3155, 3155, 3156, 3156, 3157, 3158, 3158, 3159, 3160, 3160, 3161, 3161, 3161, 3162, 3162, 3163, 3164, 3164, 3165, 3165, 3166, 3166, 3166, 3167, 3167, 3168, 3168, 3168, 3169, 3169, 3170, 3170, 3170, 3170, 3171, 3172, 3172, 3173, 3175, 3175, 3177, 3177, 3178, 3178, 3179, 3180, 3180, 3180, 3181, 3182, 3182, 3182, 3183, 3184, 3184, 3184, 3185, 3186, 3187, 3187, 3188, 3189, 3189, 3189, 3190, 3190, 3191, 3192, 3192, 3193, 3193, 3193, 3194, 3194, 3194, 3194, 3195, 3195, 3196, 3196, 3196, 3196, 3198, 3198, 3198, 3198, 3198, 3199, 3199, 3199, 3200, 3200, 3202, 3202, 3203, 3203, 3203, 3205, 3206, 3207, 3207, 3207, 3208, 3208, 3208, 3208, 3209, 3209, 3210, 3210, 3211, 3211, 3211, 3212, 3212, 3213, 3213, 3213, 3214, 3214, 3215, 3216, 3216, 3217, 3218, 3218, 3219, 3219, 3220, 3222, 3223, 3223, 3223, 3224, 3224, 3224, 3224, 3225, 3225, 3225, 3225, 3226, 3227, 3228, 3228, 3228, 3228, 3228, 3228, 3229, 3230, 3230, 3231, 3233, 3234, 3234, 3234, 3235, 3235, 3236, 3236, 3237, 3237, 3239, 3239, 3239, 3240, 3240, 3241, 3241, 3241, 3241, 3243, 3243, 3243, 3243, 3243, 3243, 3243, 3243, 3245, 3245, 3246, 3246, 3246, 3247, 3247, 3247, 3247, 3248, 3248, 3249, 3250, 3250, 3251, 3251, 3252, 3252, 3253, 3253, 3254, 3254, 3255, 3256, 3257, 3257, 3257, 3259, 3259, 3260, 3260, 3261, 3262, 3263, 3263, 3263, 3264, 3266, 3266, 3266, 3267, 3267, 3267, 3267, 3267, 3268, 3268, 3268, 3269, 3269, 3269, 3270, 3270, 3270, 3270, 3271, 3272, 3272, 3272, 3272, 3273, 3273, 3273, 3274, 3274, 3275, 3275, 3276, 3276, 3276, 3278, 3278, 3279, 3280, 3280, 3280, 3280, 3281, 3282, 3284, 3284, 3284, 3285, 3285, 3285, 3285, 3286, 3286, 3287, 3288, 3288, 3289, 3289, 3289, 3289, 3290, 3292, 3292, 3292, 3293, 3293, 3293, 3293, 3294, 3294, 3297, 3297, 3298, 3299, 3301, 3301, 3302, 3302, 3302, 3302, 3303, 3304, 3305, 3305, 3305, 3305, 3306, 3306, 3306, 3306, 3306, 3306, 3308, 3308, 3308, 3308, 3309, 3309, 3310, 3310, 3311, 3311, 3311, 3311, 3312, 3313, 3313, 3313, 3314, 3314, 3315, 3315, 3316, 3318, 3320, 3320, 3321, 3321, 3321, 3322, 3322, 3323, 3323, 3323, 3324, 3324, 3327, 3329, 3329, 3330, 3330, 3330, 3331, 3331, 3331, 3331, 3331, 3333, 3334, 3335, 3336, 3336, 3336, 3337, 3337, 3337, 3338, 3338, 3339, 3339, 3339, 3339, 3340, 3340, 3340, 3340, 3341, 3341, 3341, 3344, 3345, 3345, 3346, 3347, 3347, 3347, 3347, 3347, 3348, 3348, 3348, 3348, 3349, 3349, 3349, 3350, 3350, 3351, 3351, 3352, 3352, 3352, 3352, 3353, 3354, 3357, 3358, 3358, 3358, 3358, 3359, 3359, 3359, 3360, 3360, 3361, 3361, 3361, 3362, 3363, 3363, 3363, 3365, 3365, 3367, 3367, 3367, 3368, 3369, 3369, 3369, 3370, 3370, 3371, 3372, 3372, 3373, 3374, 3374, 3377, 3377, 3377, 3377, 3378, 3379, 3379, 3380, 3380, 3381, 3381, 3382, 3383, 3383, 3383, 3384, 3384, 3385, 3385, 3385, 3386, 3386, 3387, 3387, 3388, 3388, 3388, 3389, 3389, 3389, 3390, 3392, 3393, 3394, 3394, 3394, 3395, 3396, 3397, 3397, 3397, 3398, 3398, 3398, 3398, 3399, 3399, 3400, 3400, 3400, 3401, 3401, 3402, 3402, 3402, 3402, 3403, 3403, 3405, 3405, 3405, 3405, 3405, 3406, 3407, 3407, 3408, 3410, 3410, 3411, 3411, 3411, 3412, 3412, 3412, 3413, 3414, 3414, 3414, 3414, 3415, 3415, 3417, 3419, 3419, 3420, 3420, 3420, 3421, 3421, 3421, 3422, 3422, 3423, 3423, 3423, 3423, 3424, 3425, 3425, 3425, 3426, 3427, 3427, 3428, 3428, 3429, 3429, 3430, 3431, 3431, 3431, 3432, 3432, 3432, 3434, 3435, 3435, 3435, 3436, 3437, 3438, 3438, 3438, 3439, 3439, 3439, 3440, 3440, 3441, 3441, 3442, 3443, 3443, 3443, 3444, 3444, 3444, 3445, 3445, 3445, 3446, 3446, 3447, 3447, 3447, 3448, 3448, 3449, 3449, 3449, 3450, 3450, 3450, 3451, 3452, 3452, 3453, 3453, 3454, 3454, 3454, 3454, 3455, 3456, 3456, 3456, 3457, 3457, 3460, 3461, 3461, 3461, 3462, 3462, 3462, 3463, 3463, 3463, 3463, 3463, 3464, 3464, 3464, 3466, 3467, 3467, 3467, 3468, 3468, 3469, 3470, 3471, 3472, 3473, 3473, 3473, 3474, 3475, 3475, 3475, 3476, 3476, 3476, 3478, 3479, 3479, 3480, 3481, 3481, 3481, 3482, 3483, 3484, 3484, 3485, 3485, 3486, 3486, 3486, 3486, 3487, 3487, 3487, 3487, 3489, 3489, 3490, 3490, 3490, 3491, 3491, 3491, 3492, 3492, 3493, 3493, 3494, 3494, 3494, 3495, 3495, 3495, 3495, 3495, 3495, 3495, 3496, 3497, 3497, 3498, 3498, 3499, 3499, 3499, 3499, 3500, 3501, 3501, 3503, 3503, 3503, 3504, 3504, 3504, 3504, 3504, 3505, 3505, 3505, 3506, 3507, 3508, 3508, 3508, 3511, 3511, 3511, 3511, 3511, 3511, 3511, 3512, 3512, 3512, 3512, 3513, 3514, 3514, 3514, 3515, 3515, 3516, 3517, 3517, 3518, 3518, 3518, 3518, 3519, 3520, 3520, 3520, 3520, 3521, 3521, 3521, 3521, 3521, 3524, 3525, 3527, 3528, 3528, 3530, 3530, 3531, 3532, 3532, 3533, 3534, 3534, 3534, 3535, 3535, 3535, 3535, 3536, 3537, 3537, 3538, 3539, 3539, 3539, 3539, 3540, 3540, 3540, 3541, 3541, 3541, 3543, 3544, 3544, 3547, 3548, 3548, 3549, 3549, 3550, 3551, 3551, 3551, 3551, 3552, 3553, 3553, 3553, 3553, 3554, 3554, 3554, 3554, 3555, 3555, 3556, 3556, 3557, 3558, 3558, 3558, 3558, 3559, 3559, 3560, 3560, 3560, 3561, 3561, 3562, 3562, 3563, 3565, 3566, 3566, 3566, 3566, 3567, 3567, 3567, 3567, 3568, 3569, 3569, 3570, 3570, 3571, 3572, 3572, 3573, 3573, 3573, 3574, 3574, 3575, 3575, 3576, 3577, 3578, 3579, 3581, 3581, 3582, 3582, 3582, 3583, 3583, 3583, 3583, 3583, 3584, 3584, 3585, 3586, 3586, 3587, 3587, 3588, 3588, 3588, 3589, 3591, 3591, 3593, 3594, 3594, 3595, 3596, 3596, 3597, 3599, 3599, 3599, 3600, 3600, 3600, 3601, 3601, 3602, 3602, 3602, 3603, 3604, 3605, 3607, 3608, 3609, 3609, 3609, 3609, 3610, 3610, 3611, 3612, 3612, 3613, 3614, 3614, 3615, 3615, 3615, 3615, 3615, 3616, 3617, 3617, 3617, 3617, 3619, 3619, 3619, 3621, 3621, 3621, 3622, 3623, 3624, 3624, 3625, 3627, 3628, 3628, 3628, 3628, 3629, 3630, 3630, 3630, 3631, 3631, 3631, 3631, 3632, 3633, 3633, 3633, 3634, 3634, 3634, 3636, 3637, 3638, 3638, 3638, 3639, 3639, 3639, 3639, 3641, 3642, 3642, 3642, 3643, 3643, 3643, 3643, 3644, 3644, 3645, 3646, 3646, 3647, 3647, 3647, 3647, 3648, 3648, 3649, 3649, 3650, 3650, 3651, 3652, 3652, 3653, 3653, 3654, 3655, 3656, 3656, 3657, 3658, 3659, 3660, 3661, 3662, 3663, 3664, 3664, 3664, 3665, 3666, 3667, 3667, 3668, 3669, 3669, 3669, 3670, 3670, 3671, 3671, 3672, 3672, 3673, 3677, 3678, 3678, 3678, 3678, 3679, 3679, 3679, 3681, 3681, 3681, 3682, 3682, 3683, 3683, 3684, 3684, 3685, 3685, 3685, 3687, 3687, 3687, 3688, 3688, 3688, 3688, 3688, 3689, 3690, 3690, 3690, 3693, 3693, 3694, 3694, 3695, 3695, 3696, 3698, 3698, 3699, 3699, 3700, 3702, 3703, 3704, 3705, 3705, 3705, 3705, 3706, 3706, 3706, 3706, 3706, 3707, 3707, 3707, 3708, 3708, 3710, 3710, 3710, 3711, 3712, 3713, 3713, 3713, 3713, 3714, 3714, 3714, 3715, 3715, 3716, 3716, 3717, 3717, 3717, 3717, 3718, 3718, 3718, 3718, 3719, 3719, 3719, 3720, 3720, 3721, 3721, 3722, 3722, 3722, 3722, 3722, 3723, 3724, 3725, 3726, 3727, 3727, 3728, 3728, 3729, 3729, 3731, 3731, 3731, 3731, 3731, 3732, 3734, 3734, 3734, 3734, 3735, 3735, 3736, 3736, 3736, 3736, 3737, 3738, 3739, 3739, 3739, 3740, 3740, 3740, 3741, 3741, 3741, 3742, 3742, 3743, 3744, 3744, 3744, 3745, 3745, 3745, 3746, 3746, 3747, 3747, 3747, 3748, 3748, 3749, 3751, 3751, 3751, 3751, 3751, 3752, 3753, 3753, 3753, 3753, 3754, 3755, 3756, 3757, 3757, 3758, 3758, 3758, 3759, 3759, 3759, 3762, 3763, 3763, 3763, 3763, 3764, 3765, 3765, 3766, 3766, 3766, 3766, 3767, 3767, 3768, 3768, 3769, 3769, 3770, 3770, 3770, 3770, 3771, 3771, 3772, 3772, 3773, 3773, 3774, 3775, 3775, 3776, 3776, 3776, 3776, 3776, 3777, 3777, 3779, 3779, 3779, 3779, 3780, 3780, 3781, 3781, 3782, 3783, 3783, 3784, 3785, 3785, 3787, 3787, 3787, 3788, 3788, 3788, 3788, 3789, 3789, 3790, 3790, 3791, 3792, 3792, 3792, 3793, 3793, 3794, 3794, 3795, 3795, 3796, 3797, 3797, 3797, 3797, 3798, 3798, 3799, 3800, 3800, 3800, 3800, 3801, 3801, 3801, 3802, 3802, 3802, 3802, 3803, 3804, 3805, 3806, 3806, 3807, 3808, 3808, 3809, 3809, 3811, 3813, 3814, 3814, 3816, 3816, 3816, 3817, 3818, 3819, 3820, 3820, 3821, 3821, 3821, 3822, 3822, 3822, 3825, 3825, 3825, 3825, 3826, 3828, 3828, 3828, 3829, 3830, 3830, 3830, 3830, 3831, 3831, 3831, 3832, 3832, 3833, 3833, 3833, 3833, 3834, 3835, 3835, 3836, 3837, 3837, 3837, 3837, 3838, 3838, 3838, 3839, 3841, 3841, 3842, 3842, 3842, 3842, 3843, 3843, 3843, 3843, 3843, 3844, 3844, 3844, 3845, 3846, 3847, 3847, 3848, 3849, 3850, 3850, 3851, 3851, 3851, 3854, 3854, 3854, 3855, 3855, 3856, 3857, 3858, 3858, 3858, 3859, 3859, 3859, 3859, 3860, 3860, 3861, 3861, 3861, 3861, 3862, 3862, 3862, 3862, 3863, 3863, 3865, 3865, 3865, 3865, 3866, 3866, 3867, 3867, 3867, 3867, 3868, 3868, 3869, 3869, 3870, 3871, 3871, 3871, 3872, 3873, 3873, 3873, 3874, 3874, 3874, 3875, 3875, 3876, 3877, 3878, 3878, 3878, 3879, 3879, 3879, 3880, 3880, 3881, 3881, 3881, 3881, 3883, 3883, 3884, 3884, 3884, 3884, 3884, 3886, 3887, 3887, 3887, 3887, 3888, 3888, 3889, 3890, 3890, 3891, 3891, 3891, 3891, 3892, 3892, 3892, 3892, 3893, 3893, 3893, 3893, 3894, 3894, 3894, 3895, 3895, 3895, 3895, 3897, 3897, 3897, 3899, 3899, 3900, 3901, 3902, 3904, 3904, 3905, 3905, 3906, 3906, 3906, 3907, 3907, 3907, 3908, 3909, 3910, 3911, 3911, 3912, 3913, 3914, 3915, 3915, 3915, 3915, 3916, 3917, 3917, 3917, 3919, 3919, 3919, 3920, 3921, 3921, 3922, 3922, 3923, 3923, 3923, 3924, 3924, 3925, 3925, 3926, 3926, 3926, 3928, 3928, 3928, 3929, 3929, 3930, 3930, 3930, 3931, 3931, 3931, 3932, 3932, 3932, 3932, 3932, 3933, 3933, 3933, 3934, 3934, 3934, 3935, 3935, 3935, 3935, 3936, 3937, 3937, 3937, 3938, 3938, 3939, 3942, 3942, 3943, 3943, 3943, 3945, 3945, 3945, 3946, 3947, 3947, 3948, 3948, 3948, 3948, 3948, 3951, 3952, 3952, 3952, 3952, 3953, 3954, 3954, 3956, 3957, 3957, 3957, 3957, 3958, 3958, 3958, 3959, 3960, 3961, 3961, 3961, 3962, 3963, 3964, 3964, 3964, 3965, 3965, 3965, 3965, 3967, 3968, 3969, 3969, 3970, 3970, 3971, 3972, 3972, 3973, 3973, 3974, 3974, 3975, 3975, 3976, 3976, 3977, 3977, 3977, 3977, 3978, 3978, 3979, 3979, 3979, 3979, 3979, 3980, 3980, 3981, 3981, 3981, 3981, 3982, 3982, 3982, 3982, 3983, 3984, 3984, 3984, 3984, 3984, 3984, 3986, 3986, 3986, 3987, 3988, 3988, 3988, 3988, 3989, 3989, 3989, 3990, 3990, 3991, 3992, 3993, 3994, 3995, 3996, 3996, 3998, 3998, 3998, 3999, 4000, 4000, 4000, 4001, 4001, 4001, 4001, 4002, 4002, 4002, 4002, 4003, 4004, 4004, 4004, 4005, 4006, 4006, 4007, 4007, 4008, 4008, 4008, 4009, 4010, 4010, 4010, 4010, 4011, 4011, 4013, 4014, 4015, 4016, 4017, 4018, 4018, 4019, 4020, 4020, 4020, 4021, 4021, 4022, 4022, 4022, 4023, 4023, 4023, 4024, 4024, 4025, 4025, 4025, 4026, 4026, 4027, 4027, 4028, 4028, 4028, 4029, 4030, 4031, 4031, 4031, 4031, 4032, 4032, 4032, 4032, 4033, 4033, 4033, 4033, 4035, 4035, 4035, 4035, 4035, 4037, 4038, 4038, 4038, 4038, 4038, 4039, 4039, 4039, 4040, 4040, 4040, 4041, 4041, 4041, 4041, 4041, 4041, 4041, 4042, 4042, 4043, 4043, 4043, 4043, 4044, 4044, 4045, 4045, 4045, 4047, 4047, 4048, 4048, 4049, 4050, 4050, 4050, 4051, 4052, 4052, 4053, 4053, 4054, 4055, 4055, 4056, 4056, 4057, 4058, 4058, 4059, 4059, 4060, 4060, 4060, 4061, 4061, 4061, 4062, 4063, 4063, 4064, 4065, 4065, 4065, 4066, 4067, 4068, 4068, 4068, 4069, 4069, 4069, 4070, 4070, 4070, 4071, 4071, 4072, 4072, 4072, 4072, 4072, 4073, 4073, 4074, 4074, 4075, 4076, 4076, 4076, 4076, 4077, 4077, 4078, 4078, 4078, 4080, 4081, 4081, 4082, 4082, 4082, 4083, 4083, 4085, 4085, 4085, 4085, 4085, 4086, 4086, 4086, 4087, 4087, 4087, 4088, 4088, 4089, 4089, 4090, 4090, 4091, 4091, 4092, 4093, 4093, 4093, 4094, 4095, 4096, 4096, 4096, 4097, 4097, 4098, 4099, 4099, 4099, 4099, 4100, 4100, 4101, 4101, 4102, 4102, 4103, 4104, 4104, 4104, 4104, 4104, 4105, 4106, 4106, 4106, 4106, 4107, 4108, 4108, 4109, 4109, 4109, 4109, 4110, 4111, 4112, 4112, 4112, 4112, 4112, 4113, 4113, 4114, 4114, 4114, 4115, 4115, 4116, 4116, 4117, 4117, 4117, 4118, 4118, 4118, 4119, 4119, 4121, 4121, 4122, 4122, 4122, 4123, 4124, 4125, 4125, 4126, 4127, 4129, 4129, 4130, 4131, 4131, 4132, 4132, 4132, 4134, 4134, 4134, 4135, 4135, 4135, 4135, 4135, 4135, 4135, 4135, 4136, 4136, 4136, 4136, 4136, 4136, 4137, 4137, 4137, 4139, 4140, 4140, 4140, 4141, 4141, 4142, 4142, 4142, 4143, 4144, 4144, 4144, 4144, 4145, 4145, 4145, 4145, 4146, 4147, 4147, 4147, 4148, 4148, 4148, 4149, 4149, 4149, 4149, 4149, 4150, 4150, 4151, 4153, 4153, 4154, 4155, 4155, 4156, 4156, 4156, 4157, 4158, 4158, 4159, 4159, 4160, 4160, 4161, 4161, 4161, 4161, 4163, 4163, 4163, 4164, 4164, 4164, 4164, 4165, 4165, 4165, 4166, 4166, 4166, 4167, 4168, 4169, 4170, 4170, 4170, 4171, 4171, 4172, 4173, 4173, 4173, 4173, 4174, 4175, 4175, 4176, 4176, 4176, 4177, 4177, 4177, 4177, 4178, 4178, 4179, 4179, 4179, 4179, 4179, 4179, 4179, 4180, 4180, 4180, 4181, 4181, 4181, 4181, 4182, 4182, 4183, 4184, 4185, 4186, 4187, 4187, 4187, 4188, 4188, 4188, 4189, 4189, 4189, 4189, 4190, 4190, 4190, 4190, 4190, 4191, 4192, 4192, 4192, 4192, 4194, 4195, 4196, 4196, 4196, 4196, 4197, 4197, 4198, 4198, 4198, 4198, 4200, 4200, 4201, 4202, 4202, 4203, 4203, 4204, 4205, 4205, 4207, 4210, 4210, 4210, 4210, 4211, 4211, 4213, 4214, 4214, 4215, 4215, 4215, 4216, 4216, 4216, 4217, 4217, 4218, 4218, 4218, 4218, 4218, 4219, 4220, 4220, 4220, 4221, 4222, 4223, 4223, 4223, 4225, 4225, 4226, 4226, 4227, 4227, 4228, 4228, 4228, 4228, 4229, 4230, 4230, 4232, 4233, 4233, 4233, 4233, 4234, 4235, 4235, 4235, 4235, 4236, 4236, 4236, 4237, 4238, 4238, 4238, 4238, 4238, 4239, 4239, 4239, 4239, 4239, 4240, 4240, 4241, 4243, 4243, 4243, 4243, 4243, 4243, 4244, 4244, 4244, 4244, 4244, 4245, 4245, 4245, 4247, 4247, 4247, 4248, 4248, 4249, 4249, 4249, 4250, 4250, 4250, 4251, 4251, 4252, 4252, 4253, 4253, 4253, 4253, 4253, 4254, 4255, 4255, 4255, 4256, 4256, 4257, 4257, 4257, 4258, 4258, 4258, 4258, 4259, 4259, 4260, 4260, 4261, 4261, 4261, 4261, 4261, 4261, 4262, 4262, 4262, 4263, 4264, 4265, 4265, 4265, 4266, 4267, 4268, 4269, 4269, 4269, 4270, 4270, 4271, 4271, 4271, 4272, 4272, 4273, 4273, 4273, 4273, 4274, 4274, 4274, 4275, 4275, 4277, 4279, 4279, 4280, 4280, 4280, 4281, 4281, 4282, 4282, 4282, 4282, 4282, 4283, 4283, 4283, 4283, 4284, 4285, 4285, 4285, 4285, 4285, 4286, 4286, 4287, 4287, 4287, 4287, 4288, 4289, 4290, 4290, 4291, 4291, 4291, 4292, 4293, 4293, 4293, 4293, 4294, 4294, 4295, 4296, 4297, 4297, 4297, 4297, 4297, 4297, 4298, 4299, 4299, 4299, 4299, 4300, 4300, 4301, 4301, 4302, 4302, 4302, 4302, 4303, 4304, 4305, 4306, 4306, 4306, 4307, 4307, 4308, 4308, 4309, 4309, 4309, 4309, 4310, 4311, 4311, 4312, 4312, 4312, 4312, 4312, 4313, 4314, 4315, 4315, 4316, 4316, 4317, 4317, 4317, 4318, 4318, 4319, 4319, 4320, 4320, 4321, 4322, 4322, 4323, 4325, 4327, 4327, 4327, 4327, 4327, 4328, 4328, 4330, 4330, 4330, 4331, 4331, 4332, 4332, 4334, 4335, 4335, 4336, 4336, 4337, 4337, 4338, 4338, 4338, 4338, 4339, 4339, 4339, 4340, 4340, 4340, 4340, 4340, 4340, 4341, 4341, 4341, 4343, 4343, 4344, 4344, 4344, 4345, 4346, 4347, 4347, 4348, 4348, 4348, 4352, 4353, 4355, 4356, 4356, 4357, 4357, 4358, 4358, 4358, 4358, 4359, 4360, 4360, 4360, 4361, 4361, 4361, 4361, 4362, 4362, 4363, 4363, 4364, 4365, 4365, 4365, 4368, 4370, 4370, 4370, 4371, 4371, 4371, 4372, 4372, 4372, 4372, 4374, 4374, 4374, 4375, 4375, 4376, 4376, 4377, 4377, 4380, 4380, 4381, 4381, 4381, 4382, 4382, 4384, 4386, 4386, 4387, 4387, 4389, 4389, 4389, 4389, 4389, 4389, 4390, 4391, 4391, 4392, 4393, 4393, 4394, 4394, 4394, 4394, 4395, 4395, 4396, 4398, 4398, 4398, 4399, 4400, 4400, 4400, 4401, 4401, 4402, 4403, 4403, 4403, 4404, 4405, 4405, 4405, 4407, 4408, 4408, 4408, 4409, 4410, 4410, 4410, 4410, 4411, 4411, 4412, 4412, 4413, 4413, 4414, 4414, 4414, 4414, 4414, 4415, 4415, 4419, 4419, 4419, 4419, 4420, 4420, 4420, 4421, 4421, 4421, 4421, 4423, 4424, 4425, 4426, 4427, 4427, 4428, 4429, 4429, 4430, 4430, 4430, 4431, 4431, 4431, 4431, 4432, 4432, 4432, 4432, 4432, 4432, 4433, 4433, 4434, 4434, 4435, 4435, 4435, 4435, 4436, 4436, 4436, 4436, 4437, 4437, 4438, 4438, 4438, 4438, 4438, 4439, 4439, 4440, 4440, 4441, 4441, 4442, 4443, 4444, 4444, 4446, 4446, 4447, 4447, 4447, 4448, 4448, 4448, 4449, 4450, 4451, 4452, 4453, 4453, 4454, 4454, 4455, 4455, 4455, 4456, 4456, 4456, 4457, 4457, 4457, 4457, 4457, 4457, 4457, 4457, 4459, 4460, 4460, 4461, 4462, 4462, 4462, 4462, 4465, 4465, 4466, 4467, 4468, 4468, 4469, 4470, 4470, 4471, 4471, 4471, 4471, 4471, 4471, 4471, 4471, 4472, 4472, 4472, 4473, 4473, 4474, 4474, 4474, 4475, 4475, 4476, 4477, 4479, 4479, 4479, 4479, 4481, 4481, 4481, 4481, 4482, 4482, 4482, 4483, 4483, 4484, 4484, 4486, 4487, 4487, 4487, 4487, 4487, 4488, 4488, 4488, 4489, 4491, 4491, 4492, 4492, 4493, 4493, 4494, 4494, 4494, 4494, 4494, 4495, 4495, 4495, 4496, 4496, 4496, 4497, 4498, 4499, 4500, 4500, 4501, 4501, 4503, 4503, 4503, 4504, 4504, 4506, 4509, 4509, 4509, 4509, 4510, 4510, 4511, 4511, 4511, 4512, 4513, 4513, 4514, 4514, 4514, 4515, 4517, 4518, 4521, 4521, 4521, 4521, 4522, 4523, 4523, 4524, 4524, 4525, 4525, 4525, 4525, 4525, 4526, 4526, 4527, 4527, 4528, 4528, 4528, 4529, 4529, 4529, 4529, 4530, 4531, 4532, 4533, 4533, 4534, 4535, 4536, 4536, 4536, 4536, 4537, 4537, 4538, 4539, 4539, 4542, 4542, 4542, 4543, 4543, 4543, 4544, 4544, 4546, 4547, 4547, 4548, 4548, 4549, 4549, 4550, 4550, 4551, 4552, 4552, 4552, 4553, 4553, 4554, 4554, 4554, 4554, 4554, 4555, 4555, 4556, 4556, 4557, 4557, 4558, 4558, 4559, 4559, 4559, 4560, 4560, 4562, 4563, 4563, 4564, 4565, 4566, 4566, 4566, 4567, 4567, 4567, 4567, 4567, 4568, 4568, 4568, 4569, 4569, 4570, 4571, 4572, 4572, 4572, 4572, 4573, 4574, 4574, 4574, 4575, 4575, 4575, 4575, 4575, 4575, 4576, 4576, 4577, 4577, 4578, 4578, 4578, 4579, 4579, 4579, 4579, 4580, 4580, 4580, 4580, 4580, 4581, 4581, 4582, 4583, 4584, 4584, 4586, 4586, 4587, 4588, 4589, 4590, 4590, 4592, 4592, 4592, 4593, 4594, 4594, 4594, 4595, 4595, 4595, 4596, 4597, 4597, 4597, 4598, 4598, 4600, 4600, 4600, 4600, 4601, 4601, 4602, 4602, 4602, 4603, 4604, 4604, 4605, 4605, 4605, 4606, 4607, 4608, 4608, 4608, 4609, 4609, 4609, 4610, 4611, 4611, 4612, 4612, 4614, 4614, 4614, 4614, 4615, 4615, 4616, 4616, 4616, 4616, 4617, 4617, 4617, 4617, 4618, 4618, 4618, 4618, 4620, 4621, 4621, 4621, 4622, 4623, 4623, 4623, 4624, 4624, 4625, 4625, 4626, 4626, 4627, 4627, 4627, 4629, 4629, 4630, 4630, 4631, 4631, 4631, 4631, 4631, 4631, 4632, 4633, 4634, 4634, 4634, 4635, 4635, 4635, 4635, 4636, 4636, 4636, 4636, 4637, 4637, 4638, 4639, 4639, 4640, 4640, 4640, 4641, 4641, 4642, 4643, 4643, 4643, 4644, 4644, 4645, 4646, 4646, 4647, 4648, 4649, 4649, 4649, 4649, 4651, 4651, 4653, 4654, 4655, 4655, 4656, 4656, 4657, 4658, 4658, 4658, 4659, 4659, 4659, 4659, 4659, 4660, 4661, 4662, 4662, 4663, 4663, 4664, 4664, 4665, 4665, 4666, 4666, 4666, 4667, 4667, 4668, 4669, 4669, 4669, 4669, 4670, 4670, 4670, 4671, 4673, 4673, 4674, 4674, 4674, 4674, 4675, 4675, 4675, 4676, 4677, 4678, 4678, 4679, 4679, 4679, 4679, 4680, 4680, 4681, 4681, 4683, 4683, 4683, 4683, 4684, 4684, 4685, 4685, 4686, 4686, 4687, 4687, 4688, 4690, 4690, 4690, 4690, 4691, 4691, 4693, 4693, 4693, 4693, 4693, 4695, 4695, 4697, 4697, 4698, 4699, 4699, 4700, 4700, 4700, 4701, 4701, 4701, 4702, 4703, 4703, 4704, 4704, 4704, 4705, 4705, 4705, 4706, 4707, 4707, 4707, 4708, 4708, 4709, 4709, 4710, 4710, 4710, 4711, 4711, 4712, 4712, 4714, 4715, 4716, 4716, 4717, 4718, 4718, 4718, 4718, 4719, 4719, 4720, 4720, 4720, 4720, 4721, 4721, 4721, 4722, 4722, 4725, 4725, 4726, 4726, 4727, 4728, 4728, 4728, 4728, 4728, 4729, 4729, 4730, 4730, 4731, 4731, 4732, 4732, 4733, 4733, 4733, 4733, 4733, 4734, 4734, 4734, 4734, 4735, 4735, 4735, 4736, 4737, 4738, 4738, 4738, 4738, 4738, 4739, 4739, 4740, 4740, 4741, 4741, 4743, 4743, 4743, 4744, 4744, 4744, 4744, 4744, 4746, 4746, 4746, 4746, 4747, 4747, 4747, 4748, 4748, 4748, 4749, 4749, 4749, 4750, 4751, 4751, 4751, 4752, 4753, 4753, 4753, 4755, 4755, 4756, 4756, 4757, 4757, 4757, 4758, 4758, 4760, 4760, 4760, 4761, 4761, 4762, 4762, 4762, 4763, 4764, 4764, 4765, 4766, 4767, 4767, 4768, 4768, 4769, 4770, 4771, 4771, 4771, 4773, 4774, 4774, 4774, 4774, 4775, 4778, 4779, 4780, 4780, 4780, 4781, 4781, 4782, 4782, 4782, 4783, 4785, 4785, 4786, 4787, 4787, 4787, 4787, 4788, 4788, 4788, 4788, 4788, 4789, 4790, 4790, 4790, 4791, 4791, 4791, 4792, 4792, 4792, 4792, 4792, 4792, 4793, 4794, 4794, 4796, 4796, 4796, 4796, 4797, 4798, 4798, 4799, 4799, 4799, 4799, 4801, 4801, 4802, 4802, 4802, 4803, 4805, 4805, 4808, 4808, 4808, 4810, 4810, 4810, 4811, 4811, 4811, 4811, 4812, 4812, 4813, 4814, 4815, 4815, 4816, 4816, 4816, 4816, 4816, 4817, 4817, 4817, 4818, 4818, 4818, 4819, 4819, 4820, 4822, 4822, 4822, 4822, 4822, 4822, 4823, 4823, 4823, 4824, 4824, 4825, 4826, 4826, 4827, 4827, 4828, 4828, 4828, 4829, 4829, 4830, 4830, 4830, 4831, 4831, 4831, 4832, 4832, 4833, 4834, 4834, 4834, 4834, 4835, 4835, 4835, 4836, 4837, 4838, 4838, 4838, 4838, 4838, 4838, 4839, 4839, 4839, 4839, 4840, 4840, 4841, 4842, 4842, 4842, 4843, 4843, 4843, 4843, 4843, 4844, 4844, 4845, 4846, 4846, 4847, 4847, 4847, 4847, 4847, 4848, 4848, 4849, 4849, 4849, 4849, 4849, 4850, 4850, 4851, 4853, 4853, 4853, 4854, 4854, 4856, 4856, 4857, 4857, 4857, 4858, 4858, 4859, 4859, 4859, 4859, 4860, 4860, 4861, 4862, 4862, 4863, 4863, 4863, 4863, 4864, 4864, 4864, 4864, 4865, 4865, 4866, 4866, 4867, 4867, 4869, 4870, 4870, 4870, 4870, 4870, 4870, 4871, 4871, 4871, 4872, 4873, 4873, 4874, 4874, 4875, 4875, 4876, 4876, 4876, 4876, 4877, 4879, 4879, 4879, 4881, 4882, 4882, 4883, 4883, 4883, 4884, 4884, 4886, 4888, 4888, 4888, 4889, 4890, 4890, 4890, 4891, 4891, 4892, 4892, 4892, 4892, 4893, 4893, 4893, 4894, 4894, 4894, 4894, 4894, 4894, 4895, 4898, 4899, 4899, 4900, 4901, 4901, 4901, 4901, 4902, 4902, 4903, 4904, 4904, 4904, 4904, 4904, 4905, 4906, 4908, 4908, 4909, 4910, 4910, 4911, 4911, 4912, 4912, 4913, 4913, 4914, 4914, 4914, 4915, 4915, 4916, 4917, 4917, 4918, 4918, 4920, 4921, 4921, 4921, 4921, 4922, 4922, 4922, 4922, 4923, 4923, 4924, 4924, 4924, 4925, 4926, 4926, 4926, 4927, 4928, 4928, 4928, 4928, 4928, 4928, 4929, 4930, 4930, 4931, 4932, 4934, 4934, 4935, 4935, 4936, 4936, 4937, 4937, 4937, 4937, 4937, 4938, 4939, 4939, 4939, 4939, 4939, 4939, 4942, 4943, 4943, 4944, 4944, 4944, 4944, 4944, 4945, 4946, 4946, 4946, 4947, 4948, 4948, 4948, 4949, 4949, 4950, 4950, 4950, 4950, 4951, 4951, 4952, 4956, 4956, 4957, 4957, 4957, 4957, 4958, 4960, 4960, 4960, 4961, 4961, 4961, 4962, 4962, 4962, 4963, 4963, 4964, 4964, 4964, 4965, 4966, 4967, 4967, 4968, 4968, 4968, 4971, 4971, 4972, 4972, 4974, 4975, 4975, 4975, 4976, 4976, 4977, 4978, 4978, 4979, 4979, 4979, 4980, 4980, 4980, 4981, 4981, 4981, 4981, 4982, 4982, 4982, 4982, 4982, 4983, 4983, 4983, 4983, 4984, 4985, 4985, 4986, 4986, 4986, 4986, 4987, 4987, 4988, 4989, 4989, 4989, 4990, 4991, 4992, 4992, 4993, 4993, 4994, 4995, 4995, 4996, 4997, 4997, 4997, 4997, 4997, 4998, 4998, 4999, 4999 ]\n", + bench_group_opt, + ); +} diff --git a/cli/tests/helpers.rs b/cli/cli_utils/src/helpers.rs similarity index 100% rename from cli/tests/helpers.rs rename to cli/cli_utils/src/helpers.rs diff --git a/cli/cli_utils/src/lib.rs b/cli/cli_utils/src/lib.rs new file mode 100644 index 0000000000..b5cded1f52 --- /dev/null +++ b/cli/cli_utils/src/lib.rs @@ -0,0 +1,2 @@ +pub mod bench_utils; +pub mod helpers; diff --git a/cli/src/build.rs b/cli/src/build.rs index e6f753ec11..d75bf0361b 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -5,8 +5,8 @@ use roc_build::{ }; use roc_can::builtins::builtin_defs_map; use roc_collections::all::MutMap; -use roc_gen::llvm::build::OptLevel; use roc_load::file::LoadingProblem; +use roc_mono::ir::OptLevel; use std::path::PathBuf; use std::time::{Duration, SystemTime}; use target_lexicon::Triple; @@ -26,12 +26,23 @@ pub enum BuildOutcome { Errors, } +impl BuildOutcome { + pub fn status_code(&self) -> i32 { + match self { + Self::NoProblems => 0, + Self::OnlyWarnings => 1, + Self::Errors => 2, + } + } +} + pub struct BuiltFile { pub binary_path: PathBuf, pub outcome: BuildOutcome, pub total_time: Duration, } +#[cfg(feature = "llvm")] pub fn build_file<'a>( arena: &'a Bump, target: &Triple, @@ -204,10 +215,14 @@ pub fn build_file<'a>( let total_time = compilation_start.elapsed().unwrap(); // If the cmd errored out, return the Err. - cmd_result?; + let exit_status = cmd_result?; // TODO change this to report whether there were errors or warnings! - let outcome = BuildOutcome::NoProblems; + let outcome = if exit_status.success() { + BuildOutcome::NoProblems + } else { + BuildOutcome::Errors + }; Ok(BuiltFile { binary_path, diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 91948044c2..c7efdbc1a5 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,12 +1,12 @@ #[macro_use] extern crate clap; -use build::{build_file, BuildOutcome, BuiltFile}; +use build::{BuildOutcome, BuiltFile}; use bumpalo::Bump; use clap::{App, AppSettings, Arg, ArgMatches}; use roc_build::link::LinkType; -use roc_gen::llvm::build::OptLevel; use roc_load::file::LoadingProblem; +use roc_mono::ir::OptLevel; use std::env; use std::io; use std::path::{Path, PathBuf}; @@ -25,12 +25,13 @@ pub const CMD_DOCS: &str = "docs"; pub const FLAG_DEBUG: &str = "debug"; pub const FLAG_OPTIMIZE: &str = "optimize"; +pub const FLAG_LIB: &str = "lib"; pub const ROC_FILE: &str = "ROC_FILE"; pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES"; pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP"; pub fn build_app<'a>() -> App<'a> { - App::new("roc") + let app = App::new("roc") .version(crate_version!()) .subcommand(App::new(CMD_BUILD) .about("Build a program") @@ -45,6 +46,12 @@ pub fn build_app<'a>() -> App<'a> { .help("Optimize the compiled program to run faster. (Optimization takes time to complete.)") .required(false), ) + .arg( + Arg::with_name(FLAG_LIB) + .long(FLAG_LIB) + .help("Build a C library instead of an executable.") + .required(false), + ) .arg( Arg::with_name(FLAG_DEBUG) .long(FLAG_DEBUG) @@ -81,26 +88,31 @@ pub fn build_app<'a>() -> App<'a> { .subcommand(App::new(CMD_REPL) .about("Launch the interactive Read Eval Print Loop (REPL)") ) - .subcommand(App::new(CMD_EDIT) - .about("Launch the Roc editor") - .arg(Arg::with_name(DIRECTORY_OR_FILES) - .index(1) - .multiple(true) - .required(false) - .help("(optional) The directory or files to open on launch.") - ) - ) .subcommand( App::new(CMD_DOCS) .about("Generate documentation for Roc modules") .arg(Arg::with_name(DIRECTORY_OR_FILES) .index(1) .multiple(true) - .required(true) + .required(false) .help("The directory or files to build documentation for") ) + ); + + if cfg!(feature = "editor") { + app.subcommand( + App::new(CMD_EDIT).about("Launch the Roc editor").arg( + Arg::with_name(DIRECTORY_OR_FILES) + .index(1) + .multiple(true) + .required(false) + .help("(optional) The directory or files to open on launch."), + ), ) + } else { + app + } } pub fn docs(files: Vec) { @@ -111,12 +123,15 @@ pub fn docs(files: Vec) { ) } +#[derive(Debug, PartialEq, Eq)] pub enum BuildConfig { BuildOnly, BuildAndRun { roc_file_arg_index: usize }, } +#[cfg(feature = "llvm")] pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::Result { + use build::build_file; use BuildConfig::*; let arena = Bump::new(); @@ -130,6 +145,12 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: }; let emit_debug_info = matches.is_present(FLAG_DEBUG); + let link_type = if matches.is_present(FLAG_LIB) { + LinkType::Dylib + } else { + LinkType::Executable + }; + let path = Path::new(filename).canonicalize().unwrap(); let src_dir = path.parent().unwrap().canonicalize().unwrap(); @@ -159,7 +180,7 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: path, opt_level, emit_debug_info, - LinkType::Executable, + link_type, ); match res_binary_path { @@ -175,13 +196,6 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: .strip_prefix(env::current_dir().unwrap()) .unwrap_or(&binary_path); - // Return a nonzero exit code if there were problems - let status_code = match outcome { - BuildOutcome::NoProblems => 0, - BuildOutcome::OnlyWarnings => 1, - BuildOutcome::Errors => 2, - }; - // No need to waste time freeing this memory, // since the process is about to exit anyway. std::mem::forget(arena); @@ -192,7 +206,8 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: total_time.as_millis() ); - Ok(status_code) + // Return a nonzero exit code if there were problems + Ok(outcome.status_code()) } BuildAndRun { roc_file_arg_index } => { let mut cmd = Command::new(binary_path); @@ -210,23 +225,9 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: } } - // Run the compiled app - let exit_status = cmd - .current_dir(original_cwd) - .spawn() - .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err)) - .wait() - .expect("TODO gracefully handle block_on failing when roc run spawns a subprocess for the compiled app"); - - // `roc run` exits with the same status code as the app it ran. - // - // If you want to know whether there were compilation problems - // via status code, use either `roc build` or `roc check` instead! - match exit_status.code() { - Some(code) => Ok(code), - None => { - todo!("TODO gracefully handle the roc run subprocess terminating with a signal."); - } + match outcome { + BuildOutcome::Errors => Ok(outcome.status_code()), + _ => roc_run(cmd.current_dir(original_cwd)), } } } @@ -241,3 +242,37 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io:: } } } + +#[cfg(target_family = "unix")] +fn roc_run(cmd: &mut Command) -> io::Result { + use std::os::unix::process::CommandExt; + + // This is much faster than spawning a subprocess if we're on a UNIX system! + let err = cmd.exec(); + + // If exec actually returned, it was definitely an error! (Otherwise, + // this process would have been replaced by the other one, and we'd + // never actually reach this line of code.) + Err(err) +} + +#[cfg(not(target_family = "unix"))] +fn roc_run(cmd: &mut Command) -> io::Result { + // Run the compiled app + let exit_status = cmd + .spawn() + .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err)) + .wait() + .expect("TODO gracefully handle block_on failing when roc run spawns a subprocess for the compiled app"); + + // `roc run` exits with the same status code as the app it ran. + // + // If you want to know whether there were compilation problems + // via status code, use either `roc build` or `roc check` instead! + match exit_status.code() { + Some(code) => Ok(code), + None => { + todo!("TODO gracefully handle the roc run subprocess terminating with a signal."); + } + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 191e8409b0..c4d659a5be 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,17 +1,27 @@ use roc_cli::{ - build, build_app, docs, repl, BuildConfig, CMD_BUILD, CMD_DOCS, CMD_EDIT, CMD_REPL, CMD_RUN, + build_app, docs, repl, BuildConfig, CMD_BUILD, CMD_DOCS, CMD_EDIT, CMD_REPL, CMD_RUN, DIRECTORY_OR_FILES, ROC_FILE, }; +use std::fs::{self, FileType}; use std::io; use std::path::{Path, PathBuf}; use target_lexicon::Triple; +#[cfg(feature = "llvm")] +use roc_cli::build; +use std::ffi::{OsStr, OsString}; + +#[cfg(not(feature = "llvm"))] +fn build(_target: &Triple, _matches: &clap::ArgMatches, _config: BuildConfig) -> io::Result { + panic!("Building without LLVM is not currently supported."); +} + fn main() -> io::Result<()> { let matches = build_app().get_matches(); let exit_code = match matches.subcommand_name() { None => { - roc_editor::launch(&[])?; + launch_editor(&[])?; // rustc couldn't infer the error type here Result::::Ok(0) @@ -44,14 +54,14 @@ fn main() -> io::Result<()> { .values_of_os(DIRECTORY_OR_FILES) { None => { - roc_editor::launch(&[])?; + launch_editor(&[])?; } Some(values) => { let paths = values .map(|os_str| Path::new(os_str)) .collect::>(); - roc_editor::launch(&paths)?; + launch_editor(&paths)?; } } @@ -59,17 +69,37 @@ fn main() -> io::Result<()> { Ok(0) } Some(CMD_DOCS) => { - let values = matches + let maybe_values = matches .subcommand_matches(CMD_DOCS) .unwrap() - .values_of_os(DIRECTORY_OR_FILES) - .unwrap(); + .values_of_os(DIRECTORY_OR_FILES); - let paths = values - .map(|os_str| Path::new(os_str).to_path_buf()) - .collect::>(); + let mut values: Vec = Vec::new(); - docs(paths); + match maybe_values { + None => { + let mut os_string_values: Vec = Vec::new(); + read_all_roc_files(&OsStr::new("./").to_os_string(), &mut os_string_values)?; + for os_string in os_string_values { + values.push(os_string); + } + } + Some(os_values) => { + for os_str in os_values { + values.push(os_str.to_os_string()); + } + } + } + + let mut roc_files = Vec::new(); + + // Populate roc_files + for os_str in values { + let metadata = fs::metadata(os_str.clone())?; + roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?; + } + + docs(roc_files); Ok(0) } @@ -78,3 +108,51 @@ fn main() -> io::Result<()> { std::process::exit(exit_code); } + +fn read_all_roc_files( + dir: &OsString, + mut roc_file_paths: &mut Vec, +) -> Result<(), std::io::Error> { + let entries = fs::read_dir(dir)?; + + for entry in entries { + let path = entry?.path(); + + if path.is_dir() { + read_all_roc_files(&path.into_os_string(), &mut roc_file_paths)?; + } else if path.extension().and_then(OsStr::to_str) == Some("roc") { + let file_path = path.into_os_string(); + roc_file_paths.push(file_path); + } + } + + Ok(()) +} + +fn roc_files_recursive>( + path: P, + file_type: FileType, + roc_files: &mut Vec, +) -> io::Result<()> { + if file_type.is_dir() { + for entry_res in fs::read_dir(path)? { + let entry = entry_res?; + + roc_files_recursive(entry.path(), entry.file_type()?, roc_files)?; + } + } else { + roc_files.push(path.as_ref().to_path_buf()); + } + + Ok(()) +} + +#[cfg(feature = "editor")] +fn launch_editor(filepaths: &[&Path]) -> io::Result<()> { + roc_editor::launch(filepaths) +} + +#[cfg(not(feature = "editor"))] +fn launch_editor(_filepaths: &[&Path]) -> io::Result<()> { + panic!("Cannot launch the editor because this build of roc did not include `feature = \"editor\"`!"); +} diff --git a/cli/src/repl.rs b/cli/src/repl.rs index c0d7e03917..4de48bb3df 100644 --- a/cli/src/repl.rs +++ b/cli/src/repl.rs @@ -1,15 +1,12 @@ use const_format::concatcp; +#[cfg(feature = "llvm")] use gen::{gen_and_eval, ReplOutput}; -use roc_gen::llvm::build::OptLevel; use roc_parse::parser::{EExpr, SyntaxError}; -use rustyline::error::ReadlineError; use rustyline::highlight::{Highlighter, PromptInfo}; use rustyline::validate::{self, ValidationContext, ValidationResult, Validator}; -use rustyline::Editor; use rustyline_derive::{Completer, Helper, Hinter}; use std::borrow::Cow; use std::io; -use target_lexicon::Triple; const BLUE: &str = "\u{001b}[36m"; const PINK: &str = "\u{001b}[35m"; @@ -30,7 +27,9 @@ pub const INSTRUCTIONS: &str = "Enter an expression, or :help, or :exit/:q.\n"; pub const PROMPT: &str = concatcp!("\n", BLUE, "»", END_COL, " "); pub const CONT_PROMPT: &str = concatcp!(BLUE, "…", END_COL, " "); +#[cfg(feature = "llvm")] mod eval; +#[cfg(feature = "llvm")] mod gen; #[derive(Completer, Helper, Hinter)] @@ -107,7 +106,16 @@ impl Validator for InputValidator { } } +#[cfg(not(feature = "llvm"))] pub fn main() -> io::Result<()> { + panic!("The REPL currently requires being built with LLVM."); +} + +#[cfg(feature = "llvm")] +pub fn main() -> io::Result<()> { + use rustyline::error::ReadlineError; + use rustyline::Editor; + // To debug rustyline: // env_logger::init(); // RUST_LOG=rustyline=debug cargo run repl 2> debug.log @@ -226,7 +234,11 @@ fn report_parse_error(fail: SyntaxError) { println!("TODO Gracefully report parse error in repl: {:?}", fail); } +#[cfg(feature = "llvm")] fn eval_and_format<'a>(src: &str) -> Result> { + use roc_mono::ir::OptLevel; + use target_lexicon::Triple; + gen_and_eval(src.as_bytes(), Triple::host(), OptLevel::Normal).map(|output| match output { ReplOutput::NoProblems { expr, expr_type } => { format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type) diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index 94f51ec6cf..7d150a4756 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -2,10 +2,11 @@ use bumpalo::collections::Vec; use bumpalo::Bump; use libloading::Library; use roc_collections::all::MutMap; -use roc_gen::{run_jit_function, run_jit_function_dynamic_type}; +use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type}; use roc_module::ident::{Lowercase, TagName}; use roc_module::operator::CalledVia; use roc_module::symbol::{Interns, ModuleId, Symbol}; +use roc_mono::ir::ProcLayout; use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant}; use roc_parse::ast::{AssignedField, Expr, StrLiteral}; use roc_region::all::{Located, Region}; @@ -37,7 +38,7 @@ pub unsafe fn jit_to_ast<'a>( arena: &'a Bump, lib: Library, main_fn_name: &str, - layout: &Layout<'a>, + layout: ProcLayout<'a>, content: &Content, interns: &Interns, home: ModuleId, @@ -53,11 +54,14 @@ pub unsafe fn jit_to_ast<'a>( }; match layout { - Layout::FunctionPointer(&[], result) => { + ProcLayout { + arguments: [], + result, + } => { // this is a thunk - jit_to_ast_help(&env, lib, main_fn_name, result, content) + jit_to_ast_help(&env, lib, main_fn_name, &result, content) } - _ => jit_to_ast_help(&env, lib, main_fn_name, layout, content), + _ => Err(ToAstProblem::FunctionLayout), } } @@ -79,12 +83,26 @@ fn jit_to_ast_help<'a>( ) } Layout::Builtin(Builtin::Usize) => Ok(run_jit_function!(lib, main_fn_name, usize, |num| { - num_to_ast(env, nat_to_ast(env.arena, num), content) + num_to_ast(env, number_literal_to_ast(env.arena, num), content) })), + Layout::Builtin(Builtin::Int16) => { + Ok(run_jit_function!(lib, main_fn_name, i16, |num| num_to_ast( + env, + number_literal_to_ast(env.arena, num), + content + ))) + } + Layout::Builtin(Builtin::Int32) => { + Ok(run_jit_function!(lib, main_fn_name, i32, |num| num_to_ast( + env, + number_literal_to_ast(env.arena, num), + content + ))) + } Layout::Builtin(Builtin::Int64) => { Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast( env, - i64_to_ast(env.arena, num), + number_literal_to_ast(env.arena, num), content ))) } @@ -93,13 +111,20 @@ fn jit_to_ast_help<'a>( lib, main_fn_name, i128, - |num| num_to_ast(env, i128_to_ast(env.arena, num), content) + |num| num_to_ast(env, number_literal_to_ast(env.arena, num), content) )) } + Layout::Builtin(Builtin::Float32) => { + Ok(run_jit_function!(lib, main_fn_name, f32, |num| num_to_ast( + env, + number_literal_to_ast(env.arena, num), + content + ))) + } Layout::Builtin(Builtin::Float64) => { Ok(run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast( env, - f64_to_ast(env.arena, num), + number_literal_to_ast(env.arena, num), content ))) } @@ -116,7 +141,7 @@ fn jit_to_ast_help<'a>( } })) } - Layout::Builtin(Builtin::List(_, elem_layout)) => Ok(run_jit_function!( + Layout::Builtin(Builtin::List(elem_layout)) => Ok(run_jit_function!( lib, main_fn_name, (*const u8, usize), @@ -125,29 +150,33 @@ fn jit_to_ast_help<'a>( Layout::Builtin(other) => { todo!("add support for rendering builtin {:?} to the REPL", other) } - Layout::PhantomEmptyStruct => Ok(run_jit_function!(lib, main_fn_name, &u8, |_| { - Expr::Record { - fields: &[], - final_comments: env.arena.alloc([]), - } - })), Layout::Struct(field_layouts) => { let ptr_to_ast = |ptr: *const u8| match content { Content::Structure(FlatType::Record(fields, _)) => { - struct_to_ast(env, ptr, field_layouts, fields) + Ok(struct_to_ast(env, ptr, field_layouts, fields)) } Content::Structure(FlatType::EmptyRecord) => { - struct_to_ast(env, ptr, field_layouts, &MutMap::default()) + Ok(struct_to_ast(env, ptr, field_layouts, &MutMap::default())) } Content::Structure(FlatType::TagUnion(tags, _)) => { debug_assert_eq!(tags.len(), 1); let (tag_name, payload_vars) = tags.iter().next().unwrap(); - single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars) + Ok(single_tag_union_to_ast( + env, + ptr, + field_layouts, + tag_name.clone(), + payload_vars, + )) } - Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => { - single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), &[]) + Content::Structure(FlatType::FunctionOrTagUnion(tag_name, _, _)) => Ok( + single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), &[]), + ), + Content::Structure(FlatType::Func(_, _, _)) => { + // a function with a struct as the closure environment + Err(ToAstProblem::FunctionLayout) } other => { unreachable!( @@ -162,81 +191,141 @@ fn jit_to_ast_help<'a>( let result_stack_size = layout.stack_size(env.ptr_bytes); - Ok(run_jit_function_dynamic_type!( + run_jit_function_dynamic_type!( lib, main_fn_name, result_stack_size as usize, |bytes: *const u8| { ptr_to_ast(bytes as *const u8) } - )) + ) } - Layout::Union(UnionLayout::NonRecursive(union_layouts)) => match content { - Content::Structure(FlatType::TagUnion(tags, _)) => { - debug_assert_eq!(union_layouts.len(), tags.len()); + Layout::Union(UnionLayout::NonRecursive(union_layouts)) => { + let union_layout = UnionLayout::NonRecursive(union_layouts); - let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = - tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect(); + match content { + Content::Structure(FlatType::TagUnion(tags, _)) => { + debug_assert_eq!(union_layouts.len(), tags.len()); - let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs); + let tags_vec: std::vec::Vec<(TagName, std::vec::Vec)> = + tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect(); - let size = layout.stack_size(env.ptr_bytes); - use roc_mono::layout::WrappedVariant::*; - match union_variant { - UnionVariant::Wrapped(variant) => { - match variant { - NonRecursive { - sorted_tag_layouts: tags_and_layouts, + let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs); + + let size = layout.stack_size(env.ptr_bytes); + use roc_mono::layout::WrappedVariant::*; + match union_variant { + UnionVariant::Wrapped(variant) => { + match variant { + NonRecursive { + sorted_tag_layouts: tags_and_layouts, + } => { + Ok(run_jit_function_dynamic_type!( + lib, + main_fn_name, + size as usize, + |ptr: *const u8| { + // Because this is a `Wrapped`, the first 8 bytes encode the tag ID + let offset = tags_and_layouts + .iter() + .map(|(_, fields)| { + fields + .iter() + .map(|l| l.stack_size(env.ptr_bytes)) + .sum() + }) + .max() + .unwrap_or(0); + + let tag_id = match union_layout.tag_id_builtin() { + Builtin::Int1 => { + *(ptr.add(offset as usize) as *const i8) as i64 + } + Builtin::Int8 => { + *(ptr.add(offset as usize) as *const i8) as i64 + } + Builtin::Int16 => { + *(ptr.add(offset as usize) as *const i16) as i64 + } + _ => unreachable!("invalid tag id layout"), + }; + + // use the tag ID as an index, to get its name and layout of any arguments + let (tag_name, arg_layouts) = + &tags_and_layouts[tag_id as usize]; + + let tag_expr = tag_name_to_expr(env, tag_name); + let loc_tag_expr = + &*env.arena.alloc(Located::at_zero(tag_expr)); + + let variables = &tags[tag_name]; + + debug_assert_eq!(arg_layouts.len(), variables.len()); + + // NOTE assumes the data bytes are the first bytes + let it = + variables.iter().copied().zip(arg_layouts.iter()); + let output = sequence_of_expr(env, ptr, it); + let output = output.into_bump_slice(); + + Expr::Apply(loc_tag_expr, output, CalledVia::Space) + } + )) + } + Recursive { + sorted_tag_layouts: tags_and_layouts, + } => { + Ok(run_jit_function_dynamic_type!( + lib, + main_fn_name, + size as usize, + |ptr: *const u8| { + // Because this is a `Wrapped`, the first 8 bytes encode the tag ID + let tag_id = *(ptr as *const i64); + + // use the tag ID as an index, to get its name and layout of any arguments + let (tag_name, arg_layouts) = + &tags_and_layouts[tag_id as usize]; + + let tag_expr = tag_name_to_expr(env, tag_name); + let loc_tag_expr = + &*env.arena.alloc(Located::at_zero(tag_expr)); + + let variables = &tags[tag_name]; + + // because the arg_layouts include the tag ID, it is one longer + debug_assert_eq!( + arg_layouts.len() - 1, + variables.len() + ); + + // skip forward to the start of the first element, ignoring the tag id + let ptr = ptr.offset(8); + + let it = + variables.iter().copied().zip(&arg_layouts[1..]); + let output = sequence_of_expr(env, ptr, it); + let output = output.into_bump_slice(); + + Expr::Apply(loc_tag_expr, output, CalledVia::Space) + } + )) + } + _ => todo!(), } - | Recursive { - sorted_tag_layouts: tags_and_layouts, - } => { - Ok(run_jit_function_dynamic_type!( - lib, - main_fn_name, - size as usize, - |ptr: *const u8| { - // Because this is a `Wrapped`, the first 8 bytes encode the tag ID - let tag_id = *(ptr as *const i64); - - // use the tag ID as an index, to get its name and layout of any arguments - let (tag_name, arg_layouts) = - &tags_and_layouts[tag_id as usize]; - - let tag_expr = tag_name_to_expr(env, tag_name); - let loc_tag_expr = - &*env.arena.alloc(Located::at_zero(tag_expr)); - - let variables = &tags[tag_name]; - - // because the arg_layouts include the tag ID, it is one longer - debug_assert_eq!(arg_layouts.len() - 1, variables.len()); - - // skip forward to the start of the first element, ignoring the tag id - let ptr = ptr.offset(8); - - let it = variables.iter().copied().zip(&arg_layouts[1..]); - let output = sequence_of_expr(env, ptr, it); - let output = output.into_bump_slice(); - - Expr::Apply(loc_tag_expr, output, CalledVia::Space) - } - )) - } - _ => todo!(), } + _ => unreachable!("any other variant would have a different layout"), } - _ => unreachable!("any other variant would have a different layout"), } - } - Content::Structure(FlatType::RecursiveTagUnion(_, _, _)) => { - todo!("print recursive tag unions in the REPL") - } - Content::Alias(_, _, actual) => { - let content = env.subs.get_without_compacting(*actual).content; + Content::Structure(FlatType::RecursiveTagUnion(_, _, _)) => { + todo!("print recursive tag unions in the REPL") + } + Content::Alias(_, _, actual) => { + let content = env.subs.get_without_compacting(*actual).content; - jit_to_ast_help(env, lib, main_fn_name, layout, &content) + jit_to_ast_help(env, lib, main_fn_name, layout, &content) + } + other => unreachable!("Weird content for Union layout: {:?}", other), } - other => unreachable!("Weird content for Union layout: {:?}", other), - }, + } Layout::Union(UnionLayout::Recursive(_)) | Layout::Union(UnionLayout::NullableWrapped { .. }) | Layout::Union(UnionLayout::NullableUnwrapped { .. }) @@ -246,8 +335,6 @@ fn jit_to_ast_help<'a>( } Layout::Closure(_, _, _) => Err(ToAstProblem::FunctionLayout), - Layout::FunctionPointer(_, _) => Err(ToAstProblem::FunctionLayout), - Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"), } } @@ -272,15 +359,30 @@ fn ptr_to_ast<'a>( content: &Content, ) -> Expr<'a> { match layout { + Layout::Builtin(Builtin::Int128) => { + let num = unsafe { *(ptr as *const i128) }; + + num_to_ast(env, number_literal_to_ast(env.arena, num), content) + } Layout::Builtin(Builtin::Int64) => { let num = unsafe { *(ptr as *const i64) }; - num_to_ast(env, i64_to_ast(env.arena, num), content) + num_to_ast(env, number_literal_to_ast(env.arena, num), content) + } + Layout::Builtin(Builtin::Int32) => { + let num = unsafe { *(ptr as *const i32) }; + + num_to_ast(env, number_literal_to_ast(env.arena, num), content) + } + Layout::Builtin(Builtin::Int16) => { + let num = unsafe { *(ptr as *const i16) }; + + num_to_ast(env, number_literal_to_ast(env.arena, num), content) } Layout::Builtin(Builtin::Usize) => { let num = unsafe { *(ptr as *const usize) }; - num_to_ast(env, nat_to_ast(env.arena, num), content) + num_to_ast(env, number_literal_to_ast(env.arena, num), content) } Layout::Builtin(Builtin::Int1) => { // TODO: bits are not as expected here. @@ -292,13 +394,18 @@ fn ptr_to_ast<'a>( Layout::Builtin(Builtin::Float64) => { let num = unsafe { *(ptr as *const f64) }; - num_to_ast(env, f64_to_ast(env.arena, num), content) + num_to_ast(env, number_literal_to_ast(env.arena, num), content) + } + Layout::Builtin(Builtin::Float32) => { + let num = unsafe { *(ptr as *const f32) }; + + num_to_ast(env, number_literal_to_ast(env.arena, num), content) } Layout::Builtin(Builtin::EmptyList) => Expr::List { items: &[], final_comments: &[], }, - Layout::Builtin(Builtin::List(_, elem_layout)) => { + Layout::Builtin(Builtin::List(elem_layout)) => { // Turn the (ptr, len) wrapper struct into actual ptr and len values. let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) }; let ptr = unsafe { *(ptr as *const *const u8) }; @@ -855,25 +962,7 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E /// This is centralized in case we want to format it differently later, /// e.g. adding underscores for large numbers -fn nat_to_ast(arena: &Bump, num: usize) -> Expr<'_> { - Expr::Num(arena.alloc(format!("{}", num))) -} - -/// This is centralized in case we want to format it differently later, -/// e.g. adding underscores for large numbers -fn i64_to_ast(arena: &Bump, num: i64) -> Expr<'_> { - Expr::Num(arena.alloc(format!("{}", num))) -} - -/// This is centralized in case we want to format it differently later, -/// e.g. adding underscores for large numbers -fn i128_to_ast(arena: &Bump, num: i128) -> Expr<'_> { - Expr::Num(arena.alloc(format!("{}", num))) -} - -/// This is centralized in case we want to format it differently later, -/// e.g. adding underscores for large numbers -fn f64_to_ast(arena: &Bump, num: f64) -> Expr<'_> { +fn number_literal_to_ast(arena: &Bump, num: T) -> Expr<'_> { Expr::Num(arena.alloc(format!("{}", num))) } diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index 5a30b723fe..797fdba999 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -8,9 +8,9 @@ use roc_can::builtins::builtin_defs_map; use roc_collections::all::{MutMap, MutSet}; use roc_fmt::annotation::Formattable; use roc_fmt::annotation::{Newlines, Parens}; -use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel}; -use roc_gen::llvm::externs::add_default_roc_externs; +use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_load::file::LoadingProblem; +use roc_mono::ir::OptLevel; use roc_parse::parser::SyntaxError; use roc_types::pretty_print::{content_to_string, name_all_type_vars}; use std::path::{Path, PathBuf}; @@ -68,7 +68,8 @@ pub fn gen_and_eval<'a>( use roc_load::file::MonomorphizedModule; let MonomorphizedModule { - mut procedures, + procedures, + entry_point, interns, exposed_to_host, mut subs, @@ -130,7 +131,9 @@ pub fn gen_and_eval<'a>( let context = Context::create(); let builder = context.create_builder(); let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; - let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, "")); + let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins( + &context, "", + )); // Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no // platform to provide them. @@ -166,12 +169,12 @@ pub fn gen_and_eval<'a>( let module = arena.alloc(module); let (module_pass, function_pass) = - roc_gen::llvm::build::construct_optimization_passes(module, opt_level); + roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level); - let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module); + let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module); // Compile and add all the Procs before adding main - let env = roc_gen::llvm::build::Env { + let env = roc_gen_llvm::llvm::build::Env { arena: &arena, builder: &builder, dibuilder: &dibuilder, @@ -185,65 +188,11 @@ pub fn gen_and_eval<'a>( exposed_to_host: MutSet::default(), }; - let mut layout_ids = roc_mono::layout::LayoutIds::default(); - let mut headers = Vec::with_capacity(procedures.len()); - - // Add all the Proc headers to the module. - // We have to do this in a separate pass first, - // because their bodies may reference each other. - let mut scope = roc_gen::llvm::build::Scope::default(); - for ((symbol, layout), proc) in procedures.drain() { - let fn_val = build_proc_header(&env, &mut layout_ids, symbol, layout, &proc); - - if proc.args.is_empty() { - // this is a 0-argument thunk, i.e. a top-level constant definition - // it must be in-scope everywhere in the module! - scope.insert_top_level_thunk(symbol, arena.alloc(layout), fn_val); - } - - headers.push((proc, fn_val)); - } - - // Build each proc using its header info. - for (proc, fn_val) in headers { - let mut current_scope = scope.clone(); - - // only have top-level thunks for this proc's module in scope - // this retain is not needed for correctness, but will cause less confusion when debugging - let home = proc.name.module_id(); - current_scope.retain_top_level_thunks_for_module(home); - - build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val); - - // call finalize() before any code generation/verification - env.dibuilder.finalize(); - - if fn_val.verify(true) { - function_pass.run_on(&fn_val); - } else { - let mode = "NON-OPTIMIZED"; - - eprintln!( - "\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n", - fn_val.get_name().to_str().unwrap(), - mode, - ); - - fn_val.print_to_stderr(); - - panic!( - "The preceding code was from {:?}, which failed LLVM verification in {} build.", - fn_val.get_name().to_str().unwrap(), - mode, - ); - } - } - - let (main_fn_name, main_fn) = roc_gen::llvm::build::promote_to_main_function( + let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main( &env, - &mut layout_ids, - main_fn_symbol, - main_fn_layout, + opt_level, + procedures, + entry_point, ); env.dibuilder.finalize(); @@ -277,7 +226,7 @@ pub fn gen_and_eval<'a>( &arena, lib, main_fn_name, - &arena.alloc(main_fn_layout).full(), + main_fn_layout, &content, &env.interns, home, diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 2c55633f16..0ca7fb114e 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -7,14 +7,12 @@ extern crate roc_collections; extern crate roc_load; extern crate roc_module; -mod helpers; - #[macro_use] extern crate maplit; #[cfg(test)] mod cli_run { - use crate::helpers::{ + use cli_utils::helpers::{ example_file, extract_valgrind_errors, run_cmd, run_roc, run_with_valgrind, ValgrindError, ValgrindErrorXWhat, }; @@ -304,4 +302,162 @@ mod cli_run { assert_eq!(all_examples, std::collections::HashMap::default()); } + + #[serial(hello_world)] + fn run_hello_world() { + check_output( + &example_file("hello-world", "Hello.roc"), + "hello-world", + &[], + "Hello, World!\n", + true, + ); + } + + #[test] + #[serial(hello_world)] + fn run_hello_world_optimized() { + check_output( + &example_file("hello-world", "Hello.roc"), + "hello-world", + &[], + "Hello, World!\n", + true, + ); + } + + #[test] + #[serial(quicksort)] + fn run_quicksort_not_optimized() { + check_output( + &example_file("quicksort", "Quicksort.roc"), + "quicksort", + &[], + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n", + true, + ); + } + + #[test] + #[serial(quicksort)] + fn run_quicksort_optimized() { + check_output( + &example_file("quicksort", "Quicksort.roc"), + "quicksort", + &["--optimize"], + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n", + true, + ); + } + + #[test] + #[serial(quicksort)] + fn run_quicksort_optimized_valgrind() { + check_output( + &example_file("quicksort", "Quicksort.roc"), + "quicksort", + &["--optimize"], + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n", + true, + ); + } + + #[test] + #[serial(nqueens)] + fn run_nqueens_not_optimized() { + check_output_with_stdin( + &example_file("benchmarks", "NQueens.roc"), + "6", + "nqueens", + &[], + "4\n", + true, + ); + } + + #[test] + #[serial(cfold)] + fn run_cfold_not_optimized() { + check_output_with_stdin( + &example_file("benchmarks", "CFold.roc"), + "3", + "cfold", + &[], + "11 & 11\n", + true, + ); + } + + #[test] + #[serial(deriv)] + fn run_deriv_not_optimized() { + check_output_with_stdin( + &example_file("benchmarks", "Deriv.roc"), + "2", + "deriv", + &[], + "1 count: 6\n2 count: 22\n", + true, + ); + } + + #[test] + #[serial(deriv)] + fn run_rbtree_insert_not_optimized() { + check_output( + &example_file("benchmarks", "RBTreeInsert.roc"), + "rbtree-insert", + &[], + "Node Black 0 {} Empty Empty\n", + true, + ); + } + + #[test] + #[serial(deriv)] + fn run_rbtree_delete_not_optimized() { + check_output_with_stdin( + &example_file("benchmarks", "RBTreeDel.roc"), + "420", + "rbtree-del", + &[], + "30\n", + true, + ); + } + #[test] + #[serial(astar)] + fn run_astar_optimized_1() { + check_output( + &example_file("benchmarks", "TestAStar.roc"), + "test-astar", + &[], + "True\n", + false, + ); + } + + #[test] + #[serial(base64)] + fn base64() { + check_output( + &example_file("benchmarks", "TestBase64.roc"), + "test-base64", + &[], + "encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n", + true, + ); + } + + #[test] + #[serial(closure)] + fn closure() { + check_output( + &example_file("benchmarks", "Closure.roc"), + "closure", + &[], + "", + true, + ); + } } diff --git a/cli/tests/fixtures/multi-dep-str/platform/host.zig b/cli/tests/fixtures/multi-dep-str/platform/host.zig index 2d335ec0f7..e60688a037 100644 --- a/cli/tests/fixtures/multi-dep-str/platform/host.zig +++ b/cli/tests/fixtures/multi-dep-str/platform/host.zig @@ -59,7 +59,7 @@ pub export fn main() i32 { roc__mainForHost_1_exposed(&callresult); // stdout the result - stdout.print("{}\n", .{callresult.content.asSlice()}) catch unreachable; + stdout.print("{s}\n", .{callresult.content.asSlice()}) catch unreachable; callresult.content.deinit(); diff --git a/cli/tests/fixtures/multi-dep-thunk/platform/host.zig b/cli/tests/fixtures/multi-dep-thunk/platform/host.zig index 2d335ec0f7..e60688a037 100644 --- a/cli/tests/fixtures/multi-dep-thunk/platform/host.zig +++ b/cli/tests/fixtures/multi-dep-thunk/platform/host.zig @@ -59,7 +59,7 @@ pub export fn main() i32 { roc__mainForHost_1_exposed(&callresult); // stdout the result - stdout.print("{}\n", .{callresult.content.asSlice()}) catch unreachable; + stdout.print("{s}\n", .{callresult.content.asSlice()}) catch unreachable; callresult.content.deinit(); diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index 62efefc23d..87d53837a8 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -4,12 +4,10 @@ extern crate pretty_assertions; #[macro_use] extern crate indoc; -mod helpers; - #[cfg(test)] mod repl_eval { - use crate::helpers; - use roc_gen::run_roc::RocCallResult; + use cli_utils::helpers; + use roc_gen_llvm::run_roc::RocCallResult; #[test] fn check_discriminant_size() { @@ -18,7 +16,8 @@ mod repl_eval { let value: i64 = 1234; assert_eq!( std::mem::size_of_val(&RocCallResult::Success(value)), - roc_gen::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + std::mem::size_of_val(&value) + roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + + std::mem::size_of_val(&value) ) } @@ -500,15 +499,11 @@ mod repl_eval { #[test] fn identity_lambda() { - // Even though this gets unwrapped at runtime, the repl should still - // report it as a record expect_success("\\x -> x", " : a -> a"); } #[test] fn stdlib_function() { - // Even though this gets unwrapped at runtime, the repl should still - // report it as a record expect_success("Num.abs", " : Num a -> Num a"); } diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index a7f173d2e2..77dcf1d8a6 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -19,7 +19,7 @@ roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_load = { path = "../load" } -roc_gen = { path = "../gen" } +roc_gen_llvm = { path = "../gen_llvm", optional = true } roc_reporting = { path = "../reporting" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! @@ -28,24 +28,7 @@ inlinable_string = "0.1.0" libloading = "0.6" tempfile = "3.1.0" serde_json = "1.0" -# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. -# -# The reason for this fork is that the way Inkwell is designed, you have to use -# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that -# breaking changes get pushed directly to that branch, which breaks our build -# without warning. -# -# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch), -# but although that worked locally, it did not work on GitHub Actions. (After a few -# hours of investigation, gave up trying to figure out why.) So this is the workaround: -# having an immutable tag on the rtfeldman/inkwell fork which points to -# a particular "release" of Inkwell. -# -# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest -# commit of TheDan64/inkwell, push a new tag which points to the latest commit, -# change the tag value in this Cargo.toml to point to that tag, and `cargo update`. -# This way, GitHub Actions works and nobody's builds get broken. -inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] } +inkwell = { path = "../../vendor/inkwell", optional = true } target-lexicon = "0.10" [dev-dependencies] @@ -56,6 +39,10 @@ quickcheck = "0.8" quickcheck_macros = "0.8" [features] +default = ["llvm"] target-arm = [] target-aarch64 = [] target-webassembly = [] +# This is a separate feature because when we generate docs on Netlify, +# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.) +llvm = ["inkwell", "roc_gen_llvm"] diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 95983e1a8d..ee1124c905 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -1,21 +1,21 @@ -use crate::target; use crate::target::arch_str; -use inkwell::module::Module; -use inkwell::targets::{CodeModel, FileType, RelocMode}; +#[cfg(feature = "llvm")] use libloading::{Error, Library}; -use roc_gen::llvm::build::OptLevel; +#[cfg(feature = "llvm")] +use roc_mono::ir::OptLevel; use std::collections::HashMap; use std::env; use std::io; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Output}; use target_lexicon::{Architecture, OperatingSystem, Triple}; -use tempfile::tempdir; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum LinkType { - Executable, - Dylib, + // These numbers correspond to the --lib flag; if it's present + // (e.g. is_present returns `1 as bool`), this will be 1 as well. + Executable = 0, + Dylib = 1, } /// input_paths can include the host as well as the app. e.g. &["host.o", "roc_app.o"] @@ -360,6 +360,9 @@ fn link_linux( }; let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string()); + + init_arch(target); + // NOTE: order of arguments to `ld` matters here! // The `-l` flags should go after the `.o` arguments Ok(( @@ -466,8 +469,8 @@ fn link_macos( "-lc++", // "-lc++abi", // "-lunwind", // TODO will eventually need this, see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840 - "-framework", - "Security", // This "-framework Security" arg is needed for the `rand` crate in examples/cli + // "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli + // "Security", // Output "-o", output_path.to_str().unwrap(), // app @@ -477,12 +480,16 @@ fn link_macos( )) } +#[cfg(feature = "llvm")] pub fn module_to_dylib( - module: &Module, + module: &inkwell::module::Module, target: &Triple, opt_level: OptLevel, ) -> Result { - let dir = tempdir().unwrap(); + use crate::target::{self, convert_opt_level}; + use inkwell::targets::{CodeModel, FileType, RelocMode}; + + let dir = tempfile::tempdir().unwrap(); let filename = PathBuf::from("Test.roc"); let file_path = dir.path().join(filename); let mut app_o_file = file_path; @@ -492,7 +499,8 @@ pub fn module_to_dylib( // Emit the .o file using position-indepedent code (PIC) - needed for dylibs let reloc = RelocMode::PIC; let model = CodeModel::Default; - let target_machine = target::target_machine(target, opt_level.into(), reloc, model).unwrap(); + let target_machine = + target::target_machine(target, convert_opt_level(opt_level), reloc, model).unwrap(); target_machine .write_to_file(module, FileType::Object, &app_o_file) @@ -529,3 +537,13 @@ fn validate_output(file_name: &str, cmd_name: &str, output: Output) { } } } + +#[cfg(feature = "llvm")] +fn init_arch(target: &Triple) { + crate::target::init_arch(target); +} + +#[cfg(not(feature = "llvm"))] +fn init_arch(_target: &Triple) { + panic!("Tried to initialize LLVM when crate was not built with `feature = \"llvm\"` enabled"); +} diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index 3fad005f6e..82d7261950 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -1,14 +1,14 @@ -use crate::target; -use bumpalo::Bump; -use inkwell::context::Context; -use inkwell::targets::{CodeModel, FileType, RelocMode}; -pub use roc_gen::llvm::build::FunctionIterator; -use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope}; +#[cfg(feature = "llvm")] +use roc_gen_llvm::llvm::build::module_from_builtins; +#[cfg(feature = "llvm")] +pub use roc_gen_llvm::llvm::build::FunctionIterator; +#[cfg(feature = "llvm")] use roc_load::file::MonomorphizedModule; -use roc_mono::layout::LayoutIds; +#[cfg(feature = "llvm")] +use roc_mono::ir::OptLevel; +#[cfg(feature = "llvm")] use std::path::{Path, PathBuf}; -use std::time::{Duration, SystemTime}; -use target_lexicon::Triple; +use std::time::Duration; #[derive(Debug, Clone, Copy, Default)] pub struct CodeGenTiming { @@ -19,16 +19,24 @@ pub struct CodeGenTiming { // TODO how should imported modules factor into this? What if those use builtins too? // TODO this should probably use more helper functions // TODO make this polymorphic in the llvm functions so it can be reused for another backend. +#[cfg(feature = "llvm")] #[allow(clippy::cognitive_complexity)] pub fn gen_from_mono_module( - arena: &Bump, + arena: &bumpalo::Bump, mut loaded: MonomorphizedModule, roc_file_path: &Path, - target: Triple, + target: target_lexicon::Triple, app_o_file: &Path, opt_level: OptLevel, emit_debug_info: bool, ) -> CodeGenTiming { + use crate::target::{self, convert_opt_level}; + use inkwell::attributes::{Attribute, AttributeLoc}; + use inkwell::context::Context; + use inkwell::module::Linkage; + use inkwell::targets::{CodeModel, FileType, RelocMode}; + use std::time::SystemTime; + use roc_reporting::report::{ can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE, }; @@ -88,9 +96,6 @@ pub fn gen_from_mono_module( // module.strip_debug_info(); // mark our zig-defined builtins as internal - use inkwell::attributes::{Attribute, AttributeLoc}; - use inkwell::module::Linkage; - let app_ll_file = { let mut temp = PathBuf::from(roc_file_path); temp.set_extension("ll"); @@ -100,30 +105,32 @@ pub fn gen_from_mono_module( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = context.create_enum_attribute(kind_id, 1); + let enum_attr = context.create_enum_attribute(kind_id, 1); for function in FunctionIterator::from_module(module) { let name = function.get_name().to_str().unwrap(); + + // mark our zig-defined builtins as internal if name.starts_with("roc_builtins") { function.set_linkage(Linkage::Internal); } - if name.starts_with("roc_builtins.dict") || name.starts_with("dict.RocDict") { - function.add_attribute(AttributeLoc::Function, attr); - } - - if name.starts_with("roc_builtins.list") || name.starts_with("list.RocList") { - function.add_attribute(AttributeLoc::Function, attr); + if name.starts_with("roc_builtins.dict") + || name.starts_with("dict.RocDict") + || name.starts_with("roc_builtins.list") + || name.starts_with("list.RocList") + { + function.add_attribute(AttributeLoc::Function, enum_attr); } } let builder = context.create_builder(); - let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module); - let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level); + let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module); + let (mpm, _fpm) = roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level); // Compile and add all the Procs before adding main let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; - let env = roc_gen::llvm::build::Env { + let env = roc_gen_llvm::llvm::build::Env { arena: &arena, builder: &builder, dibuilder: &dibuilder, @@ -136,55 +143,13 @@ pub fn gen_from_mono_module( exposed_to_host: loaded.exposed_to_host.keys().copied().collect(), }; - // Populate Procs further and get the low-level Expr from the canonical Expr - let mut headers = Vec::with_capacity(loaded.procedures.len()); - - // Add all the Proc headers to the module. - // We have to do this in a separate pass first, - // because their bodies may reference each other. - let mut layout_ids = LayoutIds::default(); - - let mut scope = Scope::default(); - for ((symbol, layout), proc) in loaded.procedures { - let fn_val = build_proc_header(&env, &mut layout_ids, symbol, layout, &proc); - - if proc.args.is_empty() { - // this is a 0-argument thunk, i.e. a top-level constant definition - // it must be in-scope everywhere in the module! - scope.insert_top_level_thunk(symbol, arena.alloc(layout), fn_val); - } - - headers.push((proc, fn_val)); - } - - // Build each proc using its header info. - for (proc, fn_val) in headers { - // NOTE: This is here to be uncommented in case verification fails. - // (This approach means we don't have to defensively clone name here.) - // - // println!("\n\nBuilding and then verifying function {:?}\n\n", proc); - build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val); - - // call finalize() before any code generation/verification - env.dibuilder.finalize(); - - if fn_val.verify(true) { - fpm.run_on(&fn_val); - } else { - fn_val.print_to_stderr(); - - // write the ll code to a file, so we can modify it - env.module.print_to_file(&app_ll_file).unwrap(); - - // env.module.print_to_stderr(); - // NOTE: If this fails, uncomment the above println to debug. - panic!( - r"Non-main function {:?} failed LLVM verification. I wrote the full LLVM IR to {:?}", - fn_val.get_name(), - app_ll_file, - ); - } - } + roc_gen_llvm::llvm::build::build_procedures( + &env, + opt_level, + loaded.procedures, + loaded.entry_point, + Some(&app_ll_file), + ); env.dibuilder.finalize(); @@ -199,8 +164,9 @@ pub fn gen_from_mono_module( env.module.print_to_file(&app_ll_file).unwrap(); panic!( - "😱 LLVM errors when defining module; I wrote the full LLVM IR to {:?}\n\n {:?}", - app_ll_file, errors, + "😱 LLVM errors when defining module; I wrote the full LLVM IR to {:?}\n\n {}", + app_ll_file, + errors.to_string(), ); } @@ -229,7 +195,7 @@ pub fn gen_from_mono_module( // run the debugir https://github.com/vaivaswatha/debugir tool match Command::new("debugir") .env_clear() - .args(&[app_ll_file.to_str().unwrap()]) + .args(&["-instnamer", app_ll_file.to_str().unwrap()]) .output() { Ok(_) => {} @@ -245,7 +211,7 @@ pub fn gen_from_mono_module( } // assemble the .ll into a .bc - let _ = Command::new("llvm-as-10") + let _ = Command::new("llvm-as") .env_clear() .args(&[ app_ll_dbg_file.to_str().unwrap(), @@ -257,7 +223,7 @@ pub fn gen_from_mono_module( // write the .o file. Note that this builds the .o for the local machine, // and ignores the `target_machine` entirely. - let _ = Command::new("llc-10") + let _ = Command::new("llc-12") .env_clear() .args(&[ "-filetype=obj", @@ -273,7 +239,7 @@ pub fn gen_from_mono_module( let reloc = RelocMode::Default; let model = CodeModel::Default; let target_machine = - target::target_machine(&target, opt_level.into(), reloc, model).unwrap(); + target::target_machine(&target, convert_opt_level(opt_level), reloc, model).unwrap(); target_machine .write_to_file(&env.module, FileType::Object, &app_o_file) diff --git a/compiler/build/src/target.rs b/compiler/build/src/target.rs index 8137357377..4e171359d0 100644 --- a/compiler/build/src/target.rs +++ b/compiler/build/src/target.rs @@ -1,7 +1,10 @@ -use inkwell::targets::{ - CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple, +#[cfg(feature = "llvm")] +use inkwell::{ + targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple}, + OptimizationLevel, }; -use inkwell::OptimizationLevel; +#[cfg(feature = "llvm")] +use roc_mono::ir::OptLevel; use target_lexicon::{Architecture, OperatingSystem, Triple}; pub fn target_triple_str(target: &Triple) -> &'static str { @@ -28,36 +31,20 @@ pub fn target_triple_str(target: &Triple) -> &'static str { } } -/// NOTE: arch_str is *not* the same as the beginning of the magic target triple -/// string! For example, if it's "x86-64" here, the magic target triple string -/// will begin with "x86_64" (with an underscore) instead. -pub fn arch_str(target: &Triple) -> &'static str { - // Best guide I've found on how to determine these magic strings: - // - // https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures +#[cfg(feature = "llvm")] +pub fn init_arch(target: &Triple) { match target.architecture { Architecture::X86_64 => { Target::initialize_x86(&InitializationConfig::default()); - - "x86-64" } Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => { Target::initialize_aarch64(&InitializationConfig::default()); - "aarch64" } Architecture::Arm(_) if cfg!(feature = "target-arm") => { - // NOTE: why not enable arm and wasm by default? - // - // We had some trouble getting them to link properly. This may be resolved in the - // future, or maybe it was just some weird configuration on one machine. Target::initialize_arm(&InitializationConfig::default()); - - "arm" } Architecture::Wasm32 if cfg!(feature = "target-webassembly") => { Target::initialize_webassembly(&InitializationConfig::default()); - - "wasm32" } _ => panic!( "TODO gracefully handle unsupported target architecture: {:?}", @@ -66,6 +53,26 @@ pub fn arch_str(target: &Triple) -> &'static str { } } +/// NOTE: arch_str is *not* the same as the beginning of the magic target triple +/// string! For example, if it's "x86-64" here, the magic target triple string +/// will begin with "x86_64" (with an underscore) instead. +pub fn arch_str(target: &Triple) -> &'static str { + // Best guide I've found on how to determine these magic strings: + // + // https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures + match target.architecture { + Architecture::X86_64 => "x86-64", + Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => "aarch64", + Architecture::Arm(_) if cfg!(feature = "target-arm") => "arm", + Architecture::Wasm32 if cfg!(feature = "target-webassembly") => "wasm32", + _ => panic!( + "TODO gracefully handle unsupported target architecture: {:?}", + target.architecture + ), + } +} + +#[cfg(feature = "llvm")] pub fn target_machine( target: &Triple, opt: OptimizationLevel, @@ -74,6 +81,8 @@ pub fn target_machine( ) -> Option { let arch = arch_str(target); + init_arch(target); + Target::from_name(arch).unwrap().create_target_machine( &TargetTriple::create(target_triple_str(target)), "generic", @@ -83,3 +92,11 @@ pub fn target_machine( model, ) } + +#[cfg(feature = "llvm")] +pub fn convert_opt_level(level: OptLevel) -> OptimizationLevel { + match level { + OptLevel::Normal => OptimizationLevel::None, + OptLevel::Optimize => OptimizationLevel::Aggressive, + } +} diff --git a/compiler/builtins/README.md b/compiler/builtins/README.md index fdb5bd793b..8e1eb0e291 100644 --- a/compiler/builtins/README.md +++ b/compiler/builtins/README.md @@ -9,15 +9,15 @@ Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them. But we can use these values and some of these are necessary for implementing builtins. For example, `List.get` returns tags, and it is not easy for us to create tags when composing LLVM. What is easier however, is: -- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, Int -> elem` in LLVM -- ..writing `List elem, Int -> Result elem [ OutOfBounds ]*` in a type safe way that uses `getUnsafe` internally, only after it checks if the `elem` at `Int` index exists. +- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, Nat -> elem` in LLVM +- ..writing `List elem, Nat -> Result elem [ OutOfBounds ]*` in a type safe way that uses `getUnsafe` internally, only after it checks if the `elem` at `Nat` index exists. ### can/src/builtins.rs Right at the top of this module is a function called `builtin_defs`. All this is doing is mapping the `Symbol` defined in `module/src/symbol.rs` to its implementation. Some of the builtins are quite complex, such as `list_get`. What makes `list_get` is that it returns tags, and in order to return tags it first has to defer to lower-level functions via an if statement. -Lets look at `List.repeat : elem, Int -> List elem`, which is more straight-forward, and points directly to its lower level implementation: +Lets look at `List.repeat : elem, Nat -> List elem`, which is more straight-forward, and points directly to its lower level implementation: ``` fn list_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def { let elem_var = var_store.fresh(); @@ -42,7 +42,7 @@ fn list_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } ``` -In these builtin definitions you will need to allocate for and list the arguments. For `List.repeat`, the arguments are the `elem_var` and the `len_var`. So in both the `body` and `defn` we list these arguments in a vector, with the `Symobl::ARG_1` adn` Symvol::ARG_2` designating which argument is which. +In these builtin definitions you will need to allocate for and list the arguments. For `List.repeat`, the arguments are the `elem_var` and the `len_var`. So in both the `body` and `defn` we list these arguments in a vector, with the `Symobl::ARG_1` and` Symvol::ARG_2` designating which argument is which. Since `List.repeat` is implemented entirely as low level functions, its `body` is a `RunLowLevel`, and the `op` is `LowLevel::ListRepeat`. Lets talk about `LowLevel` in the next section. @@ -60,7 +60,7 @@ Its one thing to actually write these functions, its _another_ thing to let the ## Specifying how we pass args to the function ### builtins/mono/src/borrow.rs -After we have all of this, we need to specify if the arguements we're passing are owned, borrowed or irrelvant. Towards the bottom of this file, add a new case for you builtin and specify each arg. Be sure to read the comment, as it explains this in more detail. +After we have all of this, we need to specify if the arguments we're passing are owned, borrowed or irrelvant. Towards the bottom of this file, add a new case for you builtin and specify each arg. Be sure to read the comment, as it explains this in more detail. ## Specifying the uniqueness of a function ### builtins/src/unique.rs diff --git a/compiler/builtins/bitcode/.gitignore b/compiler/builtins/bitcode/.gitignore index cd473dd7c4..18b2c682d4 100644 --- a/compiler/builtins/bitcode/.gitignore +++ b/compiler/builtins/bitcode/.gitignore @@ -1,5 +1,7 @@ zig-cache src/zig-cache +benchmark/zig-cache builtins.ll builtins.bc builtins.o +dec diff --git a/compiler/builtins/bitcode/README.md b/compiler/builtins/bitcode/README.md index 1cbef7b727..1d9fbb225b 100644 --- a/compiler/builtins/bitcode/README.md +++ b/compiler/builtins/bitcode/README.md @@ -3,10 +3,10 @@ ## Adding a bitcode builtin To add a builtin: -1. Add the function to the relevent module. For `Num` builtin use it in `src/num.zig`, for `Str` builtins use `src/str.zig`, and so on. **For anything you add, you must add tests for it!** Not only does to make the builtins more maintainable, it's the the easiest way to test these functions on Zig. To run the test, run: `zig build test` +1. Add the function to the relevant module. For `Num` builtin use it in `src/num.zig`, for `Str` builtins use `src/str.zig`, and so on. **For anything you add, you must add tests for it!** Not only does to make the builtins more maintainable, it's the the easiest way to test these functions on Zig. To run the test, run: `zig build test` 2. Make sure the function is public with the `pub` keyword and uses the C calling convention. This is really easy, just add `pub` and `callconv(.C)` to the function declaration like so: `pub fn atan(num: f64) callconv(.C) f64 { ... }` -3. In `src/main.zig`, export the function. This is also organized by module. For example, for a `Num` function find the `Num` section and add: `comptime { exportNumFn(num.atan, "atan"); }`. The first arguement is the function, the second is the name of it in LLVM. -4. In `compiler/builtins/src/bitcode.rs`, add a constant for the new function. This is how we use it in Rust. Once again, this is organized by module, so just find the relevent area and add your new function. +3. In `src/main.zig`, export the function. This is also organized by module. For example, for a `Num` function find the `Num` section and add: `comptime { exportNumFn(num.atan, "atan"); }`. The first argument is the function, the second is the name of it in LLVM. +4. In `compiler/builtins/src/bitcode.rs`, add a constant for the new function. This is how we use it in Rust. Once again, this is organized by module, so just find the relevant area and add your new function. 5. You can now your function in Rust using `call_bitcode_fn` in `llvm/src/build.rs`! ## How it works @@ -32,4 +32,4 @@ There will be two directories like `roc_builtins-[some random characters]`, look ## Calling bitcode functions -use the `call_bitcode_fn` function defined in `llvm/src/build.rs` to call bitcode funcitons. +use the `call_bitcode_fn` function defined in `llvm/src/build.rs` to call bitcode functions. diff --git a/compiler/builtins/bitcode/benchmark.sh b/compiler/builtins/bitcode/benchmark.sh new file mode 100755 index 0000000000..ef7b3d6b35 --- /dev/null +++ b/compiler/builtins/bitcode/benchmark.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -euxo pipefail + +zig build-exe benchmark/dec.zig -O ReleaseFast --main-pkg-path . +./dec diff --git a/compiler/builtins/bitcode/benchmark/dec.zig b/compiler/builtins/bitcode/benchmark/dec.zig new file mode 100644 index 0000000000..96d35a7af1 --- /dev/null +++ b/compiler/builtins/bitcode/benchmark/dec.zig @@ -0,0 +1,174 @@ +const std = @import("std"); +const time = std.time; +const Timer = time.Timer; + +const RocStr = @import("../src/str.zig").RocStr; +const RocDec = @import("../src/dec.zig").RocDec; + +var timer: Timer = undefined; + +pub fn main() !void { + const stdout = std.io.getStdOut().writer(); + timer = try Timer.start(); + + try stdout.print("7 additions took ", .{}); + try avg_runs(add7); + + try stdout.print("7 subtractions took ", .{}); + try avg_runs(sub7); + + try stdout.print("7 multiplications took ", .{}); + try avg_runs(mul7); + + try stdout.print("7 divisions took ", .{}); + try avg_runs(div7); +} + +fn avg_runs(func: fn() u64) !void { + const stdout = std.io.getStdOut().writer(); + var first_run = func(); + var lowest = first_run; + var highest = first_run; + var sum = first_run; + + // 31 runs + var runs = [_]u64{ first_run, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + var next_run: usize = 1; // we already did first_run + + while (next_run < runs.len) { + const run = func(); + + lowest = std.math.min(lowest, run); + highest = std.math.max(highest, run); + + runs[next_run] = run; + + next_run += 1; + } + + std.sort.sort(u64, &runs, {}, comptime std.sort.asc(u64)); + + const median = runs[runs.len / 2]; + + try stdout.print("{}ns (lowest: {}ns, highest: {}ns)\n", .{median, lowest, highest}); +} + +fn add7() u64 { + var str1 = RocStr.init("1.2", 3); + const dec1 = RocDec.fromStr(str1).?; + + var str2 = RocStr.init("3.4", 3); + const dec2 = RocDec.fromStr(str2).?; + + timer.reset(); + + var a = dec1.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + a = a.add(dec1); + a = a.add(dec2); + + return timer.read(); +} + +fn sub7() u64 { + var str1 = RocStr.init("1.2", 3); + const dec1 = RocDec.fromStr(str1).?; + + var str2 = RocStr.init("3.4", 3); + const dec2 = RocDec.fromStr(str2).?; + + timer.reset(); + + var a = dec1.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + a = a.sub(dec1); + a = a.sub(dec2); + + return timer.read(); +} + +fn mul7() u64 { + var str1 = RocStr.init("1.2", 3); + const dec1 = RocDec.fromStr(str1).?; + + var str2 = RocStr.init("3.4", 3); + const dec2 = RocDec.fromStr(str2).?; + + timer.reset(); + + var a = dec1.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + a = a.mul(dec1); + a = a.mul(dec2); + + return timer.read(); +} + +fn div7() u64 { + var str1 = RocStr.init("1.2", 3); + const dec1 = RocDec.fromStr(str1).?; + + var str2 = RocStr.init("3.4", 3); + const dec2 = RocDec.fromStr(str2).?; + + timer.reset(); + + var a = dec1.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + a = a.div(dec1); + a = a.div(dec2); + + return timer.read(); +} diff --git a/compiler/builtins/bitcode/build.sh b/compiler/builtins/bitcode/build.sh new file mode 100755 index 0000000000..e863770c8e --- /dev/null +++ b/compiler/builtins/bitcode/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -euxo pipefail + +zig build-obj src/main.zig -O ReleaseFast -femit-llvm-ir=builtins.ll -femit-bin=builtins.o --strip diff --git a/compiler/builtins/bitcode/build.zig b/compiler/builtins/bitcode/build.zig index 9d3b0456a7..e9835e0db5 100644 --- a/compiler/builtins/bitcode/build.zig +++ b/compiler/builtins/bitcode/build.zig @@ -1,16 +1,15 @@ -const builtin = @import("builtin"); const std = @import("std"); const mem = std.mem; const Builder = std.build.Builder; pub fn build(b: *Builder) void { - // b.setPreferredReleaseMode(builtin.Mode.Debug); - b.setPreferredReleaseMode(builtin.Mode.ReleaseFast); + // b.setPreferredReleaseMode(builtin.Mode.Debug + b.setPreferredReleaseMode(.ReleaseFast); const mode = b.standardReleaseOptions(); // Options const fallback_main_path = "./src/main.zig"; - const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\" and \"test\". Defaults to \"{}\". ", .{fallback_main_path}); + const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\" and \"test\". Defaults to \"{s}\". ", .{fallback_main_path}); const main_path = b.option([]const u8, "main-path", main_path_desc) orelse fallback_main_path; // Tests diff --git a/compiler/builtins/bitcode/run-tests.sh b/compiler/builtins/bitcode/run-tests.sh index bf13fccb8f..92bb9fae6d 100755 --- a/compiler/builtins/bitcode/run-tests.sh +++ b/compiler/builtins/bitcode/run-tests.sh @@ -6,4 +6,4 @@ set -euxo pipefail zig build test # fmt every zig -find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check +find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check || (echo "zig fmt --check FAILED! Check the previuous lines to see which files were improperly formatted." && exit 1) diff --git a/compiler/builtins/bitcode/src/dec.zig b/compiler/builtins/bitcode/src/dec.zig index 189347780f..011e37cac1 100644 --- a/compiler/builtins/bitcode/src/dec.zig +++ b/compiler/builtins/bitcode/src/dec.zig @@ -1,16 +1,199 @@ const std = @import("std"); +const str = @import("str.zig"); const math = std.math; +const RocStr = str.RocStr; pub const RocDec = struct { num: i128, - pub const decimal_places: u32 = 18; + pub const decimal_places: u5 = 18; + pub const whole_number_places: u5 = 21; + const max_digits: u6 = 39; + const leading_zeros: [17]u8 = "00000000000000000".*; pub const min: RocDec = .{ .num = math.minInt(i128) }; pub const max: RocDec = .{ .num = math.maxInt(i128) }; - pub const one_point_zero: RocDec = .{ .num = comptime math.pow(i128, 10, RocDec.decimal_places) }; + pub const one_point_zero_i128: i128 = math.pow(i128, 10, RocDec.decimal_places); + pub const one_point_zero: RocDec = .{ .num = one_point_zero_i128 }; + + pub fn fromU64(num: u64) RocDec { + return .{ .num = num * one_point_zero_i128 }; + } + + pub fn fromStr(roc_str: RocStr) ?RocDec { + if (roc_str.isEmpty()) { + return null; + } + + const length = roc_str.len(); + + const roc_str_slice = roc_str.asSlice(); + + var is_negative: bool = roc_str_slice[0] == '-'; + var initial_index: usize = if (is_negative) 1 else 0; + + var point_index: ?usize = null; + var index: usize = initial_index; + while (index < length) { + var byte: u8 = roc_str_slice[index]; + if (byte == '.' and point_index == null) { + point_index = index; + index += 1; + continue; + } + + if (!isDigit(byte)) { + return null; + } + index += 1; + } + + var before_str_length = length; + var after_val_i128: ?i128 = null; + if (point_index) |pi| { + before_str_length = pi; + + var after_str_len = (length - 1) - pi; + if (after_str_len > decimal_places) { + std.debug.panic("TODO runtime exception for too many decimal places!", .{}); + } + var diff_decimal_places = decimal_places - after_str_len; + + var after_str = roc_str_slice[pi + 1 .. length]; + var after_u64 = std.fmt.parseUnsigned(u64, after_str, 10) catch null; + after_val_i128 = if (after_u64) |f| @intCast(i128, f) * math.pow(i128, 10, diff_decimal_places) else null; + } + + var before_str = roc_str_slice[initial_index..before_str_length]; + var before_val_not_adjusted = std.fmt.parseUnsigned(i128, before_str, 10) catch null; + + var before_val_i128: ?i128 = null; + if (before_val_not_adjusted) |before| { + var result: i128 = undefined; + var overflowed = @mulWithOverflow(i128, before, one_point_zero_i128, &result); + if (overflowed) { + std.debug.panic("TODO runtime exception for overflow!", .{}); + } + before_val_i128 = result; + } + + var dec: ?RocDec = null; + if (before_val_i128) |before| { + if (after_val_i128) |after| { + var result: i128 = undefined; + var overflowed = @addWithOverflow(i128, before, after, &result); + if (overflowed) { + std.debug.panic("TODO runtime exception for overflow!", .{}); + } + dec = .{ .num = result }; + } else { + dec = .{ .num = before }; + } + } else if (after_val_i128) |after| { + dec = .{ .num = after }; + } + + if (dec) |d| { + if (is_negative) { + dec = d.negate(); + } + } + + return dec; + } + + fn isDigit(c: u8) bool { + return (c -% 48) <= 9; + } + + pub fn toStr(self: RocDec) ?RocStr { + // Special case + if (self.num == 0) { + return RocStr.init("0.0", 3); + } + + // Check if this Dec is negative, and if so convert to positive + // We will handle adding the '-' later + const is_negative = self.num < 0; + const num = if (is_negative) std.math.negate(self.num) catch { + std.debug.panic("TODO runtime exception failing to negate", .{}); + } else self.num; + + // Format the backing i128 into an array of digits (u8s) + var digit_bytes: [max_digits + 1]u8 = undefined; + var num_digits = std.fmt.formatIntBuf(digit_bytes[0..], num, 10, false, .{}); + + // Get the slice for before the decimal point + var before_digits_slice: []const u8 = undefined; + var before_digits_offset: usize = 0; + var before_digits_adjust: u6 = 0; + if (num_digits > decimal_places) { + before_digits_offset = num_digits - decimal_places; + before_digits_slice = digit_bytes[0..before_digits_offset]; + } else { + before_digits_adjust = @intCast(u6, math.absInt(@intCast(i7, num_digits) - decimal_places) catch { + std.debug.panic("TODO runtime exception for overflow when getting abs", .{}); + }); + before_digits_slice = "0"; + } + + // Figure out how many trailing zeros there are + // I tried to use https://ziglang.org/documentation/0.8.0/#ctz and it mostly worked, + // but was giving seemingly incorrect values for certain numbers. So instead we use + // a while loop and figure it out that way. + // + // const trailing_zeros = @ctz(u6, num); + // + var trailing_zeros: u6 = 0; + var index = decimal_places - 1 - before_digits_adjust; + var is_consecutive_zero = true; + while (index != 0) { + var digit = digit_bytes[before_digits_offset + index]; + if (digit == '0' and is_consecutive_zero) { + trailing_zeros += 1; + } else { + is_consecutive_zero = false; + } + index -= 1; + } + + // Figure out if we need to prepend any zeros to the after decimal point + // For example, for the number 0.000123 we need to prepend 3 zeros after the decimal point + // This will only be needed for numbers less 0.01, otherwise after_digits_slice will handle this + const after_zeros_num = if (num_digits < decimal_places) decimal_places - num_digits else 0; + const after_zeros_slice: []const u8 = leading_zeros[0..after_zeros_num]; + + // Get the slice for after the decimal point + var after_digits_slice: []const u8 = undefined; + if ((num_digits - before_digits_offset) == trailing_zeros) { + after_digits_slice = "0"; + } else { + after_digits_slice = digit_bytes[before_digits_offset .. num_digits - trailing_zeros]; + } + + // Get the slice for the sign + const sign_slice: []const u8 = if (is_negative) "-" else leading_zeros[0..0]; + + // Hardcode adding a `1` for the '.' character + const str_len: usize = sign_slice.len + before_digits_slice.len + 1 + after_zeros_slice.len + after_digits_slice.len; + + // Join the slices together + // We do `max_digits + 2` here because we need to account for a possible sign ('-') and the dot ('.'). + // Ideally, we'd use str_len here + var str_bytes: [max_digits + 2]u8 = undefined; + _ = std.fmt.bufPrint(str_bytes[0..str_len], "{s}{s}.{s}{s}", .{ sign_slice, before_digits_slice, after_zeros_slice, after_digits_slice }) catch { + std.debug.panic("TODO runtime exception failing to print slices", .{}); + }; + + return RocStr.init(&str_bytes, str_len); + } + + pub fn negate(self: RocDec) ?RocDec { + var negated = math.negate(self.num) catch null; + return if (negated) |n| .{ .num = n } else null; + } pub fn add(self: RocDec, other: RocDec) RocDec { var answer: i128 = undefined; @@ -23,6 +206,17 @@ pub const RocDec = struct { } } + pub fn sub(self: RocDec, other: RocDec) RocDec { + var answer: i128 = undefined; + const overflowed = @subWithOverflow(i128, self.num, other.num, &answer); + + if (!overflowed) { + return RocDec{ .num = answer }; + } else { + std.debug.panic("TODO runtime exception for overflow!", .{}); + } + } + pub fn mul(self: RocDec, other: RocDec) RocDec { const self_i128 = self.num; const other_i128 = other.num; @@ -58,6 +252,76 @@ pub const RocDec = struct { return .{ .num = unsigned_answer }; } } + + pub fn div(self: RocDec, other: RocDec) RocDec { + const numerator_i128 = self.num; + const denominator_i128 = other.num; + + // (0 / n) is always 0 + if (numerator_i128 == 0) { + return RocDec{ .num = 0 }; + } + + // (n / 0) is an error + if (denominator_i128 == 0) { + std.debug.panic("TODO runtime exception for divide by 0!", .{}); + } + + // If they're both negative, or if neither is negative, the final answer + // is positive or zero. If one is negative and the denominator isn't, the + // final answer is negative (or zero, in which case final sign won't matter). + // + // It's important that we do this in terms of negatives, because doing + // it in terms of positives can cause bugs when one is zero. + const is_answer_negative = (numerator_i128 < 0) != (denominator_i128 < 0); + + // Break the two i128s into two { hi: u64, lo: u64 } tuples, discarding + // the sign for now. + // + // We'll multiply all 4 combinations of these (hi1 x lo1, hi2 x lo2, + // hi1 x lo2, hi2 x lo1) and add them as appropriate, then apply the + // appropriate sign at the very end. + // + // We do checked_abs because if we had -i128::MAX before, this will overflow. + + const numerator_abs_i128 = math.absInt(numerator_i128) catch { + // Currently, if you try to do multiplication on i64::MIN, panic + // unless you're specifically multiplying by 0 or 1. + // + // Maybe we could support more cases in the future + if (denominator_i128 == one_point_zero_i128) { + return self; + } else { + std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + } + }; + const numerator_u128 = @intCast(u128, numerator_abs_i128); + + const denominator_abs_i128 = math.absInt(denominator_i128) catch { + // Currently, if you try to do multiplication on i64::MIN, panic + // unless you're specifically multiplying by 0 or 1. + // + // Maybe we could support more cases in the future + if (numerator_i128 == one_point_zero_i128) { + return other; + } else { + std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + } + }; + const denominator_u128 = @intCast(u128, denominator_abs_i128); + + const numerator_u256: U256 = mul_u128(numerator_u128, math.pow(u128, 10, decimal_places)); + const answer = div_u256_by_u128(numerator_u256, denominator_u128); + + var unsigned_answer: i128 = undefined; + if (answer.hi == 0 and answer.lo <= math.maxInt(i128)) { + unsigned_answer = @intCast(i128, answer.lo); + } else { + std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + } + + return RocDec{ .num = if (is_answer_negative) -unsigned_answer else unsigned_answer }; + } }; const U256 = struct { @@ -75,7 +339,7 @@ fn mul_and_decimalize(a: u128, b: u128) i128 { // floor(2^315/10^18) is 66749594872528440074844428317798503581334516323645399060845050244444366430645 // Add 1. - // This can't overflow because the intial numbers are only 127bit due to removing the sign bit. + // This can't overflow because the initial numbers are only 127bit due to removing the sign bit. var overflowed = @addWithOverflow(u128, lhs_lo, 1, &lhs_lo); lhs_hi = blk: { if (overflowed) { @@ -98,7 +362,7 @@ fn mul_and_decimalize(a: u128, b: u128) i128 { const lk = mul_u128(lhs_hi, rhs_hi); const e = ea.hi; - const _a = ea.lo; + // const _a = ea.lo; const g = gf.hi; const f = gf.lo; @@ -216,18 +480,476 @@ fn mul_u128(a: u128, b: u128) U256 { return .{ .hi = hi, .lo = lo }; } -const one_e20: i256 = 100000000000000000000; +// Multiply two 128-bit ints and divide the result by 10^DECIMAL_PLACES +// +// Adapted from https://github.com/nlordell/ethnum-rs +// Copyright (c) 2020 Nicholas Rodrigues Lordello +// Licensed under the Apache License version 2.0 +// +// When translating this to Zig, we often have to use math.shr/shl instead of >>/<< +// This is because casting to the right types for Zig can be kind of tricky. +// See https://github.com/ziglang/zig/issues/7605 +fn div_u256_by_u128(numer: U256, denom: u128) U256 { + const N_UDWORD_BITS: u8 = 128; + const N_UTWORD_BITS: u9 = 256; -const expectEqual = std.testing.expectEqual; + var q: U256 = undefined; + var r: U256 = undefined; + var sr: u8 = undefined; -test "add" { - const dec: RocDec = .{ .num = 0 }; + // special case + if (numer.hi == 0) { + // 0 X + // --- + // 0 X + return .{ + .hi = 0, + .lo = numer.lo / denom, + }; + } - expectEqual(RocDec{ .num = 0 }, dec.add(.{ .num = 0 })); + // numer.hi != 0 + if (denom == 0) { + // K X + // --- + // 0 0 + return .{ + .hi = 0, + .lo = numer.hi / denom, + }; + } else { + // K X + // --- + // 0 K + // NOTE: Modified from `if (d.low() & (d.low() - 1)) == 0`. + if (math.isPowerOfTwo(denom)) { + // if d is a power of 2 + if (denom == 1) { + return numer; + } + + sr = @ctz(u128, denom); + + return .{ + .hi = math.shr(u128, numer.hi, sr), + .lo = math.shl(u128, numer.hi, N_UDWORD_BITS - sr) | math.shr(u128, numer.lo, sr), + }; + } + + // K X + // --- + // 0 K + var denom_leading_zeros = @clz(u128, denom); + var numer_hi_leading_zeros = @clz(u128, numer.hi); + sr = 1 + N_UDWORD_BITS + denom_leading_zeros - numer_hi_leading_zeros; + // 2 <= sr <= N_UTWORD_BITS - 1 + // q.all = n.all << (N_UTWORD_BITS - sr); + // r.all = n.all >> sr; + // #[allow(clippy::comparison_chain)] + if (sr == N_UDWORD_BITS) { + q = .{ + .hi = numer.lo, + .lo = 0, + }; + r = .{ + .hi = 0, + .lo = numer.hi, + }; + } else if (sr < N_UDWORD_BITS) { + // 2 <= sr <= N_UDWORD_BITS - 1 + q = .{ + .hi = math.shl(u128, numer.lo, N_UDWORD_BITS - sr), + .lo = 0, + }; + r = .{ + .hi = math.shr(u128, numer.hi, sr), + .lo = math.shl(u128, numer.hi, N_UDWORD_BITS - sr) | math.shr(u128, numer.lo, sr), + }; + } else { + // N_UDWORD_BITS + 1 <= sr <= N_UTWORD_BITS - 1 + q = .{ + .hi = math.shl(u128, numer.hi, N_UTWORD_BITS - sr) | math.shr(u128, numer.lo, sr - N_UDWORD_BITS), + .lo = math.shl(u128, numer.lo, N_UTWORD_BITS - sr), + }; + r = .{ + .hi = 0, + .lo = math.shr(u128, numer.hi, sr - N_UDWORD_BITS), + }; + } + } + + // Not a special case + // q and r are initialized with: + // q.all = n.all << (N_UTWORD_BITS - sr); + // r.all = n.all >> sr; + // 1 <= sr <= N_UTWORD_BITS - 1 + var carry: u128 = 0; + + while (sr > 0) { + // r:q = ((r:q) << 1) | carry + r.hi = (r.hi << 1) | (r.lo >> (N_UDWORD_BITS - 1)); + r.lo = (r.lo << 1) | (q.hi >> (N_UDWORD_BITS - 1)); + q.hi = (q.hi << 1) | (q.lo >> (N_UDWORD_BITS - 1)); + q.lo = (q.lo << 1) | carry; + + // carry = 0; + // if (r.all >= d.all) + // { + // r.all -= d.all; + // carry = 1; + // } + // NOTE: Modified from `(d - r - 1) >> (N_UTWORD_BITS - 1)` to be an + // **arithmetic** shift. + + var lo: u128 = undefined; + var lo_overflowed: bool = undefined; + var hi: u128 = undefined; + + lo_overflowed = @subWithOverflow(u128, denom, r.lo, &lo); + hi = 0 -% @intCast(u128, @bitCast(u1, lo_overflowed)) -% r.hi; + + lo_overflowed = @subWithOverflow(u128, lo, 1, &lo); + hi = hi -% @intCast(u128, @bitCast(u1, lo_overflowed)); + + // TODO this U256 was originally created by: + // + // ((hi as i128) >> 127).as_u256() + // + // ...however, I can't figure out where that function is defined. + // Maybe it's defined using a macro or something. Anyway, hopefully + // this is what it would do in this scenario. + var s = .{ + .hi = 0, + .lo = math.shr(u128, hi, 127), + }; + + carry = s.lo & 1; + + // var (lo, carry) = r.lo.overflowing_sub(denom & s.lo); + lo_overflowed = @subWithOverflow(u128, r.lo, (denom & s.lo), &lo); + hi = r.hi -% @intCast(u128, @bitCast(u1, lo_overflowed)); + + r = .{ .hi = hi, .lo = lo }; + + sr -= 1; + } + + var hi = (q.hi << 1) | (q.lo >> (127)); + var lo = (q.lo << 1) | carry; + + return .{ .hi = hi, .lo = lo }; } -test "mul" { - var dec1: RocDec = .{ .num = 0 }; +const testing = std.testing; +const expectEqual = testing.expectEqual; +const expectError = testing.expectError; +const expectEqualSlices = testing.expectEqualSlices; +const expect = testing.expect; - expectEqual(RocDec{ .num = 0 }, dec1.mul(.{ .num = 0 })); +test "fromU64" { + var dec = RocDec.fromU64(25); + + try expectEqual(RocDec{ .num = 25000000000000000000 }, dec); +} + +test "fromStr: empty" { + var roc_str = RocStr.init("", 0); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(dec, null); +} + +test "fromStr: 0" { + var roc_str = RocStr.init("0", 1); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = 0 }, dec.?); +} + +test "fromStr: 1" { + var roc_str = RocStr.init("1", 1); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec.one_point_zero, dec.?); +} + +test "fromStr: 123.45" { + var roc_str = RocStr.init("123.45", 6); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = 123450000000000000000 }, dec.?); +} + +test "fromStr: .45" { + var roc_str = RocStr.init(".45", 3); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = 450000000000000000 }, dec.?); +} + +test "fromStr: 0.45" { + var roc_str = RocStr.init("0.45", 4); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = 450000000000000000 }, dec.?); +} + +test "fromStr: 123" { + var roc_str = RocStr.init("123", 3); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = 123000000000000000000 }, dec.?); +} + +test "fromStr: -.45" { + var roc_str = RocStr.init("-.45", 4); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = -450000000000000000 }, dec.?); +} + +test "fromStr: -0.45" { + var roc_str = RocStr.init("-0.45", 5); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = -450000000000000000 }, dec.?); +} + +test "fromStr: -123" { + var roc_str = RocStr.init("-123", 4); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = -123000000000000000000 }, dec.?); +} + +test "fromStr: -123.45" { + var roc_str = RocStr.init("-123.45", 7); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(RocDec{ .num = -123450000000000000000 }, dec.?); +} + +test "fromStr: abc" { + var roc_str = RocStr.init("abc", 3); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(dec, null); +} + +test "fromStr: 123.abc" { + var roc_str = RocStr.init("123.abc", 7); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(dec, null); +} + +test "fromStr: abc.123" { + var roc_str = RocStr.init("abc.123", 7); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(dec, null); +} + +test "fromStr: .123.1" { + var roc_str = RocStr.init(".123.1", 6); + var dec = RocDec.fromStr(roc_str); + + try expectEqual(dec, null); +} + +test "toStr: 123.45" { + var dec: RocDec = .{ .num = 123450000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "123.45"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: -123.45" { + var dec: RocDec = .{ .num = -123450000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "-123.45"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 123.0" { + var dec: RocDec = .{ .num = 123000000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "123.0"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: -123.0" { + var dec: RocDec = .{ .num = -123000000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "-123.0"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 0.45" { + var dec: RocDec = .{ .num = 450000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "0.45"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: -0.45" { + var dec: RocDec = .{ .num = -450000000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "-0.45"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 0.00045" { + var dec: RocDec = .{ .num = 000450000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "0.00045"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: -0.00045" { + var dec: RocDec = .{ .num = -000450000000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "-0.00045"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: -111.123456789" { + var dec: RocDec = .{ .num = -111123456789000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "-111.123456789"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 123.1111111" { + var dec: RocDec = .{ .num = 123111111100000000000 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "123.1111111"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 123.1111111111111 (big str)" { + var dec: RocDec = .{ .num = 123111111111111000000 }; + var res_roc_str = dec.toStr(); + errdefer res_roc_str.?.deinit(); + defer res_roc_str.?.deinit(); + + const res_slice: []const u8 = "123.111111111111"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 123.111111111111444444 (max number of decimal places)" { + var dec: RocDec = .{ .num = 123111111111111444444 }; + var res_roc_str = dec.toStr(); + errdefer res_roc_str.?.deinit(); + defer res_roc_str.?.deinit(); + + const res_slice: []const u8 = "123.111111111111444444"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 12345678912345678912.111111111111111111 (max number of digits)" { + var dec: RocDec = .{ .num = 12345678912345678912111111111111111111 }; + var res_roc_str = dec.toStr(); + errdefer res_roc_str.?.deinit(); + defer res_roc_str.?.deinit(); + + const res_slice: []const u8 = "12345678912345678912.111111111111111111"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "toStr: 0" { + var dec: RocDec = .{ .num = 0 }; + var res_roc_str = dec.toStr(); + + const res_slice: []const u8 = "0.0"[0..]; + try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice()); +} + +test "add: 0" { + var dec: RocDec = .{ .num = 0 }; + + try expectEqual(RocDec{ .num = 0 }, dec.add(.{ .num = 0 })); +} + +test "add: 1" { + var dec: RocDec = .{ .num = 0 }; + + try expectEqual(RocDec{ .num = 1 }, dec.add(.{ .num = 1 })); +} + +test "sub: 0" { + var dec: RocDec = .{ .num = 1 }; + + try expectEqual(RocDec{ .num = 1 }, dec.sub(.{ .num = 0 })); +} + +test "sub: 1" { + var dec: RocDec = .{ .num = 1 }; + + try expectEqual(RocDec{ .num = 0 }, dec.sub(.{ .num = 1 })); +} + +test "mul: by 0" { + var dec: RocDec = .{ .num = 0 }; + + try expectEqual(RocDec{ .num = 0 }, dec.mul(.{ .num = 0 })); +} + +test "mul: by 1" { + var dec: RocDec = RocDec.fromU64(15); + + try expectEqual(RocDec.fromU64(15), dec.mul(RocDec.fromU64(1))); +} + +test "mul: by 2" { + var dec: RocDec = RocDec.fromU64(15); + + try expectEqual(RocDec.fromU64(30), dec.mul(RocDec.fromU64(2))); +} + +test "div: 0 / 2" { + var dec: RocDec = RocDec.fromU64(0); + + try expectEqual(RocDec.fromU64(0), dec.div(RocDec.fromU64(2))); +} + +test "div: 2 / 2" { + var dec: RocDec = RocDec.fromU64(2); + + try expectEqual(RocDec.fromU64(1), dec.div(RocDec.fromU64(2))); +} + +test "div: 20 / 2" { + var dec: RocDec = RocDec.fromU64(20); + + try expectEqual(RocDec.fromU64(10), dec.div(RocDec.fromU64(2))); +} + +test "div: 8 / 5" { + var dec: RocDec = RocDec.fromU64(8); + var res: RocDec = RocDec.fromStr(RocStr.init("1.6", 3)).?; + try expectEqual(res, dec.div(RocDec.fromU64(5))); +} + +test "div: 10 / 3" { + var numer: RocDec = RocDec.fromU64(10); + var denom: RocDec = RocDec.fromU64(3); + + var roc_str = RocStr.init("3.333333333333333333", 20); + errdefer roc_str.deinit(); + defer roc_str.deinit(); + + var res: RocDec = RocDec.fromStr(roc_str).?; + + try expectEqual(res, numer.div(denom)); } diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index 84f7bf6980..ea9ea768fc 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -20,13 +20,9 @@ const Slot = packed enum(u8) { PreviouslyFilled, }; -const MaybeIndexTag = enum { - index, not_found -}; +const MaybeIndexTag = enum { index, not_found }; -const MaybeIndex = union(MaybeIndexTag) { - index: usize, not_found: void -}; +const MaybeIndex = union(MaybeIndexTag) { index: usize, not_found: void }; fn nextSeed(seed: u64) u64 { // TODO is this a valid way to get a new seed? are there better ways? @@ -572,14 +568,6 @@ pub fn dictKeys(dict: RocDict, alignment: Alignment, key_width: usize, value_wid const data_bytes = length * key_width; var ptr = allocateWithRefcount(data_bytes, alignment); - var offset = blk: { - if (alignment.keyFirst()) { - break :blk 0; - } else { - break :blk (dict.capacity() * value_width); - } - }; - i = 0; var copied: usize = 0; while (i < size) : (i += 1) { @@ -621,14 +609,6 @@ pub fn dictValues(dict: RocDict, alignment: Alignment, key_width: usize, value_w const data_bytes = length * value_width; var ptr = allocateWithRefcount(data_bytes, alignment); - var offset = blk: { - if (alignment.keyFirst()) { - break :blk (dict.capacity() * key_width); - } else { - break :blk 0; - } - }; - i = 0; var copied: usize = 0; while (i < size) : (i += 1) { @@ -648,7 +628,7 @@ pub fn dictValues(dict: RocDict, alignment: Alignment, key_width: usize, value_w output.* = RocList{ .bytes = ptr, .length = length }; } -fn doNothing(ptr: Opaque) callconv(.C) void { +fn doNothing(_: Opaque) callconv(.C) void { return; } @@ -768,8 +748,6 @@ pub fn dictWalk( key_width: usize, value_width: usize, accum_width: usize, - inc_key: Inc, - inc_value: Inc, output: Opaque, ) callconv(.C) void { const alignment_u32 = alignment.toU32(); @@ -795,9 +773,7 @@ pub fn dictWalk( caller(data, key, value, b2, b1); - const temp = b1; - b2 = b1; - b1 = temp; + std.mem.swap([*]u8, &b1, &b2); }, else => {}, } diff --git a/compiler/builtins/bitcode/src/hash.zig b/compiler/builtins/bitcode/src/hash.zig index 45414e68f3..10687a3231 100644 --- a/compiler/builtins/bitcode/src/hash.zig +++ b/compiler/builtins/bitcode/src/hash.zig @@ -8,10 +8,9 @@ const str = @import("str.zig"); const mem = std.mem; pub fn wyhash(seed: u64, bytes: ?[*]const u8, length: usize) callconv(.C) u64 { - const stdout = std.io.getStdOut().writer(); - if (bytes) |nonnull| { - return wyhash_hash(seed, nonnull[0..length]); + const slice = nonnull[0..length]; + return wyhash_hash(seed, slice); } else { return 42; } @@ -202,13 +201,13 @@ const expectEqual = std.testing.expectEqual; test "test vectors" { const hash = Wyhash.hash; - expectEqual(hash(0, ""), 0x0); - expectEqual(hash(1, "a"), 0xbed235177f41d328); - expectEqual(hash(2, "abc"), 0xbe348debe59b27c3); - expectEqual(hash(3, "message digest"), 0x37320f657213a290); - expectEqual(hash(4, "abcdefghijklmnopqrstuvwxyz"), 0xd0b270e1d8a7019c); - expectEqual(hash(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x602a1894d3bbfe7f); - expectEqual(hash(6, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x829e9c148b75970e); + try expectEqual(hash(0, ""), 0x0); + try expectEqual(hash(1, "a"), 0xbed235177f41d328); + try expectEqual(hash(2, "abc"), 0xbe348debe59b27c3); + try expectEqual(hash(3, "message digest"), 0x37320f657213a290); + try expectEqual(hash(4, "abcdefghijklmnopqrstuvwxyz"), 0xd0b270e1d8a7019c); + try expectEqual(hash(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x602a1894d3bbfe7f); + try expectEqual(hash(6, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x829e9c148b75970e); } test "test vectors streaming" { @@ -216,19 +215,19 @@ test "test vectors streaming" { for ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") |e| { wh.update(mem.asBytes(&e)); } - expectEqual(wh.final(), 0x602a1894d3bbfe7f); + try expectEqual(wh.final(), 0x602a1894d3bbfe7f); const pattern = "1234567890"; const count = 8; const result = 0x829e9c148b75970e; - expectEqual(Wyhash.hash(6, pattern ** 8), result); + try expectEqual(Wyhash.hash(6, pattern ** 8), result); wh = Wyhash.init(6); var i: u32 = 0; while (i < count) : (i += 1) { wh.update(pattern); } - expectEqual(wh.final(), result); + try expectEqual(wh.final(), result); } test "iterative non-divisible update" { @@ -250,6 +249,6 @@ test "iterative non-divisible update" { } const iterative_hash = wy.final(); - std.testing.expectEqual(iterative_hash, non_iterative_hash); + try std.testing.expectEqual(iterative_hash, non_iterative_hash); } } diff --git a/compiler/builtins/bitcode/src/helpers/grapheme.zig b/compiler/builtins/bitcode/src/helpers/grapheme.zig index 252873cb4e..36cd2c4418 100644 --- a/compiler/builtins/bitcode/src/helpers/grapheme.zig +++ b/compiler/builtins/bitcode/src/helpers/grapheme.zig @@ -42,7 +42,7 @@ pub const BoundClass = enum(u8) { }; test "Bound Class" { - expectEqual(0, @enumToInt(BoundClass.START)); + try expectEqual(0, @enumToInt(BoundClass.START)); } // https://github.com/JuliaStrings/utf8proc/blob/master/utf8proc.c#L261 @@ -112,7 +112,7 @@ fn unsafeCodepointToBoundClass(codepoint: u21) *const BoundClass { } test "unsafeCodepointToBoundClass: valid" { - expectEqual(BoundClass.CONTROL, unsafeCodepointToBoundClass(8).*); + try expectEqual(BoundClass.CONTROL, unsafeCodepointToBoundClass(8).*); } // https://github.com/JuliaStrings/utf8proc/blob/master/utf8proc.c#L242 @@ -125,11 +125,11 @@ fn codepointToBoundClass(codepoint: u21) *const BoundClass { } test "codepointToBoundClass: valid" { - expectEqual(BoundClass.CONTROL, codepointToBoundClass(8).*); + try expectEqual(BoundClass.CONTROL, codepointToBoundClass(8).*); } test "codepointToBoundClass: invalid" { - expectEqual(BoundClass.OTHER, codepointToBoundClass(codepoint_max + 5).*); + try expectEqual(BoundClass.OTHER, codepointToBoundClass(codepoint_max + 5).*); } // https://github.com/JuliaStrings/utf8proc/blob/master/utf8proc.c#L319 diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index f5dbea5dc1..602b680d21 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -12,6 +12,7 @@ const Opaque = ?[*]u8; const Inc = fn (?[*]u8) callconv(.C) void; const IncN = fn (?[*]u8, usize) callconv(.C) void; const Dec = fn (?[*]u8) callconv(.C) void; +const HasTagId = fn (u16, ?[*]u8) callconv(.C) extern struct { matched: bool, data: ?[*]u8 }; pub const RocList = extern struct { bytes: ?[*]u8, @@ -405,11 +406,14 @@ pub fn listKeepOks( before_width: usize, result_width: usize, after_width: usize, + has_tag_id: HasTagId, dec_result: Dec, ) callconv(.C) RocList { + const good_constructor: u16 = 1; + return listKeepResult( list, - RocResult.isOk, + good_constructor, caller, data, inc_n_data, @@ -418,6 +422,7 @@ pub fn listKeepOks( before_width, result_width, after_width, + has_tag_id, dec_result, ); } @@ -432,11 +437,14 @@ pub fn listKeepErrs( before_width: usize, result_width: usize, after_width: usize, + has_tag_id: HasTagId, dec_result: Dec, ) callconv(.C) RocList { + const good_constructor: u16 = 0; + return listKeepResult( list, - RocResult.isErr, + good_constructor, caller, data, inc_n_data, @@ -445,13 +453,14 @@ pub fn listKeepErrs( before_width, result_width, after_width, + has_tag_id, dec_result, ); } pub fn listKeepResult( list: RocList, - is_good_constructor: fn (RocResult) bool, + good_constructor: u16, caller: Caller1, data: Opaque, inc_n_data: IncN, @@ -460,6 +469,7 @@ pub fn listKeepResult( before_width: usize, result_width: usize, after_width: usize, + has_tag_id: HasTagId, dec_result: Dec, ) RocList { if (list.bytes) |source_ptr| { @@ -479,11 +489,14 @@ pub fn listKeepResult( const before_element = source_ptr + (i * before_width); caller(data, before_element, temporary); - const result = utils.RocResult{ .bytes = temporary }; - - const after_element = temporary + @sizeOf(i64); - if (is_good_constructor(result)) { - @memcpy(target_ptr + (kept * after_width), after_element, after_width); + // a record { matched: bool, data: ?[*]u8 } + // for now, that data pointer is just the input `temporary` pointer + // this will change in the future to only return a pointer to the + // payload of the tag + const answer = has_tag_id(good_constructor, temporary); + if (answer.matched) { + const contents = (answer.data orelse unreachable); + @memcpy(target_ptr + (kept * after_width), contents, after_width); kept += 1; } else { dec_result(temporary); @@ -542,9 +555,7 @@ pub fn listWalk( const element = source_ptr + i * element_width; caller(data, element, b2, b1); - const temp = b1; - b2 = b1; - b1 = temp; + std.mem.swap([*]u8, &b1, &b2); } } @@ -591,9 +602,7 @@ pub fn listWalkBackwards( const element = source_ptr + i * element_width; caller(data, element, b2, b1); - const temp = b1; - b2 = b1; - b1 = temp; + std.mem.swap([*]u8, &b1, &b2); } } @@ -610,12 +619,13 @@ pub fn listWalkUntil( accum: Opaque, alignment: u32, element_width: usize, + continue_stop_width: usize, accum_width: usize, + has_tag_id: HasTagId, dec: Dec, output: Opaque, ) callconv(.C) void { // [ Continue a, Stop a ] - const CONTINUE: usize = 0; if (accum_width == 0) { return; @@ -626,9 +636,10 @@ pub fn listWalkUntil( return; } - const bytes_ptr: [*]u8 = utils.alloc(TAG_WIDTH + accum_width, alignment); + const bytes_ptr: [*]u8 = utils.alloc(continue_stop_width, alignment); - @memcpy(bytes_ptr + TAG_WIDTH, accum orelse unreachable, accum_width); + // NOTE: assumes data bytes are the first bytes in a tag + @memcpy(bytes_ptr, accum orelse unreachable, accum_width); if (list.bytes) |source_ptr| { var i: usize = 0; @@ -640,10 +651,12 @@ pub fn listWalkUntil( inc_n_data(data, 1); } - caller(data, element, bytes_ptr + TAG_WIDTH, bytes_ptr); + caller(data, element, bytes_ptr, bytes_ptr); - const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes_ptr)); - if (usizes[0] != 0) { + // [ Continue ..., Stop ] + const tag_id = has_tag_id(0, bytes_ptr); + + if (!tag_id.matched) { // decrement refcount of the remaining items i += 1; while (i < size) : (i += 1) { @@ -654,7 +667,7 @@ pub fn listWalkUntil( } } - @memcpy(output orelse unreachable, bytes_ptr + TAG_WIDTH, accum_width); + @memcpy(output orelse unreachable, bytes_ptr, accum_width); utils.dealloc(bytes_ptr, alignment); } @@ -722,6 +735,28 @@ pub fn listAppend(list: RocList, alignment: u32, element: Opaque, element_width: return output; } +pub fn listSwap( + list: RocList, + alignment: u32, + element_width: usize, + index_1: usize, + index_2: usize, +) callconv(.C) RocList { + const size = list.len(); + if (index_1 >= size or index_2 >= size) { + // Either index out of bounds so we just return + return list; + } + + const newList = list.makeUnique(alignment, element_width); + + if (newList.bytes) |source_ptr| { + swapElements(source_ptr, element_width, index_1, index_2); + } + + return newList; +} + pub fn listDrop( list: RocList, alignment: u32, @@ -759,43 +794,19 @@ pub fn listDrop( } pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C) RocList { - const IntWidth = utils.IntWidth; - - switch (width) { - IntWidth.U8 => { - return helper1(u8, low, high); - }, - IntWidth.U16 => { - return helper1(u16, low, high); - }, - IntWidth.U32 => { - return helper1(u32, low, high); - }, - IntWidth.U64 => { - return helper1(u64, low, high); - }, - IntWidth.U128 => { - return helper1(u128, low, high); - }, - IntWidth.I8 => { - return helper1(i8, low, high); - }, - IntWidth.I16 => { - return helper1(i16, low, high); - }, - IntWidth.I32 => { - return helper1(i32, low, high); - }, - IntWidth.I64 => { - return helper1(i64, low, high); - }, - IntWidth.I128 => { - return helper1(i128, low, high); - }, - IntWidth.Usize => { - return helper1(usize, low, high); - }, - } + return switch (width) { + .U8 => helper1(u8, low, high), + .U16 => helper1(u16, low, high), + .U32 => helper1(u32, low, high), + .U64 => helper1(u64, low, high), + .U128 => helper1(u128, low, high), + .I8 => helper1(i8, low, high), + .I16 => helper1(i16, low, high), + .I32 => helper1(i32, low, high), + .I64 => helper1(i64, low, high), + .I128 => helper1(i128, low, high), + .Usize => helper1(usize, low, high), + }; } fn helper1(comptime T: type, low: Opaque, high: Opaque) RocList { @@ -910,7 +921,7 @@ inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) voi } fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void { - const threshold: comptime usize = 64; + const threshold: usize = 64; var width = width_initial; @@ -936,11 +947,6 @@ fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void { } fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void { - const threshold: comptime usize = 64; - - var buffer_actual: [threshold]u8 = undefined; - var buffer: [*]u8 = buffer_actual[0..]; - var element_at_i = source_ptr + (index_1 * element_width); var element_at_j = source_ptr + (index_2 * element_width); @@ -1015,7 +1021,23 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt return output; } -// input: RocList, +pub fn listSetInPlace( + bytes: ?[*]u8, + index: usize, + element: Opaque, + element_width: usize, + dec: Dec, +) callconv(.C) ?[*]u8 { + // INVARIANT: bounds checking happens on the roc side + // + // at the time of writing, the function is implemented roughly as + // `if inBounds then LowLevelListGet input index item else input` + // so we don't do a bounds check here. Hence, the list is also non-empty, + // because inserting into an empty list is always out of bounds + + return listSetInPlaceHelp(bytes, index, element, element_width, dec); +} + pub fn listSet( bytes: ?[*]u8, length: usize, @@ -1034,23 +1056,32 @@ pub fn listSet( const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes)); if ((ptr - 1)[0] == utils.REFCOUNT_ONE) { - - // the element we will replace - var element_at_index = (bytes orelse undefined) + (index * element_width); - - // decrement its refcount - dec(element_at_index); - - // copy in the new element - @memcpy(element_at_index, element orelse undefined, element_width); - - return bytes; + return listSetInPlaceHelp(bytes, index, element, element_width, dec); } else { - return listSetClone(bytes, length, alignment, index, element, element_width, dec); + return listSetImmutable(bytes, length, alignment, index, element, element_width, dec); } } -inline fn listSetClone( +inline fn listSetInPlaceHelp( + bytes: ?[*]u8, + index: usize, + element: Opaque, + element_width: usize, + dec: Dec, +) ?[*]u8 { + // the element we will replace + var element_at_index = (bytes orelse undefined) + (index * element_width); + + // decrement its refcount + dec(element_at_index); + + // copy in the new element + @memcpy(element_at_index, element orelse undefined, element_width); + + return bytes; +} + +inline fn listSetImmutable( old_bytes: ?[*]u8, length: usize, alignment: u32, @@ -1059,8 +1090,6 @@ inline fn listSetClone( element_width: usize, dec: Dec, ) ?[*]u8 { - @setCold(true); - const data_bytes = length * element_width; var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 46285ed163..71e0ff8df8 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -30,6 +30,8 @@ comptime { exportListFn(list.listConcat, "concat"); exportListFn(list.listDrop, "drop"); exportListFn(list.listSet, "set"); + exportListFn(list.listSetInPlace, "set_in_place"); + exportListFn(list.listSwap, "swap"); } // Dict Module diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 4423d83df8..a52f4dd33b 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -17,7 +17,7 @@ const InPlace = packed enum(u8) { const SMALL_STR_MAX_LENGTH = small_string_size - 1; const small_string_size = 2 * @sizeOf(usize); -const blank_small_string: [16]u8 = init_blank_small_string(small_string_size); +const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size); fn init_blank_small_string(comptime n: usize) [n]u8 { var prime_list: [n]u8 = undefined; @@ -85,12 +85,6 @@ pub const RocStr = extern struct { } } - pub fn toSlice(self: RocStr) []u8 { - const str_bytes_ptr: [*]u8 = self.str_bytes orelse unreachable; - const str_bytes: []u8 = str_bytes_ptr[0..self.str_len]; - return str_bytes; - } - // This takes ownership of the pointed-to bytes if they won't fit in a // small string, and returns a (pointer, len) tuple which points to them. pub fn withCapacity(length: usize) RocStr { @@ -203,8 +197,8 @@ pub const RocStr = extern struct { return result; } + // NOTE: returns false for empty string! pub fn isSmallStr(self: RocStr) bool { - // NOTE: returns False for empty string! return @bitCast(isize, self.str_len) < 0; } @@ -223,6 +217,82 @@ pub const RocStr = extern struct { return self.len() == 0; } + // If a string happens to be null-terminated already, then we can pass its + // bytes directly to functions (e.g. for opening files) that require + // null-terminated strings. Otherwise, we need to allocate and copy a new + // null-terminated string, which has a much higher performance cost! + fn isNullTerminated(self: RocStr) bool { + const len = self.len(); + const longest_small_str = @sizeOf(RocStr) - 1; + + // NOTE: We want to compare length here, *NOT* check for is_small_str! + // This is because we explicitly want the empty string to be handled in + // this branch, even though the empty string is not a small string. + // + // (The other branch dereferences the bytes pointer, which is not safe + // to do for the empty string.) + if (len <= longest_small_str) { + // If we're a small string, then usually the next byte after the + // end of the string will be zero. (Small strings set all their + // unused bytes to 0, so that comparison for equality can be fast.) + // + // However, empty strings are *not* null terminated, so if this is + // empty, it should return false. + // + // Also, if we are exactly a maximum-length small string, + // then the next byte is off the end of the struct; + // in that case, we are also not null-terminated! + return len != 0 and len != longest_small_str; + } else { + // This is a big string, and it's not empty, so we can safely + // dereference the pointer. + const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.str_bytes)); + const capacity_or_refcount: isize = (ptr - 1)[0]; + + // If capacity_or_refcount is positive, then it's a capacity value. + // + // If we have excess capacity, then we can safely read the next + // byte after the end of the string. Maybe it happens to be zero! + if (capacity_or_refcount > @intCast(isize, len)) { + return self.str_bytes[len] == 0; + } else { + // This string was refcounted or immortal; we can't safely read + // the next byte, so assume the string is not null-terminated. + return false; + } + } + } + + // Returns (@sizeOf(RocStr) - 1) for small strings and the empty string. + // Returns 0 for refcounted stirngs and immortal strings. + // Returns the stored capacity value for all other strings. + pub fn capacity(self: RocStr) usize { + const len = self.len(); + const longest_small_str = @sizeOf(RocStr) - 1; + + if (len <= longest_small_str) { + // Note that although empty strings technically have the full + // capacity of a small string available, they aren't marked as small + // strings, so if you want to make use of that capacity, you need + // to first change its flag to mark it as a small string! + return longest_small_str; + } else { + const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.str_bytes)); + const capacity_or_refcount: isize = (ptr - 1)[0]; + + if (capacity_or_refcount > 0) { + // If capacity_or_refcount is positive, that means it's a + // capacity value. + return capacity_or_refcount; + } else { + // This is either a refcount or else this big string is stored + // in a readonly section; either way, it has no capacity, + // because we cannot mutate it in-place! + return 0; + } + } + } + pub fn isUnique(self: RocStr) bool { // the empty list is unique (in the sense that copying it will not leak memory) if (self.isEmpty()) { @@ -240,15 +310,13 @@ pub const RocStr = extern struct { } pub fn asSlice(self: RocStr) []u8 { - // Since this conditional would be prone to branch misprediction, - // make sure it will compile to a cmov. return self.asU8ptr()[0..self.len()]; } pub fn asU8ptr(self: RocStr) [*]u8 { // Since this conditional would be prone to branch misprediction, // make sure it will compile to a cmov. - return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([16]u8, self)) else (@ptrCast([*]u8, self.str_bytes)); + return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes)); } // Given a pointer to some bytes, write the first (len) bytes of this @@ -273,7 +341,7 @@ pub const RocStr = extern struct { const str2_ptr: [*]u8 = &str2; var roc_str2 = RocStr.init(str2_ptr, str2_len); - expect(roc_str1.eq(roc_str2)); + try expect(roc_str1.eq(roc_str2)); roc_str1.deinit(); roc_str2.deinit(); @@ -295,7 +363,7 @@ pub const RocStr = extern struct { roc_str2.deinit(); } - expect(!roc_str1.eq(roc_str2)); + try expect(!roc_str1.eq(roc_str2)); } test "RocStr.eq: not equal same length" { @@ -314,7 +382,7 @@ pub const RocStr = extern struct { roc_str2.deinit(); } - expect(!roc_str1.eq(roc_str2)); + try expect(!roc_str1.eq(roc_str2)); } }; @@ -449,8 +517,8 @@ test "strSplitInPlace: no delimiter" { delimiter.deinit(); } - expectEqual(array.len, expected.len); - expect(array[0].eq(expected[0])); + try expectEqual(array.len, expected.len); + try expect(array[0].eq(expected[0])); } test "strSplitInPlace: empty end" { @@ -490,10 +558,10 @@ test "strSplitInPlace: empty end" { delimiter.deinit(); } - expectEqual(array.len, expected.len); - expect(array[0].eq(expected[0])); - expect(array[1].eq(expected[1])); - expect(array[2].eq(expected[2])); + try expectEqual(array.len, expected.len); + try expect(array[0].eq(expected[0])); + try expect(array[1].eq(expected[1])); + try expect(array[2].eq(expected[2])); } test "strSplitInPlace: delimiter on sides" { @@ -532,10 +600,10 @@ test "strSplitInPlace: delimiter on sides" { delimiter.deinit(); } - expectEqual(array.len, expected.len); - expect(array[0].eq(expected[0])); - expect(array[1].eq(expected[1])); - expect(array[2].eq(expected[2])); + try expectEqual(array.len, expected.len); + try expect(array[0].eq(expected[0])); + try expect(array[1].eq(expected[1])); + try expect(array[2].eq(expected[2])); } test "strSplitInPlace: three pieces" { @@ -573,10 +641,10 @@ test "strSplitInPlace: three pieces" { delimiter.deinit(); } - expectEqual(expected_array.len, array.len); - expect(array[0].eq(expected_array[0])); - expect(array[1].eq(expected_array[1])); - expect(array[2].eq(expected_array[2])); + try expectEqual(expected_array.len, array.len); + try expect(array[0].eq(expected_array[0])); + try expect(array[1].eq(expected_array[1])); + try expect(array[2].eq(expected_array[2])); } // This is used for `Str.split : Str, Str -> Array Str @@ -639,7 +707,7 @@ test "countSegments: long delimiter" { } const segments_count = countSegments(str, delimiter); - expectEqual(segments_count, 1); + try expectEqual(segments_count, 1); } test "countSegments: delimiter at start" { @@ -658,7 +726,7 @@ test "countSegments: delimiter at start" { const segments_count = countSegments(str, delimiter); - expectEqual(segments_count, 2); + try expectEqual(segments_count, 2); } test "countSegments: delimiter interspered" { @@ -677,7 +745,7 @@ test "countSegments: delimiter interspered" { const segments_count = countSegments(str, delimiter); - expectEqual(segments_count, 3); + try expectEqual(segments_count, 3); } // Str.countGraphemeClusters @@ -721,7 +789,7 @@ fn rocStrFromLiteral(bytes_arr: *const []u8) RocStr {} test "countGraphemeClusters: empty string" { const count = countGraphemeClusters(RocStr.empty()); - expectEqual(count, 0); + try expectEqual(count, 0); } test "countGraphemeClusters: ascii characters" { @@ -731,7 +799,7 @@ test "countGraphemeClusters: ascii characters" { defer str.deinit(); const count = countGraphemeClusters(str); - expectEqual(count, 4); + try expectEqual(count, 4); } test "countGraphemeClusters: utf8 characters" { @@ -741,7 +809,7 @@ test "countGraphemeClusters: utf8 characters" { defer str.deinit(); const count = countGraphemeClusters(str); - expectEqual(count, 3); + try expectEqual(count, 3); } test "countGraphemeClusters: emojis" { @@ -751,7 +819,7 @@ test "countGraphemeClusters: emojis" { defer str.deinit(); const count = countGraphemeClusters(str); - expectEqual(count, 3); + try expectEqual(count, 3); } test "countGraphemeClusters: emojis and ut8 characters" { @@ -761,7 +829,7 @@ test "countGraphemeClusters: emojis and ut8 characters" { defer str.deinit(); const count = countGraphemeClusters(str); - expectEqual(count, 6); + try expectEqual(count, 6); } test "countGraphemeClusters: emojis, ut8, and ascii characters" { @@ -771,7 +839,7 @@ test "countGraphemeClusters: emojis, ut8, and ascii characters" { defer str.deinit(); const count = countGraphemeClusters(str); - expectEqual(count, 10); + try expectEqual(count, 10); } // Str.startsWith @@ -821,27 +889,27 @@ pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool { test "startsWithCodePoint: ascii char" { const whole = RocStr.init("foobar", 6); const prefix = 'f'; - expect(startsWithCodePoint(whole, prefix)); + try expect(startsWithCodePoint(whole, prefix)); } test "startsWithCodePoint: emoji" { const yes = RocStr.init("💖foobar", 10); const no = RocStr.init("foobar", 6); const prefix = '💖'; - expect(startsWithCodePoint(yes, prefix)); - expect(!startsWithCodePoint(no, prefix)); + try expect(startsWithCodePoint(yes, prefix)); + try expect(!startsWithCodePoint(no, prefix)); } test "startsWith: foo starts with fo" { const foo = RocStr.init("foo", 3); const fo = RocStr.init("fo", 2); - expect(startsWith(foo, fo)); + try expect(startsWith(foo, fo)); } test "startsWith: 123456789123456789 starts with 123456789123456789" { const str = RocStr.init("123456789123456789", 18); defer str.deinit(); - expect(startsWith(str, str)); + try expect(startsWith(str, str)); } test "startsWith: 12345678912345678910 starts with 123456789123456789" { @@ -850,7 +918,7 @@ test "startsWith: 12345678912345678910 starts with 123456789123456789" { const prefix = RocStr.init("123456789123456789", 18); defer prefix.deinit(); - expect(startsWith(str, prefix)); + try expect(startsWith(str, prefix)); } // Str.endsWith @@ -882,13 +950,13 @@ test "endsWith: foo ends with oo" { defer foo.deinit(); defer oo.deinit(); - expect(endsWith(foo, oo)); + try expect(endsWith(foo, oo)); } test "endsWith: 123456789123456789 ends with 123456789123456789" { const str = RocStr.init("123456789123456789", 18); defer str.deinit(); - expect(endsWith(str, str)); + try expect(endsWith(str, str)); } test "endsWith: 12345678912345678910 ends with 345678912345678910" { @@ -897,7 +965,7 @@ test "endsWith: 12345678912345678910 ends with 345678912345678910" { defer str.deinit(); defer suffix.deinit(); - expect(endsWith(str, suffix)); + try expect(endsWith(str, suffix)); } test "endsWith: hello world ends with world" { @@ -906,17 +974,18 @@ test "endsWith: hello world ends with world" { defer str.deinit(); defer suffix.deinit(); - expect(endsWith(str, suffix)); + try expect(endsWith(str, suffix)); } // Str.concat -pub fn strConcatC(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr { - return @call(.{ .modifier = always_inline }, strConcat, .{ result_in_place, arg1, arg2 }); +pub fn strConcatC(arg1: RocStr, arg2: RocStr) callconv(.C) RocStr { + return @call(.{ .modifier = always_inline }, strConcat, .{ arg1, arg2 }); } -fn strConcat(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { +fn strConcat(arg1: RocStr, arg2: RocStr) RocStr { if (arg1.isEmpty()) { // the second argument is borrowed, so we must increment its refcount before returning + const result_in_place = InPlace.Clone; return RocStr.clone(result_in_place, arg2); } else if (arg2.isEmpty()) { // the first argument is owned, so we can return it without cloning @@ -974,11 +1043,11 @@ test "RocStr.concat: small concat small" { roc_str3.deinit(); } - const result = strConcat(InPlace.Clone, roc_str1, roc_str2); + const result = strConcat(roc_str1, roc_str2); defer result.deinit(); - expect(roc_str3.eq(result)); + try expect(roc_str3.eq(result)); } pub const RocListStr = extern struct { @@ -1057,7 +1126,7 @@ test "RocStr.joinWith: result is big" { defer result.deinit(); - expect(roc_result.eq(result)); + try expect(roc_result.eq(result)); } // Str.toBytes @@ -1191,8 +1260,8 @@ fn validateUtf8BytesX(str: RocList) FromUtf8Result { return fromUtf8(str); } -fn expectOk(result: FromUtf8Result) void { - expectEqual(result.is_ok, true); +fn expectOk(result: FromUtf8Result) !void { + try expectEqual(result.is_ok, true); } fn sliceHelp(bytes: [*]const u8, length: usize) RocList { @@ -1217,7 +1286,7 @@ test "validateUtf8Bytes: ascii" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectOk(validateUtf8BytesX(list)); + try expectOk(validateUtf8BytesX(list)); } test "validateUtf8Bytes: unicode œ" { @@ -1225,7 +1294,7 @@ test "validateUtf8Bytes: unicode œ" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectOk(validateUtf8BytesX(list)); + try expectOk(validateUtf8BytesX(list)); } test "validateUtf8Bytes: unicode ∆" { @@ -1233,7 +1302,7 @@ test "validateUtf8Bytes: unicode ∆" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectOk(validateUtf8BytesX(list)); + try expectOk(validateUtf8BytesX(list)); } test "validateUtf8Bytes: emoji" { @@ -1241,7 +1310,7 @@ test "validateUtf8Bytes: emoji" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectOk(validateUtf8BytesX(list)); + try expectOk(validateUtf8BytesX(list)); } test "validateUtf8Bytes: unicode ∆ in middle of array" { @@ -1249,15 +1318,15 @@ test "validateUtf8Bytes: unicode ∆ in middle of array" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectOk(validateUtf8BytesX(list)); + try expectOk(validateUtf8BytesX(list)); } -fn expectErr(list: RocList, index: usize, err: Utf8DecodeError, problem: Utf8ByteProblem) void { +fn expectErr(list: RocList, index: usize, err: Utf8DecodeError, problem: Utf8ByteProblem) !void { const str_ptr = @ptrCast([*]u8, list.bytes); const str_len = list.length; - expectError(err, numberOfNextCodepointBytes(str_ptr, str_len, index)); - expectEqual(toErrUtf8ByteResponse(index, problem), validateUtf8Bytes(str_ptr, str_len)); + try expectError(err, numberOfNextCodepointBytes(str_ptr, str_len, index)); + try expectEqual(toErrUtf8ByteResponse(index, problem), validateUtf8Bytes(str_ptr, str_len)); } test "validateUtf8Bytes: invalid start byte" { @@ -1266,7 +1335,7 @@ test "validateUtf8Bytes: invalid start byte" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 2, error.Utf8InvalidStartByte, Utf8ByteProblem.InvalidStartByte); + try expectErr(list, 2, error.Utf8InvalidStartByte, Utf8ByteProblem.InvalidStartByte); } test "validateUtf8Bytes: unexpected eof for 2 byte sequence" { @@ -1275,7 +1344,7 @@ test "validateUtf8Bytes: unexpected eof for 2 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); + try expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); } test "validateUtf8Bytes: expected continuation for 2 byte sequence" { @@ -1284,7 +1353,7 @@ test "validateUtf8Bytes: expected continuation for 2 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); + try expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); } test "validateUtf8Bytes: unexpected eof for 3 byte sequence" { @@ -1293,7 +1362,7 @@ test "validateUtf8Bytes: unexpected eof for 3 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); + try expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); } test "validateUtf8Bytes: expected continuation for 3 byte sequence" { @@ -1302,7 +1371,7 @@ test "validateUtf8Bytes: expected continuation for 3 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); + try expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); } test "validateUtf8Bytes: unexpected eof for 4 byte sequence" { @@ -1311,7 +1380,7 @@ test "validateUtf8Bytes: unexpected eof for 4 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); + try expectErr(list, 3, error.UnexpectedEof, Utf8ByteProblem.UnexpectedEndOfSequence); } test "validateUtf8Bytes: expected continuation for 4 byte sequence" { @@ -1320,7 +1389,7 @@ test "validateUtf8Bytes: expected continuation for 4 byte sequence" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); + try expectErr(list, 3, error.Utf8ExpectedContinuation, Utf8ByteProblem.ExpectedContinuation); } test "validateUtf8Bytes: overlong" { @@ -1329,7 +1398,7 @@ test "validateUtf8Bytes: overlong" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8OverlongEncoding, Utf8ByteProblem.OverlongEncoding); + try expectErr(list, 3, error.Utf8OverlongEncoding, Utf8ByteProblem.OverlongEncoding); } test "validateUtf8Bytes: codepoint out too large" { @@ -1338,7 +1407,7 @@ test "validateUtf8Bytes: codepoint out too large" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8CodepointTooLarge, Utf8ByteProblem.CodepointTooLarge); + try expectErr(list, 3, error.Utf8CodepointTooLarge, Utf8ByteProblem.CodepointTooLarge); } test "validateUtf8Bytes: surrogate halves" { @@ -1347,5 +1416,5 @@ test "validateUtf8Bytes: surrogate halves" { const ptr: [*]const u8 = @ptrCast([*]const u8, raw); const list = sliceHelp(ptr, raw.len); - expectErr(list, 3, error.Utf8EncodesSurrogateHalf, Utf8ByteProblem.EncodesSurrogateHalf); + try expectErr(list, 3, error.Utf8EncodesSurrogateHalf, Utf8ByteProblem.EncodesSurrogateHalf); } diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index ee08aeeacf..b679c20153 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -20,18 +20,18 @@ comptime { } } -fn testing_roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { +fn testing_roc_alloc(size: usize, _: u32) callconv(.C) ?*c_void { return @ptrCast(?*c_void, std.testing.allocator.alloc(u8, size) catch unreachable); } -fn testing_roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void { +fn testing_roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, _: u32) callconv(.C) ?*c_void { const ptr = @ptrCast([*]u8, @alignCast(16, c_ptr)); const slice = ptr[0..old_size]; return @ptrCast(?*c_void, std.testing.allocator.realloc(slice, new_size) catch unreachable); } -fn testing_roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { +fn testing_roc_dealloc(c_ptr: *c_void, _: u32) callconv(.C) void { const ptr = @ptrCast([*]u8, @alignCast(16, c_ptr)); std.testing.allocator.destroy(ptr); @@ -53,8 +53,8 @@ pub const Inc = fn (?[*]u8) callconv(.C) void; pub const IncN = fn (?[*]u8, u64) callconv(.C) void; pub const Dec = fn (?[*]u8) callconv(.C) void; -const REFCOUNT_MAX_ISIZE: comptime isize = 0; -pub const REFCOUNT_ONE_ISIZE: comptime isize = std.math.minInt(isize); +const REFCOUNT_MAX_ISIZE: isize = 0; +pub const REFCOUNT_ONE_ISIZE: isize = std.math.minInt(isize); pub const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE); pub const IntWidth = enum(u8) { @@ -71,44 +71,6 @@ pub const IntWidth = enum(u8) { Usize, }; -pub fn intWidth(width: IntWidth) anytype { - switch (width) { - IntWidth.U8 => { - return u8; - }, - IntWidth.U16 => { - return u16; - }, - IntWidth.U32 => { - return u32; - }, - IntWidth.U64 => { - return u64; - }, - IntWidth.U128 => { - return u128; - }, - IntWidth.I8 => { - return i8; - }, - IntWidth.I16 => { - return i16; - }, - IntWidth.I32 => { - return i32; - }, - IntWidth.I64 => { - return i64; - }, - IntWidth.I128 => { - return i128; - }, - IntWidth.Usize => { - return usize; - }, - } -} - pub fn decref( bytes_or_null: ?[*]u8, data_bytes: usize, @@ -148,7 +110,7 @@ pub fn allocateWithRefcount( data_bytes: usize, alignment: u32, ) [*]u8 { - comptime const result_in_place = false; + const result_in_place = false; switch (alignment) { 16 => { diff --git a/compiler/builtins/build.rs b/compiler/builtins/build.rs index c157c1cef7..05f5aa284c 100644 --- a/compiler/builtins/build.rs +++ b/compiler/builtins/build.rs @@ -9,6 +9,23 @@ use std::str; 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:rustc-env=BUILTINS_O={}", dest_obj); + + // 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 + // that require having zig installed. + if env::var_os("NO_ZIG_INSTALLED").is_some() { + // We still need to do the other things before this point, because + // setting the env vars is needed for other parts of the build. + return; + } + + let big_sur_path = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib"; + let use_build_script = Path::new(big_sur_path).exists(); // "." is relative to where "build.rs" is let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap(); @@ -16,35 +33,31 @@ fn main() { let src_obj_path = bitcode_path.join("builtins.o"); let src_obj = src_obj_path.to_str().expect("Invalid src object path"); - println!("Compiling zig object to: {}", src_obj); - run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]); + let dest_ir_path = bitcode_path.join("builtins.ll"); + let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path"); + + if use_build_script { + println!("Compiling zig object & ir to: {} and {}", src_obj, dest_ir); + run_command_with_no_args(&bitcode_path, "./build.sh"); + } else { + println!("Compiling zig object to: {}", src_obj); + run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]); + + println!("Compiling ir to: {}", dest_ir); + run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]); + } - 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!("Moving zig object to: {}", dest_obj); run_command(&bitcode_path, "mv", &[src_obj, dest_obj]); - let dest_ir_path = bitcode_path.join("builtins.ll"); - let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path"); - println!("Compiling ir to: {}", dest_ir); - - run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]); - - let dest_bc_path = Path::new(&out_dir).join("builtins.bc"); + let dest_bc_path = bitcode_path.join("builtins.bc"); let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path"); println!("Compiling bitcode to: {}", dest_bc); - run_command( - build_script_dir_path, - "llvm-as-10", - &[dest_ir, "-o", dest_bc], - ); + run_command(build_script_dir_path, "llvm-as", &[dest_ir, "-o", dest_bc]); - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc); - println!("cargo:rustc-env=BUILTINS_O={}", dest_obj); get_zig_files(bitcode_path.as_path(), &|path| { let path: &Path = path; println!( @@ -79,6 +92,25 @@ where } } +fn run_command_with_no_args>(path: P, command_str: &str) { + let output_result = Command::new(OsStr::new(&command_str)) + .current_dir(path) + .output(); + match output_result { + Ok(output) => match output.status.success() { + true => (), + false => { + let error_str = match str::from_utf8(&output.stderr) { + Ok(stderr) => stderr.to_string(), + Err(_) => format!("Failed to run \"{}\"", command_str), + }; + panic!("{} failed: {}", command_str, error_str); + } + }, + Err(reason) => panic!("{} failed: {}", command_str, reason), + } +} + fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> { if dir.is_dir() { for entry in fs::read_dir(dir)? { diff --git a/compiler/builtins/docs/Bool.roc b/compiler/builtins/docs/Bool.roc index 601a3d491f..077909e84d 100644 --- a/compiler/builtins/docs/Bool.roc +++ b/compiler/builtins/docs/Bool.roc @@ -55,36 +55,36 @@ and : Bool, Bool -> Bool ## ## In some languages, `&&` and `||` are special-cased in the compiler to skip ## evaluating the expression after the operator under certain circumstances. -## # In Roc, this is not the case. See the performance notes for #Bool.and for details. +## # In Roc, this is not the case. See the performance notes for [Bool.and] for details. or : Bool, Bool -> Bool ## Exclusive or xor : Bool, Bool -> Bool +# TODO: removed `'` from signature because parser does not support it yet +# Original signature: `isEq : 'val, 'val -> Bool` ## Returns `True` if the two values are *structurally equal*, and `False` otherwise. ## ## `a == b` is shorthand for `Bool.isEq a b` ## ## Structural equality works as follows: ## -## 1. #Int and #Float values are equal if their numbers are equal. -## 2. Records are equal if all their fields are equal. -## 3. Global tags are equal if they are the same tag, and also their contents (if any) are equal. -## 4. Private tags are equal if they are the same tag, in the same module, and also their contents (if any) are equal. -## 5. Collections (#String, #List, #Map, #Set, and #Bytes) are equal if they are the same length, and also all their corresponding elements are equal. +## 1. Global tags are equal if they are the same tag, and also their contents (if any) are equal. +## 2. Private tags are equal if they are the same tag, in the same module, and also their contents (if any) are equal. +## 3. Records are equal if all their fields are equal. +## 4. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal. +## 5. [Num] values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `False`. See [Num.isNaN] for more about *NaN*. ## ## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not ## accept arguments whose types contain functions. -# TODO: removed `'` from signature because parser does not support it yet -# Original signature: `isEq : 'val, 'val -> Bool` isEq : val, val -> Bool -## Calls #eq on the given values, then calls #not on the result. +# TODO: removed `'` from signature because parser does not support it yet +# Original signature: `isNotEq : 'val, 'val -> Bool` +## Calls [isEq] on the given values, then calls [not] on the result. ## ## `a != b` is shorthand for `Bool.isNotEq a b` ## ## Note that `isNotEq` takes `'val` instead of `val`, which means `isNotEq` does not ## accept arguments whose types contain functions. -# TODO: removed `'` from signature because parser does not support it yet -# Original signature: `isNotEq : 'val, 'val -> Bool` isNotEq : val, val -> Bool diff --git a/compiler/builtins/docs/Defaults.roc b/compiler/builtins/docs/Defaults.roc deleted file mode 100644 index 046ba7dc3f..0000000000 --- a/compiler/builtins/docs/Defaults.roc +++ /dev/null @@ -1,7 +0,0 @@ -interface Defaults - exposes [] - imports [ - Dict.{ Dict }, - Set.{ Set }, - Num.{ Num, Int, Float } - ] diff --git a/compiler/builtins/docs/Dict.roc b/compiler/builtins/docs/Dict.roc index 9486764194..3aad9e4eee 100644 --- a/compiler/builtins/docs/Dict.roc +++ b/compiler/builtins/docs/Dict.roc @@ -19,3 +19,15 @@ map : Dict beforeKey beforeValue, ({ key: beforeKey, value: beforeValue } -> { key: afterKey, value: afterValue }) -> Dict afterKey afterValue + +# DESIGN NOTES: The reason for panicking when given NaN is that: +# * If we allowed NaN in, Dict.insert would no longer be idempotent. +# * If we allowed NaN but overrode its semantics to make it feel like "NaN == NaN" we'd need isNaN checks in all hashing operations as well as all equality checks (during collision detection), not just insert. This would be much worse for performance than panicking on insert, which only requires one extra conditional on insert. +# * It's obviously invalid; the whole point of NaN is that an error occurred. Giving a runtime error notifies you when this problem happens. Giving it only on insert is the best for performance, because it means you aren't paying for isNaN checks on lookups as well. + +# TODO: removed `'` from signature because parser does not support it yet +# Original signature: insert : Dict 'key val, 'key, val -> Dict 'key val +## Make sure never to insert a key of *NaN* into a [Dict]! Because *NaN* is +## defined to be unequal to *NaN*, inserting a *NaN* key results in an entry +## that can never be retrieved or removed from the [Dict]. +insert : Dict key val, key, val -> Dict key val diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index 02866d5d37..2f3248da4b 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -62,7 +62,7 @@ interface List2 ## the same type. If you want to put a mix of #Int and #Str values into a list, try this: ## ## ``` -## mixedList : List [ IntElem Int, StrElem Str ]* +## mixedList : List [ IntElem I64, StrElem Str ]* ## mixedList = [ IntElem 1, IntElem 2, StrElem "a", StrElem "b" ] ## ``` ## @@ -180,7 +180,7 @@ interface List2 ## we can free it immediately because there are no other refcounts. However, ## in the case of `lists`, we have to iterate through the list and decrement ## the refcounts of each of its contained lists - because they, too, have -## refcounts! Importantly, beacuse the first element had its refcount incremented +## refcounts! Importantly, because the first element had its refcount incremented ## because the function returned `first`, that element will actually end up ## *not* getting freed at the end - but all the others will be. ## @@ -232,9 +232,28 @@ reverse : List elem -> List elem ## Sorts a list using a function which specifies how two elements are ordered. ## -## +## When sorting by numeric values, it's more efficient to use [sortAsc] or +## [sortDesc] instead. sort : List elem, (elem, elem -> [ Lt, Eq, Gt ]) -> List elem +## Sorts a list in ascending order (lowest to highest), using a function which +## specifies a way to represent each element as a number. +## +## This is more efficient than [sort] because it skips +## calculating the `[ Lt, Eq, Gt ]` value and uses the number directly instead. +## +## To sort in descending order (highest to lowest), use [List.sortDesc] instead. +sortAsc : List elem, (elem -> Num *) -> List elem + +## Sorts a list in descending order (highest to lowest), using a function which +## specifies a way to represent each element as a number. +## +## This is more efficient than [sort] because it skips +## calculating the `[ Lt, Eq, Gt ]` value and uses the number directly instead. +## +## To sort in ascending order (lowest to highest), use [List.sortAsc] instead. +sortDesc : List elem, (elem -> Num *) -> List elem + ## Convert each element in the list to something new, by calling a conversion ## function on each of them. Then return a new list of the converted values. ## @@ -248,7 +267,7 @@ map : List before, (before -> after) -> List after ## This works like #List.map, except it also passes the index ## of the element to the conversion function. -mapWithIndex : List before, (before, Int -> after) -> List after +mapWithIndex : List before, (before, Nat -> after) -> List after ## This works like #List.map, except at any time you can return `Err` to ## cancel the entire operation immediately, and return that #Err. @@ -279,7 +298,7 @@ update : List elem, Nat, (elem -> elem) -> List elem ## A more flexible version of #List.update, which returns an "updater" function ## that lets you delay performing the update until later. -updater : List elem, Nat -> { elem, new : elem -> List elem } +updater : List elem, Nat -> { elem, new : (elem -> List elem) } ## If all the elements in the list are #Ok, return a new list containing the ## contents of those #Ok tags. If any elements are #Err, return #Err. @@ -629,7 +648,7 @@ walk : List elem, { start : state, step : (state, elem -> state) } -> state ## Note that in other languages, `walkBackwards` is sometimes called `reduceRight`, ## `fold`, `foldRight`, or `foldr`. -walkBackwards : List elem, { start : state, step : (state, elem -> state ]) } -> state +walkBackwards : List elem, { start : state, step : (state, elem -> state) } -> state ## Same as #List.walk, except you can stop walking early. ## diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index 813fe34e23..db8d41bd7f 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -9,13 +9,24 @@ interface Num2 ## This is useful for functions that can work on either, for example #Num.add, whose type is: ## ## ``` -## add : Num range, Num range -> Num range +## add : Num a, Num a -> Num a ## ``` ## -## The number 1.5 technically has the type `Num FloatingPoint`, so when you pass two of them to `Num.add`, the answer you get is `3.0 : Num FloatingPoint`. +## The number 1.5 technically has the type `Num (Fraction *)`, so when you pass +## two of them to [Num.add], the answer you get is `3.0 : Num (Fraction *)`. +# +## Similarly, the number 0x1 (that is, the integer 1 in hexadecimal notation) +## technically has the type `Num (Integer *)`, so when you pass two of them to +## [Num.add], the answer you get is `2 : Num (Integer *)`. ## -## The type #Float is defined to be an alias for `Num FloatingPoint`, so `3.0 : Num FloatingPoint` is the same answer as `3.0 : Float`. # # Similarly, the number 1 technically has the type `Num Integer`, so when you pass two of them to `Num.add`, the answer you get is `2 : Num Integer`. # # The type #Int is defined to be an alias for `Num Integer`, so `2 : Num Integer` is the same answer as `2 : Int`. # -## In this way, the `Num` type makes it possible to have `1 + 1` return `2 : Int` and `1.5 + 1.5` return `3.0 : Float`. +## The type [`Frac a`](#Frac) is defined to be an alias for `Num (Fraction a)`, +## so `3.0 : Num (Fraction *)` is the same value as `3.0 : Frac *`. +## Similarly, the type [`Int a`](#Int) is defined to be an alias for +## `Num (Integer a)`, so `2 : Num (Integer *)` is the same value as +## `2 : Int *`. +## +## In this way, the [Num] type makes it possible to have `1 + 0x1` return +## `2 : Int *` and `1.5 + 1.5` return `3.0 : Frac`. ## ## ## Number Literals ## @@ -29,29 +40,30 @@ interface Num2 ## ends up having the type `Nat`. ## ## Sometimes number literals don't become more specific. For example, -## the #Num.toStr function has the type `Num * -> Str`. This means that +## the [Num.toStr] function has the type `Num * -> Str`. This means that ## when calling `Num.toStr (5 + 6)`, the expression `(5 + 6)` ## still has the type `Num *`. When this happens, `Num *` defaults to -## being an #I32 - so this addition expression would overflow +## being an [I64] - so this addition expression would overflow ## if either 5 or 6 were replaced with a number big enough to cause -## addition overflow on an #I32. +## addition overflow on an [I64] value. ## -## If this default of #I32 is not big enough for your purposes, -## you can add an `i64` to the end of the number literal, like so: +## If this default of [I64] is not big enough for your purposes, +## you can add an `i128` to the end of the number literal, like so: ## -## >>> Num.toStr 5_000_000_000i64 +## >>> Num.toStr 5_000_000_000i128 ## -## This `i64` suffix specifies that you want this number literal to be -## an #I64 instead of a `Num *`. All the other numeric types have -## suffixes just like `i64`; here are some other examples: +## This `i128` suffix specifies that you want this number literal to be +## an [I128] instead of a `Num *`. All the other numeric types have +## suffixes just like `i128`; here are some other examples: ## -## * `215u8` is a `215` value of type #U8 -## * `76.4f32` is a `76.4` value of type #F32 -## * `12345ulen` is a `12345` value of type #Nat +## * `215u8` is a `215` value of type [U8] +## * `76.4f32` is a `76.4` value of type [F32] +## * `123.45dec` is a `123.45` value of type [Dec] +## * `12345nat` is a `12345` value of type [Nat] ## ## In practice, these are rarely needed. It's most common to write ## number literals without any suffix. -Num range : [ @Num range ] +Num a : [ @Num a ] ## A decimal number. ## @@ -110,6 +122,30 @@ Dec : Frac [ @Decimal128 ] ## been done in a base-2 floating point calculation, which causes noticeable ## precision loss in this case. ## +## The floating-point numbers ([F32] and [F64]) also have three values which +## are not ordinary [finite numbers](https://en.wikipedia.org/wiki/Finite_number). +## They are: +## * ∞ ([infinity](https://en.wikipedia.org/wiki/Infinity)) +## * -∞ (negative infinity) +## * *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)) +## +## These values are different from ordinary numbers in that they only occur +## when a floating-point calculation encounters an error. For example: +## * Dividing a positive [F64] by `0.0` returns ∞. +## * Dividing a negative [F64] by `0.0` returns -∞. +## * Dividing a [F64] of `0.0` by `0.0` returns [*NaN*](Num.isNaN). +## +## These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) +## floating point standard. Because almost all modern processors are built to +## this standard, deviating from these rules has a significant performance +## cost! Since the most common reason to choose [F64] or [F32] over [Dec] is +## access to hardware-accelerated performance, Roc follows these rules exactly. +## +## There's no literal syntax for these error values, but you can check to see if +## you ended up with one of them by using [isNaN], [isFinite], and [isInfinite]. +## Whenever a function in this module could return one of these values, that +## possibility is noted in the function's documentation. +## ## ## Performance Notes ## ## On typical modern CPUs, performance is similar between [Dec], [F64], and [F32] @@ -128,7 +164,7 @@ Dec : Frac [ @Decimal128 ] ## an even bigger performance difference. [F32] and [F64] can do these in a ## single instruction, whereas [Dec] needs entire custom procedures - which use ## loops and conditionals. If you need to do performance-critical trigonometry -## or square roots, either [F32] or [F64] is probably a better choice than the +## or square roots, either [F64] or [F32] is probably a better choice than the ## usual default choice of [Dec], despite the precision problems they bring. Frac a : Num [ @Fraction a ] @@ -194,6 +230,17 @@ I64 : Int [ @Signed64 ] U64 : Int [ @Unsigned64 ] I128 : Int [ @Signed128 ] U128 : Int [ @Unsigned128 ] + +## A [natural number](https://en.wikipedia.org/wiki/Natural_number) represented +## as a 64-bit unsigned integer on 64-bit systems, a 32-bit unsigned integer +## on 32-bit systems, and so on. +## +## This system-specific size makes it useful for certain data structure +## functions like [List.len], because the number of elements many data strucures +## can hold is also system-specific. For example, the maximum number of elements +## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and +## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a +## good fit for [List.len] regardless of system. Nat : Int [ @Natural ] ## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values. @@ -272,148 +319,6 @@ Nat : Int [ @Natural ] ## If you need to do math outside these bounds, consider using a larger numeric size. Int size : Num [ @Int size ] -## A 64-bit floating-point number. All number literals with decimal points are #Float values. -## -## >>> 0.1 -## -## >>> 1.0 -## -## >>> 0.0 -## -## If you like, you can put underscores in your #Float literals. -## They have no effect on the number's value, but can make things easier to read. -## -## >>> 1_000_000.000_000_001 -## -## Roc supports two types of floating-point numbers: -## -## - *Decimal* floating-point numbers -## - *Binary* floating-point numbers -## -## Decimal floats are precise for decimal calculations. For example: -## -## >>> 0.1 + 0.2 -## -## Operations on binary floats tend to run *much* faster than operations on -## decimal floats, because almost all processors have dedicated instructions -## for binary floats and not for decimal floats. -## However, binary floats are less precise for decimal calculations. -## -## For example, here is the same `0.1 + 0.2` calculation again, this time putting -## `f64` after the numbers to specify that they should be #F64 binary floats -## instead of the default of decimal floats. -## -## >>> 0.1f64 + 0.2f64 -## -## If decimal precision is unimportant, binary floats give better performance. -## If decimal precision is important - for example, when representing money - -## decimal floats tend to be worth the performance cost. -## -## Usually, Roc's compiler can infer a more specific type than #Float for -## a particular float value, based on how it is used with other numbers. For example: -## -## >>> coordinates : { x : F32, y : F32 } -## >>> coordinates = { x: 1, y: 2.5 } -## >>> -## >>> coordinates.x + 1 -## -## On the last line, the compiler infers that the `1` in `+ 1` is an #F32 -## beacuse it's being added to `coordinates.x`, which was defined to be an #F32 -## on the first line. -## -## Sometimes the compiler has no information about which specific type to pick. -## For example: -## -## >>> 0.1 + 0.2 == 0.3 -## -## When this happens, the compiler defaults to choosing #D64 decimal floats. -## If you want something else, you can write (for example) `0.1f32 + 0.2 == 0.3` -## to compare them as #F32 values instead. -## -## Both decimal and binary #Float values conform to the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754#Interchange_formats) -## specification for floating point numbers. Conforming to this specification -## means Roc's binary floats have nearly universal hardware support, and its -## decimal floats have [some hardware support](http://speleotrove.com/decimal/) -## among the rare processors which support decimal float instructions at all. -## -## This specification covers several float formats. Here are the ones Roc supports: -## -## - #F32 (32-bit binary float) -## - #F64 (64-bit binary float) -## - #D32 (32-bit decimal float) -## - #D64 (64-bit decimal float) # TODO show a table like we do with ints, with the min/max ranges -## -## Like #Int, it's possible for #Float operations to overflow. Like with ints, -## you'll typically get a crash when this happens. -## -## * In a development build, you'll get an assertion failure. -## * In an optimized build, you'll get [`Infinity` or `-Infinity`](https://en.wikipedia.org/wiki/IEEE_754-1985#Positive_and_negative_infinity). -## -## Although some languages treat have first-class representations for -## `-Infinity`, `Infinity`, and the special `NaN` ("not a number") -## floating-point values described in the IEEE-754, Roc does not. -## Instead, Roc treats all of these as errors. If any Float operation -## in a development build encounters one of these values, it will -## result in an assertion failure. -## -## Stll, it's possible that these values may accidentally arise in -## release builds. If this happens, they will behave according to the usual -## IEEE-754 rules: any operation involving `NaN` will output `NaN`, -## any operation involving `Infinity` or `-Infinity` will output either -## `Infinity`, `-Infinity`, or `NaN`, and `NaN` is defined to be not -## equal to itself - meaning `(x == x)` returns `False` if `x` is `NaN`. -## -## These are very error-prone values, so if you see an assertion fail in -## developent because of one of them, take it seriously - and try to fix -## the code so that it can't come up in a release! -## -## ## Loud versus Quiet errors -## -## Besides precision problems, another reason floats are error-prone -## is that they have quiet error handling built in. For example, in -## a 64-bit floating point number, there are certain patterns of those -## 64 bits which do not represent valid floats; instead, they represent -## invalid results of previous operations. -## -## Whenever any arithmetic operation is performed on an invalid float, -## the result is also invalid. This is called *error propagation*, and -## it is notoriously error-prone. In Roc, using equality operations like -## `==` and `!=` on an invalid float causes a crash. (See #Float.verify -## to check the validity of your float.) -## -## Beause invalid floats are so error-prone, Roc discourages using them. -## Instead, by default it treats them the same way as overflow: by -## crashing whenever any #Float function would otherwise return one. -## You can also use functions like #Float.tryAdd to get an `Ok` or an error -## back so you can gracefully recover from invalid values. -## -## Quiet errors can be useful sometimes. For example, you might want to -## do three floating point calculations in a row, and then gracefully handle -## the situation where any one of the three was invalid. In that situation, -## quiet errors can be more efficient than using three `try` functions, because -## it can have one condition at the end instead of three along the way. -## -## Another potential use for quiet errors is for risky performance optimizations. -## When you are absolutely certain there is no chance of overflow or other -## errors, using a *quiet* operation may save an entry in the instruction cache -## by removing a branch that would always have been predicted correctly. -## Always [measure the performance change](https://youtu.be/r-TLSBdHe1A) -## if you do this! The compiler can optimize away those branches behind the scenes, -## so you may find that using the quiet version expliitly -## makes the code riskier to future change, without actually affecting performance. -## -## ## Performance Notes -## -## Currently, loud errors are implemented using an extra conditional. Although -## this conditional will always be correctly branh-predicted unless an error -## occurs, there is a small effect on the instruction cache, which means -## quiet errors are very slightly more efficient. -## -## Long-term, it's possible that the Roc compiler may be able to implement -## loud errors using *signalling errors* in some situations, which could -## eliminate the performance difference between loud and quiet errors in -## the situation where no error occurs. - ## Convert ## Return a negative number when given a positive one, and vice versa. @@ -426,16 +331,16 @@ Int size : Num [ @Int size ] ## ## >>> Num.neg 0.0 ## -## This is safe to use with any #Float, but it can cause overflow when used with certain #Int values. +## This is safe to use with any #Frac, but it can cause overflow when used with certain #Int values. ## ## For example, calling #Num.neg on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow. ## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than ## the highest value it can represent. (For this reason, calling #Num.abs on the lowest signed value will also cause overflow.) ## -## Additionally, calling #Num.neg on any unsigned integer (such as any #U64 or #U32 value) other than 0 will cause overflow. +## Additionally, calling #Num.neg on any unsigned integer (such as any #U64 or #U32 value) other than zero will cause overflow. ## -## (It will never crash when given a #Float, however, because of how floating point numbers represent positive and negative numbers.) -neg : Num range -> Num range +## (It will never crash when given a #Frac, however, because of how floating point numbers represent positive and negative numbers.) +neg : Num a -> Num a ## Return the absolute value of the number. ## @@ -451,14 +356,14 @@ neg : Num range -> Num range ## ## >>> Num.abs 0.0 ## -## This is safe to use with any #Float, but it can cause overflow when used with certain #Int values. +## This is safe to use with any #Frac, but it can cause overflow when used with certain #Int values. ## ## For example, calling #Num.abs on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow. ## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than ## the highest value it can represent. (For this reason, calling #Num.neg on the lowest signed value will also cause overflow.) ## ## Calling this on an unsigned integer (like #U32 or #U64) never does anything. -abs : Num range -> Num range +abs : Num a -> Num a ## Check @@ -485,7 +390,7 @@ isOdd : Num * -> Bool ## Add two numbers of the same type. ## -## (To add an #Int and a #Float, first convert one so that they both have the same type. There are functions in the [`Float`](/Float) module that can convert both #Int to #Float and the other way around.) +## (To add an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.) ## ## `a + b` is shorthand for `Num.add a b`. ## @@ -495,13 +400,24 @@ isOdd : Num * -> Bool ## ## `Num.add` can be convenient in pipelines. ## -## >>> Float.pi +## >>> Frac.pi ## >>> |> Num.add 1.0 -add : Num range, Num range -> Num range +## +## If the answer to this operation can't fit in the return value (e.g. an +## [I8] answer that's higher than 127 or lower than -128), the result is an +## *overflow*. For [F64] and [F32], overflow results in an answer of either +## ∞ or -∞. For all other number types, overflow results in a panic. +add : Num a, Num a -> Num a + +## Add two numbers and check for overflow. +## +## This is the same as [Num.add] except if the operation overflows, instead of +## panicking or returning ∞ or -∞, it will return `Err Overflow`. +addCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## Subtract two numbers of the same type. ## -## (To subtract an #Int and a #Float, first convert one so that they both have the same type. There are functions in the [`Float`](/Float) module that can convert both #Int to #Float and the other way around.) +## (To subtract an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.) ## ## `a - b` is shorthand for `Num.sub a b`. ## @@ -511,13 +427,24 @@ add : Num range, Num range -> Num range ## ## `Num.sub` can be convenient in pipelines. ## -## >>> Float.pi +## >>> Frac.pi ## >>> |> Num.sub 2.0 -sub : Num range, Num range -> Num range +## +## If the answer to this operation can't fit in the return value (e.g. an +## [I8] answer that's higher than 127 or lower than -128), the result is an +## *overflow*. For [F64] and [F32], overflow results in an answer of either +## ∞ or -∞. For all other number types, overflow results in a panic. +sub : Num a, Num a -> Num a + +## Subtract two numbers and check for overflow. +## +## This is the same as [Num.sub] except if the operation overflows, instead of +## panicking or returning ∞ or -∞, it will return `Err Overflow`. +subCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## Multiply two numbers of the same type. ## -## (To multiply an #Int and a #Float, first convert one so that they both have the same type. There are functions in the [`Float`](/Float) module that can convert both #Int to #Float and the other way around.) +## (To multiply an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.) ## ## `a * b` is shorthand for `Num.mul a b`. ## @@ -527,36 +454,115 @@ sub : Num range, Num range -> Num range ## ## `Num.mul` can be convenient in pipelines. ## -## >>> Float.pi +## >>> Frac.pi ## >>> |> Num.mul 2.0 -mul : Num range, Num range -> Num range +## +## If the answer to this operation can't fit in the return value (e.g. an +## [I8] answer that's higher than 127 or lower than -128), the result is an +## *overflow*. For [F64] and [F32], overflow results in an answer of either +## ∞ or -∞. For all other number types, overflow results in a panic. +mul : Num a, Num a -> Num a + +## Multiply two numbers and check for overflow. +## +## This is the same as [Num.mul] except if the operation overflows, instead of +## panicking or returning ∞ or -∞, it will return `Err Overflow`. +mulCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## Convert -## Convert a number to a string, formatted as the traditional base 10 (decimal). +## Convert a number to a [Str]. +## +## This is the same as calling `Num.format {}` - so for more details on +## exact formatting, see [Num.format]. ## ## >>> Num.toStr 42 ## -## Only #Float values will include a decimal point, and they will always include one. +## Only #Frac values will include a decimal point, and they will always include one. ## ## >>> Num.toStr 4.2 ## ## >>> Num.toStr 4.0 ## -## For other bases see #toHexStr, #toOctalStr, and #toBinaryStr. +## When this function is given a non-[finite](Num.isFinite) +## [F64] or [F32] value, the returned string will be `"NaN"`, `"∞"`, or `"-∞"`. +## +## To get strings in hexadecimal, octal, or binary format, use [Num.format]. toStr : Num * -> Str +## Convert a number into a [Str], formatted with the given options. +## +## Default options: +## * `base: Decimal` +## * `notation: Standard` +## * `decimalMark: HideForIntegers "."` +## * `decimalDigits: { min: 0, max: All }` +## * `minIntDigits: 1` +## * `wholeSep: { mark: ",", places: 3 }` +## +## ## Options +## +## +## ### decimalMark +## +## * `AlwaysShow` always shows the decimal mark, no matter what. +## * `HideForIntegers` hides the decimal mark if all the numbers after the decimal mark are 0. +## +## The [Str] included in either of these represents the mark itself. +## +## ### `decimalDigits +## +## With 0 decimal digits, the decimal mark will still be rendered if +## `decimalMark` is set to `AlwaysShow`. +## +## If `max` is less than `min`, then first the number will be truncated to `max` +## digits, and then zeroes will be added afterwards until it reaches `min` digits. +## +## >>> Num.format 1.23 { decPlaces: 0, decPointVis: AlwaysShow } +## +## ### minIntDigits +## +## If the integer portion of number is fewer than this many digits, zeroes will +## be added in front of it until there are at least `minWholeDigits` digits. +## +## If this is set to zero, then numbers less than 1 will begin with `"."` +## rather than `"0."`. +## +## ### wholeSep +## +## Examples: +## +## In some countries (e.g. USA and UK), a comma is used to separate thousands: +## >>> Num.format 1_000_000 { base: Decimal, wholeSep: { mark: ",", places: 3 } } +## +## Sometimes when rendering bits, it's nice to group them into groups of 4: +## >>> Num.format 1_000_000 { base: Binary, wholeSep: { mark: " ", places: 4 } } +## +## It's also common to render hexadecimal in groups of 2: +## >>> Num.format 1_000_000 { base: Hexadecimal, wholeSep: { mark: " ", places: 2 } } +format : + Num *, + { + base ? [ Decimal, Hexadecimal, Octal, Binary ], + notation ? [ Standard, Scientific ], + decimalMark ? [ AlwaysShow Str, HideForIntegers ], + decimalDigits ? { min : U16, max : [ All, Trunc U16, Round U16, Floor U16, Ceil U16 ] }, + minWholeDigits ? U16, + wholeSep ? { mark : Str, places : U64 } + } + -> Str + ## Round off the given float to the nearest integer. -round : Float * -> Int * -ceil : Float * -> Int * -floor : Float * -> Int * -trunc : Float * -> Int * +round : Frac * -> Int * +ceil : Frac * -> Int * +floor : Frac * -> Int * +trunc : Frac * -> Int * ## Convert an #Int to a #Nat. If the given number doesn't fit in #Nat, it will be truncated. ## Since #Nat has a different maximum number depending on the system you're building ## for, this may give a different answer on different systems. ## -## For example, on a 32-bit sytem, #Num.maxNat will return the same answer as +## For example, on a 32-bit system, #Num.maxNat will return the same answer as ## #Num.maxU32. This means that calling `Num.toNat 9_000_000_000` on a 32-bit ## system will return #Num.maxU32 instead of 9 billion, because 9 billion is ## higher than #Num.maxU32 and will not fit in a #Nat on a 32-bit system. @@ -565,13 +571,13 @@ trunc : Float * -> Int * ## the #Nat value of 9_000_000_000. This is because on a 64-bit system, #Nat can ## hold up to #Num.maxU64, and 9_000_000_000 is lower than #Num.maxU64. ## -## To convert a #Float to a #Nat, first call either #Num.round, #Num.ceil, or #Num.floor +## To convert a #Frac to a #Nat, first call either #Num.round, #Num.ceil, or #Num.floor ## on it, then call this on the resulting #Int. toNat : Int * -> Nat ## Convert an #Int to an #I8. If the given number doesn't fit in #I8, it will be truncated. ## -## To convert a #Float to an #I8, first call either #Num.round, #Num.ceil, or #Num.floor +## To convert a #Frac to an #I8, first call either #Num.round, #Num.ceil, or #Num.floor ## on it, then call this on the resulting #Int. toI8 : Int * -> I8 toI16 : Int * -> I16 @@ -595,13 +601,9 @@ toF32 : Num * -> F32 ## there will be a loss of precision. toF64 : Num * -> F64 -## Convert a #Num to a #D32. If the given number can't be precisely represented in a #D32, +## Convert a #Num to a #Dec. If the given number can't be precisely represented in a #Dec, ## there will be a loss of precision. -toD32 : Num * -> D32 - -## Convert a #Num to a #D64. If the given number can't be precisely represented in a #D64, -## there will be a loss of precision. -toD64 : Num * -> D64 +toDec : Num * -> Dec ## Divide two integers and #Num.round the resulut. ## @@ -624,16 +626,16 @@ toD64 : Num * -> D64 ## >>> Num.divRound 8 -3 ## ## This is the same as the #// operator. -divRound : Int, Int -> Int +divRound : Int a, Int a -> Int a ## Perform flooring modulo on two integers. ## ## Modulo is the same as remainder when working with positive numbers, ## but if either number is negative, then modulo works differently. ## -## Additionally, flooring modulo uses #Float.floor on the result. +## Additionally, flooring modulo uses #Frac.floor on the result. ## -## (Use #Float.mod for non-flooring modulo.) +## (Use #Frac.mod for non-flooring modulo.) ## ## Return `Err DivByZero` if the second integer is zero, because division by zero is undefined in mathematics. ## @@ -646,40 +648,16 @@ divRound : Int, Int -> Int ## >>> -8 %% -3 ## ## >>> Int.modFloor -8 -3 -#modFloor : Int, Int -> Result DivByZero Int +#modFloor : Int a, Int a -> Result (Int a) [ DivByZero ]* ## Bitwise -xor : Int, Int -> Int +xor : Int a, Int a -> Int a -and : Int, Int -> Int +and : Int a, Int a -> Int a -not : Int -> Int - -## Sort ascending - that is, with the lowest first, and the highest last. -## -## List.sort Num.asc [ 3, 6, 0 ] -## -asc : Num a, Num a -> [ Eq, Lt, Gt ] - -## Sort descending - that is, with the highest first, and the lowest last. -## -## List.sort Num.desc [ 3, 6, 0 ] -## -desc : Num a, Num a -> [ Eq, Lt, Gt ] - -## TODO should we offer hash32 etc even if someday it has to do a hash64 and truncate? -## -## This function can crash under these circumstances: -## -## * It receives a function, or any type that contains a function (for example a record, tag, or #List containing a function) -## * It receives an erroneous #Float (`NaN`, `Infinity`, or `-Infinity` - these values can only originate from hosts) -## -## CAUTION: This function may give different answers in future releases of Roc, -## so be aware that if you rely on the exact answer this gives today, your -## code may break in a future Roc release. -hash64 : a -> U64 +not : Int a -> Int a ## Limits @@ -738,88 +716,81 @@ maxU32 : U32 ## and zero is the lowest unsigned number. Unsigned numbers cannot be negative. minU32 : U32 -## The highest supported #Float value you can have, which is approximately 1.8 × 10^308. +## The highest supported #F64 value you can have, which is approximately 1.8 × 10^308. ## ## If you go higher than this, your running Roc code will crash - so be careful not to! -maxF64 : Float * +maxF64 : F64 -## The lowest supported #Float value you can have, which is approximately -1.8 × 10^308. +## The lowest supported #F64 value you can have, which is approximately -1.8 × 10^308. ## ## If you go lower than this, your running Roc code will crash - so be careful not to! -minF64 : Float * +minF64 : F64 -## The highest integer that can be represented as a #Float without # losing precision. -## It is equal to 2^53, which is approximately 9 × 10^15. +## The highest supported #F32 value you can have, which is approximately 1.8 × 10^308. ## -## Some integers higher than this can be represented, but they may lose precision. For example: -## -## >>> Float.highestInt -## -## >>> Float.highestInt + 100 # Increasing may lose precision -## -## >>> Float.highestInt - 100 # Decreasing is fine - but watch out for lowestLosslessInt! -maxPreciseInt : Float * +## If you go higher than this, your running Roc code will crash - so be careful not to! +maxF32 : F32 -## The lowest integer that can be represented as a #Float without losing precision. -## It is equal to -2^53, which is approximately -9 × 10^15. +## The lowest supported #F32 value you can have, which is approximately -1.8 × 10^308. ## -## Some integers lower than this can be represented, but they may lose precision. For example: +## If you go lower than this, your running Roc code will crash - so be careful not to! +minF32 : F32 + +## The highest supported #F64 value you can have, which is approximately 1.8 × 10^308. ## -## >>> Float.lowestIntVal +## If you go higher than this, your running Roc code will crash - so be careful not to! +maxDec : Dec + +## The lowest supported #F64 value you can have, which is approximately -1.8 × 10^308. ## -## >>> Float.lowestIntVal - 100 # Decreasing may lose precision -## -## >>> Float.lowestIntVal + 100 # Increasing is fine - but watch out for highestInt! -maxPreciseInt : Float * +## If you go lower than this, your running Roc code will crash - so be careful not to! +maxDec : Dec ## Constants ## An approximation of e, specifically 2.718281828459045. -e : Float * +e : Frac * ## An approximation of pi, specifically 3.141592653589793. -pi : Float * -## Constants - -## An approximation of e, specifically 2.718281828459045. -e : Float * - -## An approximation of pi, specifically 3.141592653589793. -pi : Float * - -#ceiling : Float -> Int - -#floor : Float -> Int +pi : Frac * ## Trigonometry -#cos : Float -> Float +cos : Frac a -> Frac a -#acos : Float -> Float +acos : Frac a -> Frac a -#sin : Float -> Float +sin : Frac a -> Frac a -#asin : Float -> Float +asin : Frac a -> Frac a -#tan : Float -> Float +tan : Frac a -> Frac a -#atan : Float -> Float +atan : Frac a -> Frac a ## Other Calculations (arithmetic?) -## Divide two #Float numbers. +## Divide one [Frac] by another. ## ## `a / b` is shorthand for `Num.div a b`. ## -## Division by zero is undefined in mathematics. As such, you should make -## sure never to pass zero as the denomaintor to this function! +## [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero). +## As such, you should make sure never to pass zero as the denomaintor to this function! +## Calling [div] on a [Dec] denominator of zero will cause a panic. ## -## If zero does get passed as the denominator... +## Calling [div] on [F32] and [F64] values follows these rules: +## * Dividing a positive [F64] or [F32] by zero returns ∞. +## * Dividing a negative [F64] or [F32] by zero returns -∞. +## * Dividing a zero [F64] or [F32] by zero returns [*NaN*](Num.isNaN). ## -## * In a development build, you'll get an assertion failure. -## * In a release build, the function will return `Infinity`, `-Infinity`, or `NaN` depending on the arguments. +## > These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) +## > floating point standard. Because almost all modern processors are built to +## > this standard, deviating from these rules has a significant performance +## > cost! Since the most common reason to choose [F64] or [F32] over [Dec] is +## > access to hardware-accelerated performance, Roc follows these rules exactly. ## -## To divide an #Int and a #Float, first convert the #Int to a #Float using one of the functions in this module. +## To divide an [Int] and a [Frac], first convert the [Int] to a [Frac] using +## one of the functions in this module like [toDec]. ## ## >>> 5.0 / 7.0 ## @@ -827,45 +798,47 @@ pi : Float * ## ## `Num.div` can be convenient in pipelines. ## -## >>> Float.pi +## >>> Num.pi ## >>> |> Num.div 2.0 -#div : Float, Float -> Result Float DivByZero -div = \numerator, denominator -> - when numerator is - 0.0 -> 0.0 # TODO return Result! - _ -> denominator +div : Frac a, Frac a -> Frac a -## Perform modulo on two #Float numbers. +## Perform modulo on two [Frac]s. ## ## Modulo is the same as remainder when working with positive numbers, ## but if either number is negative, then modulo works differently. ## -## Return `Err DivByZero` if the second number is zero, because division by zero is undefined in mathematics. +## `a % b` is shorthand for `Num.mod a b`. ## -## `a % b` is shorthand for `Float.mod a b`. +## [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero), +## and as such, so is modulo by zero. Because of this, you should make sure never +## to pass zero for the second argument to this function! +## +## Passing [mod] a [Dec] value of zero for its second argument will cause a panic. +## Passing [mod] a [F32] and [F64] value for its second argument will cause it +## to return [*NaN*](Num.isNaN). ## ## >>> 5.0 % 7.0 ## -## >>> Float.mod 5 7 +## >>> Num.mod 5 7 ## -## `Float.mod` can be convenient in pipelines. +## `Num.mod` can be convenient in pipelines. ## -## >>> Float.pi -## >>> |> Float.mod 2.0 -mod : Float a, Float a -> Result (Float a) [ DivByZero ]* +## >>> Num.pi +## >>> |> Num.mod 2.0 +mod : Frac a, Frac a -> Frac a -## Raises a #Float to the power of another #Float. +## Raises a #Frac to the power of another #Frac. ## ## ` ## For an #Int alternative to this function, see #Num.raise. -pow : Float a, Float a -> Float a +pow : Frac a, Frac a -> Frac a ## Raises an integer to the power of another, by multiplying the integer by ## itself the given number of times. ## ## This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring). ## -## For a #Float alternative to this function, which supports negative exponents, +## For a #Frac alternative to this function, which supports negative exponents, ## see #Num.exp. ## ## >>> Num.exp 5 0 @@ -882,33 +855,187 @@ pow : Float a, Float a -> Float a ## overflow expBySquaring : Int a, U8 -> Int a -## Return the reciprocal of a #Float - that is, divides `1.0` by the given number. +## Returns an approximation of the absolute value of a [Frac]'s square root. ## -## Crashes if given `0.0`, because division by zero is undefined in mathematics. +## The square root of a negative number is an irrational number, and [Frac] only +## supports rational numbers. As such, you should make sure never to pass this +## function a negative number! Calling [sqrt] on a negative [Dec] will cause a panic. ## -## For a version that does not crash, use #tryRecip -recip : Float a -> Result (Float a) [ DivByZero ]* +## Calling [sqrt] on [F32] and [F64] values follows these rules: +## * Passing a negative [F64] or [F32] returns [*NaN*](Num.isNaN). +## * Passing [*NaN*](Num.isNaN) or -∞ also returns [*NaN*](Num.isNaN). +## * Passing ∞ returns ∞. +## +## > These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) +## > floating point standard. Because almost all modern processors are built to +## > this standard, deviating from these rules has a significant performance +## > cost! Since the most common reason to choose [F64] or [F32] over [Dec] is +## > access to hardware-accelerated performance, Roc follows these rules exactly. +## +## >>> Frac.sqrt 4.0 +## +## >>> Frac.sqrt 1.5 +## +## >>> Frac.sqrt 0.0 +## +## >>> Frac.sqrt -4.0f64 +## +## >>> Frac.sqrt -4.0dec +sqrt : Frac a -> Frac a -## NOTE: Need to come up a suffix alternative to the "try" prefix. -## This should be like (for example) recipTry so that it's more discoverable -## in documentation and editor autocomplete when you type "recip" -tryRecip : Float a -> Result (Float a) [ DivByZero ]* +## Bit shifts -## Return an approximation of the absolute value of the square root of the #Float. +## [Logical bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift) left. ## -## Return #InvalidSqrt if given a negative number or an invalid #Float. The square root of a negative number is an irrational number, and #Float only supports rational numbers. +## `a << b` is shorthand for `Num.shl a b`. +shl : Int a, Int a -> Int a + +## [Arithmetic bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift) left. ## -## >>> Float.sqrt 4.0 +## This is called `shlWrap` because any bits shifted +## off the beginning of the number will be wrapped around to +## the end. (In contrast, [shl] replaces discarded bits with zeroes.) +shlWrap : Int a, Int a -> Int a + +## [Logical bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift) right. ## -## >>> Float.sqrt 1.5 +## `a >> b` is shorthand for `Num.shr a b`. +shr : Int a, Int a -> Int a + +## [Arithmetic bit shift](https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift) right. ## -## >>> Float.sqrt 0.0 -## -## >>> Float.sqrt -4.0 -sqrt : Float a -> [Ok (Float a), InvalidSqrt]* +## This is called `shlWrap` because any bits shifted +## off the end of the number will be wrapped around to +## the beginning. (In contrast, [shr] replaces discarded bits with zeroes.) +shrWrap : Int a, Int a -> Int a ## [Endianness](https://en.wikipedia.org/wiki/Endianness) Endi : [ Big, Little ] +## The [Endi] argument does not matter for [U8] and [I8], since they have +## only one byte. toBytes : Num *, Endi -> List U8 + +## when Num.parseBytes bytes Big is +## Ok { val: f64, rest } -> ... +## Err (ExpectedNum (Float Binary64)) -> ... +parseBytes : List U8, Endi -> Result { val : Num a, rest : List U8 } [ ExpectedNum a ]* + +## when Num.fromBytes bytes Big is +## Ok f64 -> ... +## Err (ExpectedNum (Float Binary64)) -> ... +fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]* + +## Comparison + +## Returns `True` if the first number is less than the second. +## +## `a < b` is shorthand for `Num.isLt a b`. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +## +## >>> 5 +## >>> |> Num.isLt 6 +isLt : Num a, Num a -> Bool + +## Returns `True` if the first number is less than or equal to the second. +## +## `a <= b` is shorthand for `Num.isLte a b`. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +isLte : Num a, Num a -> Bool + +## Returns `True` if the first number is greater than the second. +## +## `a > b` is shorthand for `Num.isGt a b`. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +## +## >>> 6 +## >>> |> Num.isGt 5 +isGt : Num a, Num a -> Bool + +## Returns `True` if the first number is greater than or equal to the second. +## +## `a >= b` is shorthand for `Num.isGte a b`. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +isGte : Num a, Num a -> Bool + +## Returns the higher of two numbers. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +higher : Num a, Num a -> Num a + +## Returns the lower of two numbers. +## +## If either argument is [*NaN*](Num.isNaN), returns `False` no matter what. (*NaN* +## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) +lower : Num a, Num a -> Num a + +# Branchless implementation that works for all numeric types: +# +# let is_lt = arg1 < arg2; +# let is_eq = arg1 == arg2; +# return (is_lt as i8 - is_eq as i8) + 1; +# +# 1, 1 -> (0 - 1) + 1 == 0 # Eq +# 5, 1 -> (0 - 0) + 1 == 1 # Gt +# 1, 5 -> (1 - 0) + 1 == 2 # Lt + +## Returns `Lt` if the first number is less than the second, `Gt` if +## the first is greater than the second, and `Eq` if they're equal. +## +## Although this can be passed to [List.sort], you'll get better performance +## by using [List.sortAsc] or [List.sortDesc] instead. +compare : Num a, Num a -> [ Lt, Eq, Gt ] + +## Special Floating-Point Values + +## When given a [F64] or [F32] value, returns `False` if that value is +## [*NaN*](Num.isNaN), ∞ or -∞, and `True` otherwise. +## +## Always returns `True` when given a [Dec]. +## +## This is the opposite of [isInfinite], except when given [*NaN*](Num.isNaN). Both +## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN). +isFinite : Frac * -> Bool + +## When given a [F64] or [F32] value, returns `True` if that value is either +## ∞ or -∞, and `False` otherwise. +## +## Always returns `False` when given a [Dec]. +## +## This is the opposite of [isFinite], except when given [*NaN*](Num.isNaN). Both +## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN). +isInfinite : Frac * -> Bool + +## When given a [F64] or [F32] value, returns `True` if that value is +## *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)), and `False` otherwise. +## +## Always returns `False` when given a [Dec]. +## +## >>> Num.isNaN 12.3 +## +## >>> Num.isNaN (Num.sqrt -2) +## +## *NaN* is unusual from other numberic values in that: +## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `False` if either argument is *NaN*. +## * *NaN* has no ordering, so [isLt], [isLte], [isGt], and [isGte] always return `False` if either argument is *NaN*. +## +## These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) +## floating point standard. Because almost all modern processors are built to +## this standard, deviating from these rules has a significant performance +## cost! Since the most common reason to choose [F64] or [F32] over [Dec] is +## access to hardware-accelerated performance, Roc follows these rules exactly. +## +## Note that you should never put a *NaN* into a [Set], or use it as the key in +## a [Dict]. The result is entries that can never be removed from those +## collections! See the documentation for [Set.add] and [Dict.insert] for details. +isNaN : Frac * -> Bool diff --git a/compiler/builtins/docs/Set.roc b/compiler/builtins/docs/Set.roc index bc5211b407..24c29dac0e 100644 --- a/compiler/builtins/docs/Set.roc +++ b/compiler/builtins/docs/Set.roc @@ -18,6 +18,9 @@ len : Set * -> Nat # TODO: removed `'` from signature because parser does not support it yet # Original signature: `add : Set 'elem, 'elem -> Set 'elem` +## Make sure never to add a *NaN* to a [Set]! Because *NaN* is defined to be +## unequal to *NaN*, adding a *NaN* results in an entry that can never be +## retrieved or removed from the [Set]. add : Set elem, elem -> Set elem ## Drops the given element from the set. diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index 4789db17b2..3a5ed0c88e 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -194,7 +194,7 @@ startsWith : Str, Str -> Bool ## if you want to check whether a string begins with something that's representable ## in a single code point, you can use (for example) `Str.startsWithCodePoint '鹏'` ## instead of `Str.startsWithCodePoint "鹏"`. ('鹏' evaluates to the [U32] -## value `40527`.) This will not work for graphemes which take up mulitple code +## value `40527`.) This will not work for graphemes which take up multiple code ## points, however; `Str.startsWithCodePoint '👩‍👩‍👦‍👦'` would be a compiler error ## because 👩‍👩‍👦‍👦 takes up multiple code points and cannot be represented as a ## single [U32]. You'd need to use `Str.startsWithCodePoint "🕊"` instead. @@ -427,18 +427,68 @@ chomp : Str, Str -> Result Str [ Expected [ ExactStr Str ]* Str ]* ## equal to the given [U32], return whatever comes after that code point. chompCodePoint : Str, U32 -> Result Str [ Expected [ ExactCodePoint U32 ]* Str ]* -## If the string begins with digits which can represent a valid #U8, return -## that number along with the rest of the string after the digits. -parseU8 : Str -> Result { val : U8, rest : Str } [ Expected [ NumU8 ]* Str ]* -parseI8 : Str -> Result { val : I8, rest : Str } [ Expected [ NumI8 ]* Str ]* -parseU16 : Str -> Result { val : U16, rest : Str } [ Expected [ NumU16 ]* Str ]* -parseI16 : Str -> Result { val : I16, rest : Str } [ Expected [ NumI16 ]* Str ]* -parseU32 : Str -> Result { val : U32, rest : Str } [ Expected [ NumU32 ]* Str ]* -parseI32 : Str -> Result { val : I32, rest : Str } [ Expected [ NumI32 ]* Str ]* -parseU64 : Str -> Result { val : U64, rest : Str } [ Expected [ NumU64 ]* Str ]* -parseI64 : Str -> Result { val : I64, rest : Str } [ Expected [ NumI64 ]* Str ]* -parseU128 : Str -> Result { val : U128, rest : Str } [ Expected [ NumU128 ]* Str ]* -parseI128 : Str -> Result { val : I128, rest : Str } [ Expected [ NumI128 ]* Str ]* +## If the string represents a valid #U8 number, return that number. +## +## For more advanced options, see [parseU8]. +toU8 : Str -> Result U8 [ InvalidU8 ]* +toI8 : Str -> Result I8 [ InvalidI8 ]* +toU16 : Str -> Result U16 [ InvalidU16 ]* +toI16 : Str -> Result I16 [ InvalidI16 ]* +toU32 : Str -> Result U32 [ InvalidU32 ]* +toI32 : Str -> Result I32 [ InvalidI32 ]* +toU64 : Str -> Result U64 [ InvalidU64 ]* +toI64 : Str -> Result I64 [ InvalidI64 ]* +toU128 : Str -> Result U128 [ InvalidU128 ]* +toI128 : Str -> Result I128 [ InvalidI128 ]* +toF64 : Str -> Result U128 [ InvalidF64 ]* +toF32 : Str -> Result I128 [ InvalidF32 ]* +toDec : Str -> Result Dec [ InvalidDec ]* -parseF64 : Str -> Result { val : U128, rest : Str } [ Expected [ NumF64 ]* Str ]* -parseF32 : Str -> Result { val : I128, rest : Str } [ Expected [ NumF32 ]* Str ]* +## If the string represents a valid number, return that number. +## +## The exact number type to look for will be inferred from usage. Here's an +## example where the `Err` branch matches `Integer Signed64`, which causes this to +## parse an [I64] because [I64] is defined as `I64 : Num [ Integer [ Signed64 ] ]`. +## +## >>> when Str.toNum "12345" is +## >>> Ok i64 -> "The I64 was: \(i64)" +## >>> Err (ExpectedNum (Integer Signed64)) -> "Not a valid I64!" +## +## If the string is exactly `"NaN"`, `"∞"`, or `"-∞"`, they will be accepted +## only when converting to [F64] or [F32] numbers, and will be translated accordingly. +## +## This never accepts numbers with underscores or commas in them. For more +## advanced options, see [parseNum]. +toNum : Str -> Result (Num a) [ ExpectedNum a ]* + +## If the string begins with an [Int] or a [finite](Num.isFinite) [Frac], return +## that number along with the rest of the string after it. +## +## The exact number type to look for will be inferred from usage. Here's an +## example where the `Err` branch matches `Float Binary64`, which causes this to +## parse an [F64] because [F64] is defined as `F64 : Num [ Fraction [ Float64 ] ]`. +## +## >>> when Str.parseNum input {} is +## >>> Ok { val: f64, rest } -> "The F64 was: \(f64)" +## >>> Err (ExpectedNum (Fraction Float64)) -> "Not a valid F64!" +## +## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent +## [finite](Num.isFinite) numbers), they will be accepted only when parsing +## [F64] or [F32] numbers, and translated accordingly. +parseNum : Str, NumParseConfig -> Result { val : Num a, rest : Str } [ ExpectedNum a ]* + +## Notes: +## * You can allow a decimal mark for integers; they'll only parse if the numbers after it are all 0. +## * For `wholeSep`, `Required` has a payload for how many digits (e.g. "required every 3 digits") +## * For `wholeSep`, `Allowed` allows the separator to appear anywhere. +NumParseConfig : + { + base ? [ Decimal, Hexadecimal, Octal, Binary ], + notation ? [ Standard, Scientific, Any ], + decimalMark ? [ Allowed Str, Required Str, Disallowed ], + decimalDigits ? [ Any, AtLeast U16, Exactly U16 ], + wholeDigits ? [ Any, AtLeast U16, Exactly U16 ], + leadingZeroes ? [ Allowed, Disallowed ], + trailingZeroes ? [ Allowed, Disallowed ], + wholeSep ? { mark : Str, policy : [ Allowed, Required U64 ] } + } diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 732244d4ae..4ac7d0f0f3 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -1,28 +1,8 @@ -use std::fs::File; -use std::io::prelude::Read; -use std::vec::Vec; - -const BC_PATH: &str = env!( - "BUILTINS_BC", - "Env var BUILTINS_BC not found. Is there a problem with the build script?" -); - pub const OBJ_PATH: &str = env!( "BUILTINS_O", "Env var BUILTINS_O not found. Is there a problem with the build script?" ); -pub fn get_bytes() -> Vec { - // In the build script for the builtins module, we compile the builtins bitcode and set - // BUILTINS_BC to the path to the compiled output. - let mut builtins_bitcode = File::open(BC_PATH).expect("Unable to find builtins bitcode source"); - let mut buffer = Vec::new(); - builtins_bitcode - .read_to_end(&mut buffer) - .expect("Unable to read builtins bitcode"); - buffer -} - pub const NUM_ASIN: &str = "roc_builtins.num.asin"; pub const NUM_ACOS: &str = "roc_builtins.num.acos"; pub const NUM_ATAN: &str = "roc_builtins.num.atan"; @@ -77,6 +57,7 @@ pub const LIST_CONTAINS: &str = "roc_builtins.list.contains"; pub const LIST_REPEAT: &str = "roc_builtins.list.repeat"; pub const LIST_APPEND: &str = "roc_builtins.list.append"; pub const LIST_DROP: &str = "roc_builtins.list.drop"; +pub const LIST_SWAP: &str = "roc_builtins.list.swap"; pub const LIST_SINGLE: &str = "roc_builtins.list.single"; pub const LIST_JOIN: &str = "roc_builtins.list.join"; pub const LIST_RANGE: &str = "roc_builtins.list.range"; @@ -84,3 +65,4 @@ pub const LIST_REVERSE: &str = "roc_builtins.list.reverse"; pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with"; pub const LIST_CONCAT: &str = "roc_builtins.list.concat"; pub const LIST_SET: &str = "roc_builtins.list.set"; +pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place"; diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index f6c4b07b19..158f0523b8 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -292,7 +292,7 @@ pub fn types() -> MutMap { // minInt : Int range add_type!(Symbol::NUM_MIN_INT, int_type(flex(TVAR1))); - // div : Int, Int -> Result Int [ DivByZero ]* + // divInt : Int a, Int a -> Result (Int a) [ DivByZero ]* let div_by_zero = SolvedType::TagUnion( vec![(TagName::Global("DivByZero".into()), vec![])], Box::new(SolvedType::Wildcard), @@ -856,6 +856,13 @@ pub fn types() -> MutMap { Box::new(list_type(flex(TVAR1))), ); + // swap : List elem, Nat, Nat -> List elem + add_top_level_function_type!( + Symbol::LIST_SWAP, + vec![list_type(flex(TVAR1)), nat_type(), nat_type()], + Box::new(list_type(flex(TVAR1))), + ); + // prepend : List elem, elem -> List elem add_top_level_function_type!( Symbol::LIST_PREPEND, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 7283316ba9..b6d13be1da 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -30,7 +30,7 @@ macro_rules! macro_magic { /// Some builtins cannot be constructed in code gen alone, and need to be defined /// as separate Roc defs. For example, List.get has this type: /// -/// List.get : List elem, Int -> Result elem [ OutOfBounds ]* +/// List.get : List elem, Nat -> Result elem [ OutOfBounds ]* /// /// Because this returns an open tag union for its Err type, it's not possible /// for code gen to return a hardcoded value for OutOfBounds. For example, @@ -85,6 +85,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_MAP2 => list_map2, LIST_MAP3 => list_map3, LIST_DROP => list_drop, + LIST_SWAP => list_swap, LIST_MAP_WITH_INDEX => list_map_with_index, LIST_KEEP_IF => list_keep_if, LIST_KEEP_OKS => list_keep_oks, @@ -449,7 +450,7 @@ fn num_add(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumAdd) } -/// Num.addWrap : Int, Int -> Int +/// Num.addWrap : Int a, Int a -> Int a fn num_add_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumAddWrap) } @@ -548,7 +549,7 @@ fn num_sub(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumSub) } -/// Num.subWrap : Int, Int -> Int +/// Num.subWrap : Int a, Int a -> Int a fn num_sub_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumSubWrap) } @@ -647,7 +648,7 @@ fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumMul) } -/// Num.mulWrap : Int, Int -> Int +/// Num.mulWrap : Int a, Int a -> Int a fn num_mul_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumMulWrap) } @@ -1151,7 +1152,7 @@ fn num_ceiling(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// Num.powInt : Int, Int -> Int +/// Num.powInt : Int a, Int a -> Int a fn num_pow_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); @@ -1250,17 +1251,17 @@ fn num_asin(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// Num.bitwiseAnd : Int, Int -> Int +/// Num.bitwiseAnd : Int a, Int a -> Int a fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumBitwiseAnd) } -/// Num.bitwiseXor : Int, Int -> Int +/// Num.bitwiseXor : Int a, Int a -> Int a fn num_bitwise_xor(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumBitwiseXor) } -/// Num.bitwiseOr: Int, Int -> Int +/// Num.bitwiseOr: Int a, Int a -> Int a fn num_bitwise_or(symbol: Symbol, var_store: &mut VarStore) -> Def { num_binop(symbol, var_store, LowLevel::NumBitwiseOr) } @@ -1666,7 +1667,7 @@ fn list_concat(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// List.repeat : elem, Int -> List elem +/// List.repeat : elem, Nat -> List elem fn list_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def { let elem_var = var_store.fresh(); let len_var = var_store.fresh(); @@ -1808,7 +1809,7 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// List.set : List elem, Int, elem -> List elem +/// List.set : List elem, Nat, elem -> List elem /// /// List.set : /// Attr (w | u | v) (List (Attr u a)), @@ -1882,6 +1883,36 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { list_ret_var, ) } + +/// List.swap : List elem, Nat, Nat -> List elem +fn list_swap(symbol: Symbol, var_store: &mut VarStore) -> Def { + let list_var = var_store.fresh(); + let index1_var = var_store.fresh(); + let index2_var = var_store.fresh(); + + let body = RunLowLevel { + op: LowLevel::ListSwap, + args: vec![ + (list_var, Var(Symbol::ARG_1)), + (index1_var, Var(Symbol::ARG_2)), + (index2_var, Var(Symbol::ARG_3)), + ], + ret_var: list_var, + }; + + defn( + symbol, + vec![ + (list_var, Symbol::ARG_1), + (index1_var, Symbol::ARG_2), + (index2_var, Symbol::ARG_3), + ], + var_store, + body, + list_var, + ) +} + /// List.drop : List elem, Nat -> List elem fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); @@ -2267,7 +2298,7 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def { let make_err = tag( "Err", - vec![tag("OutOfBounds", Vec::new(), var_store)], + vec![tag("KeyNotFound", Vec::new(), var_store)], var_store, ); @@ -2500,7 +2531,7 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// Num.rem : Int, Int -> Result Int [ DivByZero ]* +/// Num.rem : Int a, Int a -> Result (Int a) [ DivByZero ]* fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { let num_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); @@ -2559,7 +2590,7 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// Num.isMultipleOf : Int, Int -> Bool +/// Num.isMultipleOf : Int a, Int a -> Bool fn num_is_multiple_of(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_2(symbol, LowLevel::NumIsMultipleOf, var_store) } @@ -2665,7 +2696,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// Num.div : Int, Int -> Result Int [ DivByZero ]* +/// Num.div : Int a , Int a -> Result (Int a) [ DivByZero ]* fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let num_var = var_store.fresh(); diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 6f464cda1a..b4a4652924 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -561,14 +561,14 @@ pub fn sort_can_defs( ))); declarations.push(Declaration::InvalidCycle(entries)); - - // other groups may depend on the symbols defined here, so - // also push this cycle onto the groups - groups.push(cycle); - } else { - // slightly inefficient, because we know this becomes exactly one DeclareRec already - groups.push(cycle); } + + // if it's an invalid cycle, other groups may depend on the + // symbols defined here, so also push this cycle onto the groups + // + // if it's not an invalid cycle, this is slightly inefficient, + // because we know this becomes exactly one DeclareRec already + groups.push(cycle); } // now we have a collection of groups whose dependencies are not cyclic. diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 9fd811c3e6..4116a51128 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -60,7 +60,6 @@ pub enum Expr { Float(Variable, Variable, f64), Str(InlinableString), List { - list_var: Variable, // required for uniqueness of the list elem_var: Variable, loc_elems: Vec>, }, @@ -304,7 +303,6 @@ pub fn canonicalize_expr<'a>( if loc_elems.is_empty() { ( List { - list_var: var_store.fresh(), elem_var: var_store.fresh(), loc_elems: Vec::new(), }, @@ -331,7 +329,6 @@ pub fn canonicalize_expr<'a>( ( List { - list_var: var_store.fresh(), elem_var: var_store.fresh(), loc_elems: can_elems, }, @@ -603,7 +600,7 @@ pub fn canonicalize_expr<'a>( // A "when" with no branches is a runtime error, but it will mess things up // if code gen mistakenly thinks this is a tail call just because its condition - // happend to be one. (The condition gave us our initial output value.) + // happened to be one. (The condition gave us our initial output value.) if branches.is_empty() { output.tail_call = None; } @@ -1234,7 +1231,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> | other @ ForeignCall { .. } => other, List { - list_var, elem_var, loc_elems, } => { @@ -1250,7 +1246,6 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } List { - list_var, elem_var, loc_elems: new_elems, } diff --git a/compiler/can/src/operator.rs b/compiler/can/src/operator.rs index e34340da64..4b6fbf14f2 100644 --- a/compiler/can/src/operator.rs +++ b/compiler/can/src/operator.rs @@ -345,7 +345,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located>) -> &'a ) } If(if_thens, final_else_branch) => { - // If does not get desugared into `when` so we can give more targetted error messages during type checking. + // If does not get desugared into `when` so we can give more targeted error messages during type checking. let desugared_final_else = &*arena.alloc(desugar_expr(arena, &final_else_branch)); let mut desugared_if_thens = Vec::with_capacity_in(if_thens.len(), arena); diff --git a/compiler/constrain/src/builtins.rs b/compiler/constrain/src/builtins.rs index 807d542e73..4704c67539 100644 --- a/compiler/constrain/src/builtins.rs +++ b/compiler/constrain/src/builtins.rs @@ -13,7 +13,7 @@ use roc_types::types::Type::{self, *}; #[inline(always)] pub fn int_literal( num_var: Variable, - percision_var: Variable, + precision_var: Variable, expected: Expected, region: Region, ) -> Constraint { @@ -25,7 +25,7 @@ pub fn int_literal( And(vec![ Eq( num_type.clone(), - ForReason(reason, num_int(Type::Variable(percision_var)), region), + ForReason(reason, num_int(Type::Variable(precision_var)), region), Category::Int, region, ), diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index c4c5b5ed8e..05dff22dd7 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -96,7 +96,7 @@ pub fn constrain_expr( expected: Expected, ) -> Constraint { match expr { - Int(var, percision, _) => int_literal(*var, *percision, expected, region), + Int(var, precision, _) => int_literal(*var, *precision, expected, region), Num(var, _) => exists( vec![*var], Eq( @@ -106,7 +106,7 @@ pub fn constrain_expr( region, ), ), - Float(var, percision, _) => float_literal(*var, *percision, expected, region), + Float(var, precision, _) => float_literal(*var, *precision, expected, region), EmptyRecord => constrain_empty_record(region, expected), Expr::Record { record_var, fields } => { if fields.is_empty() { @@ -220,7 +220,6 @@ pub fn constrain_expr( List { elem_var, loc_elems, - list_var: _unused, } => { if loc_elems.is_empty() { exists( diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index ee580ad0a4..a6462eb570 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -146,7 +146,7 @@ pub fn pre_constrain_imports( // Translate referenced symbols into constraints. We do this on the main // thread because we need exclusive access to the exposed_types map, in order // to get the necessary constraint info for any aliases we imported. We also - // resolve builtin types now, so we can use a refernce to stdlib instead of + // resolve builtin types now, so we can use a reference to stdlib instead of // having to either clone it or recreate it from scratch on the other thread. for &symbol in references.iter() { let module_id = symbol.module_id(); diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index ebcf367152..edf60c1696 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -116,7 +116,7 @@ mod test_fmt { } #[test] - fn force_space_at_begining_of_comment() { + fn force_space_at_beginning_of_comment() { expr_formats_to( indoc!( r#" diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index b960c7468e..8fb6646e26 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "roc_gen_dev" +description = "The development backend for the Roc compiler" version = "0.1.0" authors = ["The Roc Contributors"] license = "UPL-1.0" diff --git a/compiler/gen_dev/README.md b/compiler/gen_dev/README.md index 6cbe429895..a9b544b93c 100644 --- a/compiler/gen_dev/README.md +++ b/compiler/gen_dev/README.md @@ -5,7 +5,7 @@ It goes from Roc's [mono ir](https://github.com/rtfeldman/roc/blob/trunk/compile ## General Process -The backend is essentially defined as two recursive match statment over the mono ir. +The backend is essentially defined as two recursive match statement over the mono ir. The first pass is used to do simple linear scan lifetime analysis. In the future it may be expanded to add a few other quick optimizations. The second pass is the actual meat of the backend that generates the byte buffer of output binary. @@ -62,7 +62,7 @@ Here are example implementations for [arm](https://github.com/rtfeldman/roc/blob Adding a new builtin to the dev backend can be pretty simple. Here is [an example](https://github.com/rtfeldman/roc/pull/893/files) of adding `Num.Sub`. -This is the general procede I follow with some helpful links: +This is the general procedure I follow with some helpful links: 1. Find a feature that is just n+1. For example, since we already have integers, adding a builtin that functions on them should be n+1. diff --git a/compiler/gen_dev/src/generic64/mod.rs b/compiler/gen_dev/src/generic64/mod.rs index af67498706..bf394204da 100644 --- a/compiler/gen_dev/src/generic64/mod.rs +++ b/compiler/gen_dev/src/generic64/mod.rs @@ -205,7 +205,7 @@ pub struct Backend64Bit< float_used_callee_saved_regs: MutSet, stack_size: u32, - // The ammount of stack space needed to pass args for function calling. + // The amount of stack space needed to pass args for function calling. fn_call_stack_size: u32, } @@ -409,7 +409,7 @@ impl< Ok(()) } x => Err(format!( - "recieving return type, {:?}, is not yet implemented", + "receiving return type, {:?}, is not yet implemented", x )), } diff --git a/compiler/gen_dev/src/generic64/x86_64.rs b/compiler/gen_dev/src/generic64/x86_64.rs index d0b8053056..67ae107b5a 100644 --- a/compiler/gen_dev/src/generic64/x86_64.rs +++ b/compiler/gen_dev/src/generic64/x86_64.rs @@ -236,7 +236,7 @@ impl CallConv for X86_64SystemV { Layout::Builtin(Builtin::Float64) => {} x => { return Err(format!( - "recieving return type, {:?}, is not yet implemented", + "receiving return type, {:?}, is not yet implemented", x )); } @@ -530,7 +530,7 @@ impl CallConv for X86_64WindowsFastcall { Layout::Builtin(Builtin::Float64) => {} x => { return Err(format!( - "recieving return type, {:?}, is not yet implemented", + "receiving return type, {:?}, is not yet implemented", x )); } @@ -765,12 +765,11 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, imm32: i32, ) { - if dst == src1 { - add_reg64_imm32(buf, dst, imm32); - } else { + if dst != src1 { mov_reg64_reg64(buf, dst, src1); - add_reg64_imm32(buf, dst, imm32); } + + add_reg64_imm32(buf, dst, imm32); } #[inline(always)] fn add_reg64_reg64_reg64( @@ -821,12 +820,11 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - if dst == src1 { - imul_reg64_reg64(buf, dst, src2); - } else { + if dst != src1 { mov_reg64_reg64(buf, dst, src1); - imul_reg64_reg64(buf, dst, src2); } + + imul_reg64_reg64(buf, dst, src2); } #[inline(always)] @@ -926,12 +924,11 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, imm32: i32, ) { - if dst == src1 { - sub_reg64_imm32(buf, dst, imm32); - } else { + if dst != src1 { mov_reg64_reg64(buf, dst, src1); - sub_reg64_imm32(buf, dst, imm32); } + + sub_reg64_imm32(buf, dst, imm32); } #[inline(always)] fn sub_reg64_reg64_reg64( @@ -940,12 +937,11 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - if dst == src1 { - sub_reg64_reg64(buf, dst, src2); - } else { + if dst != src1 { mov_reg64_reg64(buf, dst, src1); - sub_reg64_reg64(buf, dst, src2); } + + sub_reg64_reg64(buf, dst, src2); } #[inline(always)] diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index d6544bfbc1..af414118f0 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -106,6 +106,7 @@ where call, pass, fail: _, + exception_id: _, } => { // for now, treat invoke as a normal call self.build_expr(symbol, &Expr::Call(call.clone()), layout)?; @@ -144,7 +145,7 @@ where ) -> Result<(), String>; /// build_expr builds the expressions for the specified symbol. - /// The builder must keep track of the symbol because it may be refered to later. + /// The builder must keep track of the symbol because it may be referred to later. fn build_expr( &mut self, sym: &Symbol, @@ -229,7 +230,7 @@ where } /// build_run_low_level builds the low level opertation and outputs to the specified symbol. - /// The builder must keep track of the symbol because it may be refered to later. + /// The builder must keep track of the symbol because it may be referred to later. fn build_run_low_level( &mut self, sym: &Symbol, @@ -443,7 +444,13 @@ where self.set_last_seen(*sym, stmt); } } - Expr::AccessAtIndex { structure, .. } => { + Expr::StructAtIndex { structure, .. } => { + self.set_last_seen(*structure, stmt); + } + Expr::GetTagId { structure, .. } => { + self.set_last_seen(*structure, stmt); + } + Expr::UnionAtIndex { structure, .. } => { self.set_last_seen(*structure, stmt); } Expr::Array { elems, .. } => { @@ -486,6 +493,7 @@ where call, pass, fail: _, + exception_id: _, } => { // for now, treat invoke as a normal call self.set_last_seen(*symbol, stmt); @@ -508,7 +516,7 @@ where Stmt::Ret(sym) => { self.set_last_seen(*sym, stmt); } - Stmt::Rethrow => {} + Stmt::Resume(_exception_id) => {} Stmt::Refcounting(modify, following) => { let sym = modify.get_symbol(); @@ -517,7 +525,7 @@ where } Stmt::Join { parameters, - continuation, + body: continuation, remainder, .. } => { diff --git a/compiler/gen_dev/src/object_builder.rs b/compiler/gen_dev/src/object_builder.rs index 03bbc854de..e3b09d7b52 100644 --- a/compiler/gen_dev/src/object_builder.rs +++ b/compiler/gen_dev/src/object_builder.rs @@ -9,8 +9,7 @@ use object::{ }; use roc_collections::all::MutMap; use roc_module::symbol; -use roc_mono::ir::Proc; -use roc_mono::layout::Layout; +use roc_mono::ir::{Proc, ProcLayout}; use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple}; // This is used by some code below which is currently commented out. @@ -22,7 +21,7 @@ use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Tripl pub fn build_module<'a>( env: &'a Env, target: &Triple, - procedures: MutMap<(symbol::Symbol, Layout<'a>), Proc<'a>>, + procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, ) -> Result { match target { Triple { @@ -145,7 +144,7 @@ fn generate_wrapper<'a, B: Backend<'a>>( fn build_object<'a, B: Backend<'a>>( env: &'a Env, - procedures: MutMap<(symbol::Symbol, Layout<'a>), Proc<'a>>, + procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, mut backend: B, mut output: Object, ) -> Result { @@ -187,7 +186,7 @@ fn build_object<'a, B: Backend<'a>>( let mut procs = Vec::with_capacity_in(procedures.len(), env.arena); for ((sym, layout), proc) in procedures { let fn_name = layout_ids - .get(sym, &layout) + .get_toplevel(sym, &layout) .to_symbol_string(sym, &env.interns); let section_id = output.add_section( diff --git a/compiler/gen_dev/tests/helpers/eval.rs b/compiler/gen_dev/tests/helpers/eval.rs index 9800444959..f3403bb9d4 100644 --- a/compiler/gen_dev/tests/helpers/eval.rs +++ b/compiler/gen_dev/tests/helpers/eval.rs @@ -69,8 +69,8 @@ pub fn helper<'a>( let mut procedures = MutMap::default(); - for ((symbol, top_level), proc) in top_procedures { - procedures.insert((symbol, arena.alloc(top_level).full()), proc); + for (key, proc) in top_procedures { + procedures.insert(key, proc); } /* @@ -87,17 +87,12 @@ pub fn helper<'a>( println!("=================================\n"); */ debug_assert_eq!(exposed_to_host.len(), 1); - let main_fn_symbol = exposed_to_host.keys().copied().next().unwrap(); - - let main_fn_layout = procedures - .keys() - .find(|(s, _)| *s == main_fn_symbol) - .map(|t| t.1) - .unwrap(); + let main_fn_symbol = loaded.entry_point.symbol; + let main_fn_layout = loaded.entry_point.layout; let mut layout_ids = roc_mono::layout::LayoutIds::default(); let main_fn_name = layout_ids - .get(main_fn_symbol, &main_fn_layout) + .get_toplevel(main_fn_symbol, &main_fn_layout) .to_symbol_string(main_fn_symbol, &interns); let mut lines = Vec::new(); diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml new file mode 100644 index 0000000000..7fb3060814 --- /dev/null +++ b/compiler/gen_llvm/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "roc_gen_llvm" +description = "The LLVM backend for the Roc compiler" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[dependencies] +roc_collections = { path = "../collections" } +roc_region = { path = "../region" } +roc_module = { path = "../module" } +roc_problem = { path = "../problem" } +roc_types = { path = "../types" } +roc_builtins = { path = "../builtins" } +roc_unify = { path = "../unify" } +roc_solve = { path = "../solve" } +roc_mono = { path = "../mono" } +morphic_lib = { path = "../../vendor/morphic_lib" } +im = "14" # im and im-rc should always have the same version! +im-rc = "14" # im and im-rc should always have the same version! +bumpalo = { version = "3.6.1", features = ["collections"] } +inlinable_string = "0.1" +inkwell = { path = "../../vendor/inkwell" } +target-lexicon = "0.10" + +[dev-dependencies] +roc_can = { path = "../can" } +roc_parse = { path = "../parse" } +roc_load = { path = "../load" } +roc_reporting = { path = "../reporting" } +roc_build = { path = "../build" } +roc_std = { path = "../../roc_std" } +pretty_assertions = "0.5.1" +maplit = "1.0.1" +indoc = "0.3.3" +quickcheck = "0.8" +quickcheck_macros = "0.8" +tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] } +bumpalo = { version = "3.6.1", features = ["collections"] } +libc = "0.2" diff --git a/compiler/gen/src/lib.rs b/compiler/gen_llvm/src/lib.rs similarity index 87% rename from compiler/gen/src/lib.rs rename to compiler/gen_llvm/src/lib.rs index 092cf0b8a8..bef97f894c 100644 --- a/compiler/gen/src/lib.rs +++ b/compiler/gen_llvm/src/lib.rs @@ -2,7 +2,7 @@ // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits -#![allow(clippy::clippy::float_cmp)] +#![allow(clippy::float_cmp)] pub mod llvm; diff --git a/compiler/gen/src/llvm/bitcode.rs b/compiler/gen_llvm/src/llvm/bitcode.rs similarity index 76% rename from compiler/gen/src/llvm/bitcode.rs rename to compiler/gen_llvm/src/llvm/bitcode.rs index ab54010fa2..b92df1f79b 100644 --- a/compiler/gen/src/llvm/bitcode.rs +++ b/compiler/gen_llvm/src/llvm/bitcode.rs @@ -1,16 +1,16 @@ /// Helpers for interacting with the zig that generates bitcode use crate::debug_info_init; -use crate::llvm::build::{set_name, Env, C_CALL_CONV, FAST_CALL_CONV}; +use crate::llvm::build::{struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX}; use crate::llvm::convert::basic_type_from_layout; use crate::llvm::refcounting::{ decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout, }; use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::types::{BasicType, BasicTypeEnum}; -use inkwell::values::{BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue}; +use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue}; use inkwell::AddressSpace; use roc_module::symbol::Symbol; -use roc_mono::layout::{Layout, LayoutIds}; +use roc_mono::layout::{Layout, LayoutIds, UnionLayout}; pub fn call_bitcode_fn<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -66,6 +66,126 @@ const ARGUMENT_SYMBOLS: [Symbol; 8] = [ Symbol::ARG_8, ]; +pub fn build_has_tag_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + function: FunctionValue<'ctx>, + union_layout: UnionLayout<'a>, +) -> FunctionValue<'ctx> { + let fn_name: &str = &format!("{}_has_tag_id", function.get_name().to_string_lossy()); + + // currently the code assumes we're dealing with a non-recursive layout + debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_))); + + match env.module.get_function(fn_name) { + Some(function_value) => function_value, + None => build_has_tag_id_help(env, union_layout, &fn_name), + } +} + +fn build_has_tag_id_help<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + union_layout: UnionLayout<'a>, + fn_name: &str, +) -> FunctionValue<'ctx> { + let i8_ptr_type = env.context.i8_type().ptr_type(AddressSpace::Generic); + let argument_types: &[BasicTypeEnum] = &[env.context.i16_type().into(), i8_ptr_type.into()]; + + let block = env.builder.get_insert_block().expect("to be in a function"); + let di_location = env.builder.get_current_debug_location().unwrap(); + + let output_type = crate::llvm::convert::zig_has_tag_id_type(env); + + let function_value = crate::llvm::refcounting::build_header_help( + env, + &fn_name, + output_type.into(), + &argument_types, + ); + + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + + let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); + debug_assert!(kind_id > 0); + let attr = env.context.create_enum_attribute(kind_id, 1); + function_value.add_attribute(AttributeLoc::Function, attr); + + let entry = env.context.append_basic_block(function_value, "entry"); + env.builder.position_at_end(entry); + + debug_info_init!(env, function_value); + + let it = function_value.get_param_iter(); + + let arguments = + bumpalo::collections::Vec::from_iter_in(it.take(argument_types.len()), env.arena); + + for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS.iter()) { + argument.set_name(name.ident_string(&env.interns)); + } + + match arguments.as_slice() { + [tag_id, tag_value_ptr] => { + let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout)); + + let argument_cast = env + .builder + .build_bitcast( + *tag_value_ptr, + tag_type.ptr_type(AddressSpace::Generic), + "load_opaque", + ) + .into_pointer_value(); + + let tag_value = env.builder.build_load(argument_cast, "get_value"); + + let actual_tag_id = { + let tag_id_i64 = + crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value); + + env.builder + .build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16") + }; + + let answer = env.builder.build_int_compare( + inkwell::IntPredicate::EQ, + tag_id.into_int_value(), + actual_tag_id, + "compare", + ); + + let tag_data_ptr = { + let data_index = env + .context + .i64_type() + .const_int(TAG_DATA_INDEX as u64, false); + + let ptr = unsafe { + env.builder.build_gep( + tag_value_ptr.into_pointer_value(), + &[data_index], + "get_data_ptr", + ) + }; + env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque") + }; + + let field_vals = [(0, answer.into()), (1, tag_data_ptr)]; + + let output = struct_from_fields(env, output_type, field_vals.iter().copied()); + + env.builder.build_return(Some(&output)); + + env.builder.position_at_end(block); + env.builder + .set_current_debug_location(env.context, di_location); + + function_value + } + _ => unreachable!(), + } +} + pub fn build_transform_caller<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, function: FunctionValue<'ctx>, @@ -110,6 +230,9 @@ fn build_transform_caller_help<'a, 'ctx, 'env>( &(bumpalo::vec![ in env.arena; BasicTypeEnum::PointerType(arg_type); argument_layouts.len() + 2 ]), ); + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); let attr = env.context.create_enum_attribute(kind_id, 1); @@ -122,13 +245,13 @@ fn build_transform_caller_help<'a, 'ctx, 'env>( let mut it = function_value.get_param_iter(); let closure_ptr = it.next().unwrap().into_pointer_value(); - set_name(closure_ptr.into(), Symbol::ARG_1.ident_string(&env.interns)); + closure_ptr.set_name(Symbol::ARG_1.ident_string(&env.interns)); let arguments = bumpalo::collections::Vec::from_iter_in(it.take(argument_layouts.len()), env.arena); for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS[1..].iter()) { - set_name(*argument, name.ident_string(&env.interns)); + argument.set_name(name.ident_string(&env.interns)); } let mut arguments_cast = @@ -148,9 +271,6 @@ fn build_transform_caller_help<'a, 'ctx, 'env>( } match closure_data_layout { - Layout::FunctionPointer(_, _) => { - // do nothing - } Layout::Closure(_, lambda_set, _) => { if let Layout::Struct(&[]) = lambda_set.runtime_representation() { // do nothing @@ -303,6 +423,9 @@ fn build_rc_wrapper<'a, 'ctx, 'env>( ), }; + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); let attr = env.context.create_enum_attribute(kind_id, 1); @@ -316,7 +439,7 @@ fn build_rc_wrapper<'a, 'ctx, 'env>( let mut it = function_value.get_param_iter(); let value_ptr = it.next().unwrap().into_pointer_value(); - set_name(value_ptr.into(), Symbol::ARG_1.ident_string(&env.interns)); + value_ptr.set_name(Symbol::ARG_1.ident_string(&env.interns)); let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic); @@ -334,7 +457,7 @@ fn build_rc_wrapper<'a, 'ctx, 'env>( } Mode::IncN => { let n = it.next().unwrap().into_int_value(); - set_name(n.into(), Symbol::ARG_2.ident_string(&env.interns)); + n.set_name(Symbol::ARG_2.ident_string(&env.interns)); increment_n_refcount_layout(env, function_value, layout_ids, n, value, layout); } @@ -381,6 +504,9 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>( &[arg_type.into(), arg_type.into()], ); + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); let attr = env.context.create_enum_attribute(kind_id, 1); @@ -395,8 +521,8 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>( let value_ptr1 = it.next().unwrap().into_pointer_value(); let value_ptr2 = it.next().unwrap().into_pointer_value(); - set_name(value_ptr1.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value_ptr2.into(), Symbol::ARG_2.ident_string(&env.interns)); + value_ptr1.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value_ptr2.set_name(Symbol::ARG_2.ident_string(&env.interns)); let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic); @@ -455,6 +581,9 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>( &[arg_type.into(), arg_type.into(), arg_type.into()], ); + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + // we expose this function to zig; must use c calling convention function_value.set_call_conventions(C_CALL_CONV); @@ -473,9 +602,9 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>( let value_ptr1 = it.next().unwrap().into_pointer_value(); let value_ptr2 = it.next().unwrap().into_pointer_value(); - set_name(closure_ptr.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value_ptr1.into(), Symbol::ARG_2.ident_string(&env.interns)); - set_name(value_ptr2.into(), Symbol::ARG_3.ident_string(&env.interns)); + closure_ptr.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value_ptr1.set_name(Symbol::ARG_2.ident_string(&env.interns)); + value_ptr2.set_name(Symbol::ARG_3.ident_string(&env.interns)); let value_type = basic_type_from_layout(env, layout); let value_ptr_type = value_type.ptr_type(AddressSpace::Generic); @@ -496,7 +625,6 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>( let default = [value1, value2]; let arguments_cast = match closure_data_layout { - Layout::FunctionPointer(_, _) => &default, Layout::Closure(_, lambda_set, _) => { if let Layout::Struct(&[]) = lambda_set.runtime_representation() { &default diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs similarity index 80% rename from compiler/gen/src/llvm/build.rs rename to compiler/gen_llvm/src/llvm/build.rs index c1a0006844..201acf20d6 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use crate::llvm::bitcode::call_bitcode_fn; use crate::llvm::build_dict::{ dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection, @@ -8,7 +10,7 @@ use crate::llvm::build_list::{ allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains, list_drop, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map_with_index, list_prepend, list_range, list_repeat, - list_reverse, list_set, list_single, list_sort_with, + list_reverse, list_set, list_single, list_sort_with, list_swap, }; use crate::llvm::build_str::{ empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, @@ -17,15 +19,13 @@ use crate::llvm::build_str::{ }; use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::convert::{ - basic_type_from_builtin, basic_type_from_layout, block_of_memory, block_of_memory_slices, - get_fn_type, get_ptr_type, ptr_int, + basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, ptr_int, }; use crate::llvm::refcounting::{ decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, }; use bumpalo::collections::Vec; use bumpalo::Bump; -use either::Either; use inkwell::basic_block::BasicBlock; use inkwell::builder::Builder; use inkwell::context::Context; @@ -35,22 +35,25 @@ use inkwell::debug_info::{ use inkwell::memory_buffer::MemoryBuffer; use inkwell::module::{Linkage, Module}; use inkwell::passes::{PassManager, PassManagerBuilder}; -use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType}; +use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType, StructType}; use inkwell::values::BasicValueEnum::{self, *}; use inkwell::values::{ - BasicValue, CallSiteValue, FloatValue, FunctionValue, InstructionOpcode, InstructionValue, - IntValue, PointerValue, StructValue, + BasicValue, CallSiteValue, CallableValue, FloatValue, FunctionValue, InstructionOpcode, + InstructionValue, IntValue, PointerValue, StructValue, }; use inkwell::OptimizationLevel; use inkwell::{AddressSpace, IntPredicate}; +use morphic_lib::{ + CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar, +}; use roc_builtins::bitcode; -use roc_collections::all::{ImMap, MutSet}; -use roc_module::ident::TagName; +use roc_collections::all::{ImMap, MutMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::symbol::{Interns, ModuleId, Symbol}; -use roc_mono::ir::{BranchInfo, CallType, JoinPointId, ModifyRc, TopLevelFunctionLayout, Wrapped}; -use roc_mono::layout::{Builtin, InPlace, LambdaSet, Layout, LayoutIds, UnionLayout}; -use target_lexicon::CallingConvention; +use roc_mono::ir::{ + BranchInfo, CallType, EntryPoint, ExceptionId, JoinPointId, ModifyRc, OptLevel, ProcLayout, +}; +use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, UnionLayout}; /// This is for Inkwell's FunctionValue::verify - we want to know the verification /// output in debug builds, but we don't want it to print to stdout in release builds! @@ -84,21 +87,6 @@ macro_rules! debug_info_init { }}; } -#[derive(Debug, Clone, Copy)] -pub enum OptLevel { - Normal, - Optimize, -} - -impl From for OptimizationLevel { - fn from(level: OptLevel) -> Self { - match level { - OptLevel::Normal => OptimizationLevel::None, - OptLevel::Optimize => OptimizationLevel::Aggressive, - } - } -} - /// Iterate over all functions in an llvm module pub struct FunctionIterator<'ctx> { next: Option>, @@ -130,7 +118,7 @@ impl<'ctx> Iterator for FunctionIterator<'ctx> { #[derive(Default, Debug, Clone, PartialEq)] pub struct Scope<'a, 'ctx> { symbols: ImMap, BasicValueEnum<'ctx>)>, - pub top_level_thunks: ImMap, FunctionValue<'ctx>)>, + pub top_level_thunks: ImMap, FunctionValue<'ctx>)>, join_points: ImMap, &'a [PointerValue<'ctx>])>, } @@ -144,11 +132,11 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { pub fn insert_top_level_thunk( &mut self, symbol: Symbol, - layout: &'a TopLevelFunctionLayout<'a>, + layout: &'a ProcLayout<'a>, function_value: FunctionValue<'ctx>, ) { self.top_level_thunks - .insert(symbol, (layout.full(), function_value)); + .insert(symbol, (*layout, function_value)); } fn remove(&mut self, symbol: &Symbol) { self.symbols.remove(symbol); @@ -313,6 +301,8 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { /* dwo_id */ 0, /* split_debug_inling */ false, /* debug_info_for_profiling */ false, + "", + "", ) } @@ -353,7 +343,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { } pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> { - let bitcode_bytes = bitcode::get_bytes(); + // In the build script for the builtins module, + // we compile the builtins into LLVM bitcode + let bitcode_bytes: &[u8] = include_bytes!("../../../builtins/bitcode/builtins.bc"); let memory_buffer = MemoryBuffer::create_from_memory_range(&bitcode_bytes, module_name); @@ -592,22 +584,37 @@ pub fn construct_optimization_passes<'a>( (mpm, fpm) } -pub fn promote_to_main_function<'a, 'ctx, 'env>( +fn promote_to_main_function<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, + mod_solutions: &'a ModSolutions, symbol: Symbol, - layout: TopLevelFunctionLayout<'a>, + top_level: ProcLayout<'a>, ) -> (&'static str, FunctionValue<'ctx>) { - let fn_name = layout_ids - .get(symbol, &(env.arena.alloc(layout).full())) - .to_symbol_string(symbol, &env.interns); + let it = top_level.arguments.iter().copied(); + let bytes = roc_mono::alias_analysis::func_name_bytes_help(symbol, it, top_level.result); + let func_name = FuncName(&bytes); + let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); - let roc_main_fn = env.module.get_function(&fn_name).unwrap(); + let mut it = func_solutions.specs(); + let func_spec = it.next().unwrap(); + debug_assert!( + it.next().is_none(), + "we expect only one specialization of this symbol" + ); + + // NOTE fake layout; it is only used for debug prints + let roc_main_fn = + function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::Struct(&[])); let main_fn_name = "$Test.main"; // Add main to the module. - let main_fn = expose_function_to_host_help(env, roc_main_fn, main_fn_name); + let main_fn = expose_function_to_host_help( + env, + &inlinable_string::InlinableString::from(main_fn_name), + roc_main_fn, + main_fn_name, + ); (main_fn_name, main_fn) } @@ -624,6 +631,7 @@ pub fn int_with_precision<'a, 'ctx, 'env>( Builtin::Int32 => env.context.i32_type().const_int(value as u64, false), Builtin::Int16 => env.context.i16_type().const_int(value as u64, false), Builtin::Int8 => env.context.i8_type().const_int(value as u64, false), + Builtin::Int1 => env.context.bool_type().const_int(value as u64, false), _ => panic!("Invalid layout for int literal = {:?}", precision), } } @@ -781,6 +789,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>( pub fn build_exp_call<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, layout: &Layout<'a>, @@ -793,7 +802,11 @@ pub fn build_exp_call<'a, 'ctx, 'env>( match call_type { CallType::ByName { - name, full_layout, .. + name, + specialization_id, + arg_layouts, + ret_layout, + .. } => { let mut arg_tuples: Vec = Vec::with_capacity_in(arguments.len(), env.arena); @@ -802,34 +815,62 @@ pub fn build_exp_call<'a, 'ctx, 'env>( arg_tuples.push(load_symbol(scope, symbol)); } - call_with_args( + let bytes = specialization_id.to_bytes(); + let callee_var = CalleeSpecVar(&bytes); + let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap(); + + roc_call_with_args( env, - layout_ids, - &full_layout, + arg_layouts, + ret_layout, *name, - parent, + func_spec, arg_tuples.into_bump_slice(), ) } - CallType::LowLevel { op, update_mode: _ } => { - run_low_level(env, layout_ids, scope, parent, layout, *op, arguments) + CallType::LowLevel { op, update_mode } => { + let bytes = update_mode.to_bytes(); + let update_var = UpdateModeVar(&bytes); + let update_mode = func_spec_solutions.update_mode(update_var).ok(); + + run_low_level( + env, + layout_ids, + scope, + parent, + layout, + *op, + arguments, + update_mode, + ) } CallType::HigherOrderLowLevel { op, - closure_layout, function_owns_closure_data, - } => run_higher_order_low_level( - env, - layout_ids, - scope, - layout, - *op, - *closure_layout, - *function_owns_closure_data, - arguments, - ), + specialization_id, + arg_layouts, + ret_layout, + .. + } => { + let bytes = specialization_id.to_bytes(); + let callee_var = CalleeSpecVar(&bytes); + let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap(); + + run_higher_order_low_level( + env, + layout_ids, + scope, + layout, + *op, + func_spec, + arg_layouts, + ret_layout, + *function_owns_closure_data, + arguments, + ) + } CallType::Foreign { foreign_symbol, @@ -840,6 +881,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>( build_foreign_symbol( env, layout_ids, + func_spec_solutions, scope, parent, foreign_symbol, @@ -851,25 +893,58 @@ pub fn build_exp_call<'a, 'ctx, 'env>( } } +pub const TAG_ID_INDEX: u32 = 1; +pub const TAG_DATA_INDEX: u32 = 0; + +pub fn struct_from_fields<'a, 'ctx, 'env, I>( + env: &Env<'a, 'ctx, 'env>, + struct_type: StructType<'ctx>, + values: I, +) -> StructValue<'ctx> +where + I: Iterator)>, +{ + let mut struct_value = struct_type.const_zero().into(); + + // Insert field exprs into struct_val + for (index, field_val) in values { + let index: u32 = index as u32; + + struct_value = env + .builder + .build_insert_value(struct_value, field_val, index, "insert_record_field") + .unwrap(); + } + + struct_value.into_struct_value() +} + pub fn build_exp_expr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, layout: &Layout<'a>, expr: &roc_mono::ir::Expr<'a>, ) -> BasicValueEnum<'ctx> { - use inkwell::types::BasicType; use roc_mono::ir::Expr::*; match expr { Literal(literal) => build_exp_literal(env, layout, literal), - Call(call) => build_exp_call(env, layout_ids, scope, parent, layout, call), + Call(call) => build_exp_call( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + layout, + call, + ), Struct(sorted_fields) => { let ctx = env.context; - let builder = env.builder; // Determine types let num_fields = sorted_fields.len(); @@ -889,526 +964,24 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( // Create the struct_type let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let mut struct_val = struct_type.const_zero().into(); // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - struct_val = builder - .build_insert_value(struct_val, field_val, index as u32, "insert_record_field") - .unwrap(); - } - - BasicValueEnum::StructValue(struct_val.into_struct_value()) - } - - Tag { - union_size, - arguments, - tag_layout, - .. - } if *union_size == 1 - && matches!(tag_layout, Layout::Union(UnionLayout::NonRecursive(_))) => - { - let it = arguments.iter(); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - for field_symbol in it { - let (val, field_layout) = load_symbol_and_layout(scope, field_symbol); - if !field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, &field_layout); - - field_types.push(field_type); - field_vals.push(val); - } - } - - // If the struct has only one field that isn't zero-sized, - // unwrap it. This is what the layout expects us to do. - if field_vals.len() == 1 { - field_vals.pop().unwrap() - } else { - // Create the struct_type - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let mut struct_val = struct_type.const_zero().into(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - struct_val = builder - .build_insert_value( - struct_val, - field_val, - index as u32, - "insert_single_tag_field", - ) - .unwrap(); - } - - BasicValueEnum::StructValue(struct_val.into_struct_value()) - } + struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into() } Tag { arguments, - tag_layout: Layout::Union(UnionLayout::NonRecursive(fields)), + tag_layout: union_layout, union_size, tag_id, .. - } => { - let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields)); - - debug_assert!(*union_size > 1); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - let tag_field_layouts = &fields[*tag_id as usize]; - - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - panic!( - r"non-recursive tag unions cannot directly contain a recursive pointer" - ); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } - - // Create the struct_type - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let mut struct_val = struct_type.const_zero().into(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - struct_val = builder - .build_insert_value( - struct_val, - field_val, - index as u32, - "insert_multi_tag_field", - ) - .unwrap(); - } - - // How we create tag values - // - // The memory layout of tags can be different. e.g. in - // - // [ Ok Int, Err Str ] - // - // the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct. - // All tags of a union must have the same length, for easy addressing (e.g. array lookups). - // So we need to ask for the maximum of all tag's sizes, even if most tags won't use - // all that memory, and certainly won't use it in the same way (the tags have fields of - // different types/sizes) - // - // In llvm, we must be explicit about the type of value we're creating: we can't just - // make a unspecified block of memory. So what we do is create a byte array of the - // desired size. Then when we know which tag we have (which is here, in this function), - // we need to cast that down to the array of bytes that llvm expects - // - // There is the bitcast instruction, but it doesn't work for arrays. So we need to jump - // through some hoops using store and load to get this to work: the array is put into a - // one-element struct, which can be cast to the desired type. - // - // This tricks comes from - // https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116 - - let internal_type = basic_type_from_layout(env, &tag_layout); - - cast_tag_to_block_of_memory(builder, struct_val.into_struct_value(), internal_type) - } - Tag { - arguments, - tag_layout: Layout::Union(UnionLayout::Recursive(fields)), - union_size, - tag_id, - .. - } => { - let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields)); - - debug_assert!(*union_size > 1); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - let tag_field_layouts = &fields[*tag_id as usize]; - - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } - - // Create the struct_type - let data_ptr = reserve_with_refcount(env, &tag_layout); - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - data_ptr.into() - } - - Tag { - arguments, - tag_layout: Layout::Union(UnionLayout::NonNullableUnwrapped(fields)), - union_size, - tag_id, - .. - } => { - debug_assert_eq!(*union_size, 1); - debug_assert_eq!(*tag_id, 0); - debug_assert_eq!(arguments.len(), fields.len()); - - let struct_layout = - Layout::Union(UnionLayout::NonRecursive(env.arena.alloc([*fields]))); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) { - let val = load_symbol(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - - field_vals.push(val); - } - } - } - - // Create the struct_type - let data_ptr = reserve_with_refcount(env, &struct_layout); - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - data_ptr.into() - } - - Tag { - arguments, - tag_layout: - Layout::Union(UnionLayout::NullableWrapped { - nullable_id, - other_tags: fields, - }), - union_size, - tag_id, - .. - } => { - let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields)); - let tag_struct_type = basic_type_from_layout(env, &tag_layout); - if *tag_id == *nullable_id as u8 { - let output_type = tag_struct_type.ptr_type(AddressSpace::Generic); - - return output_type.const_null().into(); - } - - debug_assert!(*union_size > 1); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - let tag_field_layouts = { - use std::cmp::Ordering::*; - match tag_id.cmp(&(*nullable_id as u8)) { - Equal => &[] as &[_], - Less => &fields[*tag_id as usize], - Greater => &fields[*tag_id as usize - 1], - } - }; - - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let val = load_symbol(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } - - // Create the struct_type - let data_ptr = reserve_with_refcount(env, &tag_layout); - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - data_ptr.into() - } - - Tag { - arguments, - tag_layout: - Layout::Union(UnionLayout::NullableUnwrapped { - nullable_id, - other_fields, - .. - }), - union_size, - tag_id, - tag_name, - .. - } => { - let other_fields = &other_fields[1..]; - - let tag_struct_type = - block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes); - - if *tag_id == *nullable_id as u8 { - let output_type = tag_struct_type.ptr_type(AddressSpace::Generic); - - return output_type.const_null().into(); - } - - // this tag id is not the nullable one. For the type to be recursive, the other - // constructor must have at least one argument! - debug_assert!(!arguments.is_empty()); - - debug_assert!(*union_size == 2); - - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - debug_assert!(!matches!(tag_name, TagName::Closure(_))); - - let tag_field_layouts = other_fields; - let arguments = &arguments[1..]; - - debug_assert_eq!(arguments.len(), tag_field_layouts.len()); - - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let val = load_symbol(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } - - // Create the struct_type - let data_ptr = reserve_with_refcount( - env, - &Layout::Union(UnionLayout::NonRecursive(&[other_fields])), - ); - - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - data_ptr.into() - } - - Tag { .. } => unreachable!("tags should have a Union or RecursiveUnion layout"), + } => build_tag(env, scope, union_layout, *union_size, *tag_id, arguments), Reset(_) => todo!(), Reuse { .. } => todo!(), - AccessAtIndex { - index, - structure, - wrapped: Wrapped::SingleElementRecord, - field_layouts, - .. - } => { - debug_assert_eq!(field_layouts.len(), 1); - debug_assert_eq!(*index, 0); - load_symbol(scope, structure) - } - - AccessAtIndex { - index, - structure, - wrapped: Wrapped::RecordOrSingleTagUnion, - .. + StructAtIndex { + index, structure, .. } => { // extract field from a record match load_symbol_and_layout(scope, structure) { @@ -1458,162 +1031,642 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env.builder.build_load(ptr, "load_rosetree_like") } - (other, layout) => unreachable!( - "can only index into struct layout\nValue: {:?}\nLayout: {:?}\nIndex: {:?}", - other, layout, index - ), + (other, layout) => { + // potential cause: indexing into an unwrapped 1-element record/tag? + unreachable!( + "can only index into struct layout\nValue: {:?}\nLayout: {:?}\nIndex: {:?}", + other, layout, index + ) + } } } - AccessAtIndex { - index, - structure, - field_layouts, - .. - } => { - use BasicValueEnum::*; + EmptyArray => empty_polymorphic_list(env), + Array { elem_layout, elems } => list_literal(env, scope, elem_layout, elems), + RuntimeErrorFunction(_) => todo!(), + UnionAtIndex { + tag_id, + structure, + index, + union_layout, + } => { let builder = env.builder; - // Determine types, assumes the descriminant is in the field layouts - let num_fields = field_layouts.len(); - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - - for field_layout in field_layouts.iter() { - let field_type = basic_type_from_layout(env, &field_layout); - field_types.push(field_type); - } - // cast the argument bytes into the desired shape for this tag - let (argument, structure_layout) = load_symbol_and_layout(scope, structure); + let (argument, _structure_layout) = load_symbol_and_layout(scope, structure); - match argument { - StructValue(value) => { + match union_layout { + UnionLayout::NonRecursive(tag_layouts) => { + debug_assert!(argument.is_struct_value()); + let field_layouts = tag_layouts[*tag_id as usize]; let struct_layout = Layout::Struct(field_layouts); - let struct_type = env - .context - .struct_type(field_types.into_bump_slice(), false); - let struct_value = access_index_struct_value(builder, value, struct_type); + let struct_type = basic_type_from_layout(env, &struct_layout); + + let struct_value = access_index_struct_value( + builder, + argument.into_struct_value(), + struct_type.into_struct_type(), + ); let result = builder .build_extract_value(struct_value, *index as u32, "") .expect("desired field did not decode"); - if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) { - let desired_type = - block_of_memory(env.context, &struct_layout, env.ptr_bytes); - - // the value is a pointer to the actual value; load that value! - let ptr = env.builder.build_bitcast( - result, - desired_type.ptr_type(AddressSpace::Generic), - "cast_struct_value_pointer", - ); - - builder.build_load(ptr.into_pointer_value(), "load_recursive_field") - } else { - result - } + result } - PointerValue(value) => match structure_layout { - Layout::Union(UnionLayout::NullableWrapped { nullable_id, .. }) - if *index == 0 => - { - let ptr = value; - let is_null = env.builder.build_is_null(ptr, "is_null"); + UnionLayout::Recursive(tag_layouts) => { + debug_assert!(argument.is_pointer_value()); - let ctx = env.context; - let then_block = ctx.append_basic_block(parent, "then"); - let else_block = ctx.append_basic_block(parent, "else"); - let cont_block = ctx.append_basic_block(parent, "cont"); + let field_layouts = tag_layouts[*tag_id as usize]; - let result = builder.build_alloca(ctx.i64_type(), "result"); + let tag_id_type = + basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type(); - env.builder - .build_conditional_branch(is_null, then_block, else_block); + lookup_at_index_ptr2( + env, + union_layout, + tag_id_type, + field_layouts, + *index as usize, + argument.into_pointer_value(), + ) + } + UnionLayout::NonNullableUnwrapped(field_layouts) => { + let struct_layout = Layout::Struct(&field_layouts); - { - env.builder.position_at_end(then_block); - let tag_id = ctx.i64_type().const_int(*nullable_id as u64, false); - env.builder.build_store(result, tag_id); - env.builder.build_unconditional_branch(cont_block); - } + let struct_type = basic_type_from_layout(env, &struct_layout); - { - env.builder.position_at_end(else_block); - let tag_id = extract_tag_discriminant_ptr(env, ptr); - env.builder.build_store(result, tag_id); - env.builder.build_unconditional_branch(cont_block); - } + lookup_at_index_ptr( + env, + union_layout, + field_layouts, + *index as usize, + argument.into_pointer_value(), + struct_type.into_struct_type(), + ) + } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + debug_assert!(argument.is_pointer_value()); + debug_assert_ne!(*tag_id as i64, *nullable_id); - env.builder.position_at_end(cont_block); + let tag_index = if (*tag_id as i64) < *nullable_id { + *tag_id + } else { + tag_id - 1 + }; - env.builder.build_load(result, "load_result") - } - Layout::Union(UnionLayout::NullableUnwrapped { nullable_id, .. }) => { - if *index == 0 { - let is_null = env.builder.build_is_null(value, "is_null"); + let field_layouts = other_tags[tag_index as usize]; - let ctx = env.context; + let tag_id_type = + basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type(); - let then_value = ctx.i64_type().const_int(*nullable_id as u64, false); - let else_value = ctx.i64_type().const_int(!*nullable_id as u64, false); + lookup_at_index_ptr2( + env, + union_layout, + tag_id_type, + field_layouts, + *index as usize, + argument.into_pointer_value(), + ) + } + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => { + debug_assert!(argument.is_pointer_value()); + debug_assert_ne!(*tag_id != 0, *nullable_id); - env.builder.build_select( - is_null, - then_value, - else_value, - "select_tag_id", - ) - } else { - let struct_type = env - .context - .struct_type(&field_types.into_bump_slice()[1..], false); + let field_layouts = other_fields; + let struct_layout = Layout::Struct(field_layouts); - lookup_at_index_ptr( - env, - &field_layouts[1..], - *index as usize - 1, - value, - struct_type, - structure_layout, - ) - } - } - _ => { - let struct_type = env - .context - .struct_type(field_types.into_bump_slice(), false); + let struct_type = basic_type_from_layout(env, &struct_layout); - lookup_at_index_ptr( - env, - field_layouts, - *index as usize, - value, - struct_type, - structure_layout, - ) - } - }, - _ => panic!("cannot look up index in {:?}", argument), + lookup_at_index_ptr( + env, + union_layout, + field_layouts, + // the tag id is not stored + *index as usize, + argument.into_pointer_value(), + struct_type.into_struct_type(), + ) + } } } - EmptyArray => empty_polymorphic_list(env), - Array { elem_layout, elems } => { - list_literal(env, layout.in_place(), scope, elem_layout, elems) + + GetTagId { + structure, + union_layout, + } => { + // cast the argument bytes into the desired shape for this tag + let (argument, _structure_layout) = load_symbol_and_layout(scope, structure); + + get_tag_id(env, parent, &union_layout, argument).into() + } + } +} + +pub fn build_tag<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + scope: &Scope<'a, 'ctx>, + union_layout: &UnionLayout<'a>, + union_size: u8, + tag_id: u8, + arguments: &[Symbol], +) -> BasicValueEnum<'ctx> { + let tag_id_layout = union_layout.tag_id_layout(); + + match union_layout { + UnionLayout::NonRecursive(tags) => { + debug_assert!(union_size > 1); + + let ctx = env.context; + let builder = env.builder; + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + let tag_field_layouts = &tags[tag_id as usize]; + + for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { + let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); + + // Zero-sized fields have no runtime representation. + // The layout of the struct expects them to be dropped! + if !tag_field_layout.is_dropped_because_empty() { + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + panic!( + r"non-recursive tag unions cannot directly contain a recursive pointer" + ); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + field_vals.push(val); + } + } + } + + // Create the struct_type + let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); + + // Insert field exprs into struct_val + let struct_val = + struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()); + + // How we create tag values + // + // The memory layout of tags can be different. e.g. in + // + // [ Ok Int, Err Str ] + // + // the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct. + // All tags of a union must have the same length, for easy addressing (e.g. array lookups). + // So we need to ask for the maximum of all tag's sizes, even if most tags won't use + // all that memory, and certainly won't use it in the same way (the tags have fields of + // different types/sizes) + // + // In llvm, we must be explicit about the type of value we're creating: we can't just + // make a unspecified block of memory. So what we do is create a byte array of the + // desired size. Then when we know which tag we have (which is here, in this function), + // we need to cast that down to the array of bytes that llvm expects + // + // There is the bitcast instruction, but it doesn't work for arrays. So we need to jump + // through some hoops using store and load to get this to work: the array is put into a + // one-element struct, which can be cast to the desired type. + // + // This tricks comes from + // https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116 + + let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes); + + let data = cast_tag_to_block_of_memory(builder, struct_val, internal_type); + let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); + let wrapper_type = env + .context + .struct_type(&[data.get_type(), tag_id_type.into()], false); + + let tag_id_intval = tag_id_type.const_int(tag_id as u64, false); + + let field_vals = [ + (TAG_DATA_INDEX as usize, data), + (TAG_ID_INDEX as usize, tag_id_intval.into()), + ]; + + struct_from_fields(env, wrapper_type, field_vals.iter().copied()).into() + } + UnionLayout::Recursive(tags) => { + debug_assert!(union_size > 1); + + let ctx = env.context; + let builder = env.builder; + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + let tag_field_layouts = &tags[tag_id as usize]; + + for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { + let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); + + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(val.is_pointer_value()); + + // we store recursive pointers as `i64*` + let ptr = env.builder.build_bitcast( + val, + ctx.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ); + + field_vals.push(ptr); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + field_vals.push(val); + } + } + + // Create the struct_type + let raw_data_ptr = + reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags); + + let tag_id_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") + .unwrap(); + + let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); + + env.builder + .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); + + let opaque_struct_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") + .unwrap(); + + let struct_type = env.context.struct_type(&field_types, false); + let struct_ptr = env + .builder + .build_bitcast( + opaque_struct_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + raw_data_ptr.into() + } + UnionLayout::NonNullableUnwrapped(fields) => { + debug_assert_eq!(union_size, 1); + debug_assert_eq!(tag_id, 0); + debug_assert_eq!(arguments.len(), fields.len()); + + let ctx = env.context; + let builder = env.builder; + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) { + let val = load_symbol(scope, field_symbol); + + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(val.is_pointer_value()); + + // we store recursive pointers as `i64*` + let ptr = env.builder.build_bitcast( + val, + ctx.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ); + + field_vals.push(ptr); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + + field_vals.push(val); + } + } + + // Create the struct_type + let data_ptr = + reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[fields]); + + let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); + let struct_ptr = env + .builder + .build_bitcast( + data_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "block_of_memory_to_tag", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + data_ptr.into() + } + UnionLayout::NullableWrapped { + nullable_id, + other_tags: tags, + } => { + if tag_id == *nullable_id as u8 { + let layout = Layout::Union(*union_layout); + + return basic_type_from_layout(env, &layout) + .into_pointer_type() + .const_null() + .into(); + } + + debug_assert!(union_size > 1); + + let ctx = env.context; + let builder = env.builder; + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + let tag_field_layouts = { + use std::cmp::Ordering::*; + match tag_id.cmp(&(*nullable_id as u8)) { + Equal => unreachable!("early return above"), + Less => &tags[tag_id as usize], + Greater => &tags[tag_id as usize - 1], + } + }; + + for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { + let val = load_symbol(scope, field_symbol); + + // Zero-sized fields have no runtime representation. + // The layout of the struct expects them to be dropped! + if !tag_field_layout.is_dropped_because_empty() { + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(val.is_pointer_value()); + + // we store recursive pointers as `i64*` + let ptr = env.builder.build_bitcast( + val, + ctx.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ); + + field_vals.push(ptr); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + field_vals.push(val); + } + } + } + + // Create the struct_type + let raw_data_ptr = + reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags); + + let tag_id_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") + .unwrap(); + + let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); + + env.builder + .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); + + let opaque_struct_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") + .unwrap(); + + let struct_type = env.context.struct_type(&field_types, false); + let struct_ptr = env + .builder + .build_bitcast( + opaque_struct_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + raw_data_ptr.into() + } + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => { + let tag_struct_type = + block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes); + + if tag_id == *nullable_id as u8 { + let output_type = tag_struct_type.ptr_type(AddressSpace::Generic); + + return output_type.const_null().into(); + } + + // this tag id is not the nullable one. For the type to be recursive, the other + // constructor must have at least one argument! + debug_assert!(!arguments.is_empty()); + + debug_assert!(union_size == 2); + + let ctx = env.context; + let builder = env.builder; + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + debug_assert_eq!(arguments.len(), other_fields.len()); + + for (field_symbol, tag_field_layout) in arguments.iter().zip(other_fields.iter()) { + let val = load_symbol(scope, field_symbol); + + // Zero-sized fields have no runtime representation. + // The layout of the struct expects them to be dropped! + if !tag_field_layout.is_dropped_because_empty() { + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(val.is_pointer_value()); + + // we store recursive pointers as `i64*` + let ptr = env.builder.build_bitcast( + val, + ctx.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ); + + field_vals.push(ptr); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + field_vals.push(val); + } + } + } + + // Create the struct_type + let data_ptr = + reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[other_fields]); + + let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); + let struct_ptr = env + .builder + .build_bitcast( + data_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "block_of_memory_to_tag", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + data_ptr.into() + } + } +} + +pub fn get_tag_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + parent: FunctionValue<'ctx>, + union_layout: &UnionLayout<'a>, + argument: BasicValueEnum<'ctx>, +) -> IntValue<'ctx> { + let builder = env.builder; + + let tag_id_layout = union_layout.tag_id_layout(); + let tag_id_int_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); + + match union_layout { + UnionLayout::NonRecursive(_) => { + let tag = argument.into_struct_value(); + + get_tag_id_non_recursive(env, tag) + } + UnionLayout::Recursive(_) => get_tag_id_wrapped(env, argument.into_pointer_value()), + UnionLayout::NonNullableUnwrapped(_) => tag_id_int_type.const_zero(), + UnionLayout::NullableWrapped { nullable_id, .. } => { + let argument_ptr = argument.into_pointer_value(); + let is_null = env.builder.build_is_null(argument_ptr, "is_null"); + + let ctx = env.context; + let then_block = ctx.append_basic_block(parent, "then"); + let else_block = ctx.append_basic_block(parent, "else"); + let cont_block = ctx.append_basic_block(parent, "cont"); + + let result = builder.build_alloca(tag_id_int_type, "result"); + + env.builder + .build_conditional_branch(is_null, then_block, else_block); + + { + env.builder.position_at_end(then_block); + let tag_id = tag_id_int_type.const_int(*nullable_id as u64, false); + env.builder.build_store(result, tag_id); + env.builder.build_unconditional_branch(cont_block); + } + + { + env.builder.position_at_end(else_block); + let tag_id = get_tag_id_wrapped(env, argument_ptr); + env.builder.build_store(result, tag_id); + env.builder.build_unconditional_branch(cont_block); + } + + env.builder.position_at_end(cont_block); + + env.builder + .build_load(result, "load_result") + .into_int_value() + } + UnionLayout::NullableUnwrapped { nullable_id, .. } => { + let argument_ptr = argument.into_pointer_value(); + let is_null = env.builder.build_is_null(argument_ptr, "is_null"); + + let then_value = tag_id_int_type.const_int(*nullable_id as u64, false); + let else_value = tag_id_int_type.const_int(!*nullable_id as u64, false); + + env.builder + .build_select(is_null, then_value, else_value, "select_tag_id") + .into_int_value() } - RuntimeErrorFunction(_) => todo!(), } } fn lookup_at_index_ptr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + union_layout: &UnionLayout<'a>, field_layouts: &[Layout<'_>], index: usize, value: PointerValue<'ctx>, struct_type: StructType<'ctx>, - structure_layout: &Layout<'_>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; @@ -1635,10 +1688,66 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) { // a recursive field is stored as a `i64*`, to use it we must cast it to // a pointer to the block of memory representation + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); + builder.build_bitcast( result, - basic_type_from_layout(env, structure_layout), - "cast_rec_pointer_lookup_at_index_ptr", + actual_type, + "cast_rec_pointer_lookup_at_index_ptr_old", + ) + } else { + result + } +} + +fn lookup_at_index_ptr2<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + union_layout: &UnionLayout<'a>, + tag_id_type: IntType<'ctx>, + field_layouts: &[Layout<'_>], + index: usize, + value: PointerValue<'ctx>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + + let struct_layout = Layout::Struct(field_layouts); + let struct_type = basic_type_from_layout(env, &struct_layout); + + let wrapper_type = env + .context + .struct_type(&[struct_type, tag_id_type.into()], false); + + let ptr = env + .builder + .build_bitcast( + value, + wrapper_type.ptr_type(AddressSpace::Generic), + "cast_lookup_at_index_ptr", + ) + .into_pointer_value(); + + let data_ptr = builder + .build_struct_gep(ptr, TAG_DATA_INDEX, "at_index_struct_gep") + .unwrap(); + + let elem_ptr = builder + .build_struct_gep(data_ptr, index as u32, "at_index_struct_gep") + .unwrap(); + + let result = builder.build_load(elem_ptr, "load_at_index_ptr"); + + if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) { + // a recursive field is stored as a `i64*`, to use it we must cast it to + // a pointer to the block of memory representation + + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); + + builder.build_bitcast( + result, + actual_type, + "cast_rec_pointer_lookup_at_index_ptr_new", ) } else { result @@ -1648,17 +1757,67 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( pub fn reserve_with_refcount<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>, +) -> PointerValue<'ctx> { + let stack_size = layout.stack_size(env.ptr_bytes); + let alignment_bytes = layout.alignment_bytes(env.ptr_bytes); + + let basic_type = basic_type_from_layout(env, layout); + + reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes) +} + +fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + union_layout: UnionLayout<'a>, + fields: &[&[Layout<'a>]], +) -> PointerValue<'ctx> { + let block_type = block_of_memory_slices(env.context, fields, env.ptr_bytes); + + let basic_type = if union_layout.stores_tag_id() { + let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); + + env.context + .struct_type(&[block_type, tag_id_type], false) + .into() + } else { + block_type + }; + + let mut stack_size = fields + .iter() + .map(|tag| tag.iter().map(|l| l.stack_size(env.ptr_bytes)).sum()) + .max() + .unwrap_or_default(); + + if union_layout.stores_tag_id() { + stack_size += union_layout.tag_id_layout().stack_size(env.ptr_bytes); + } + + let alignment_bytes = fields + .iter() + .map(|tag| tag.iter().map(|l| l.alignment_bytes(env.ptr_bytes))) + .flatten() + .max() + .unwrap_or(0); + + reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes) +} + +fn reserve_with_refcount_help<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + basic_type: impl BasicType<'ctx>, + stack_size: u32, + alignment_bytes: u32, ) -> PointerValue<'ctx> { let ctx = env.context; let len_type = env.ptr_int(); - let value_bytes = layout.stack_size(env.ptr_bytes); - let value_bytes_intvalue = len_type.const_int(value_bytes as u64, false); + let value_bytes_intvalue = len_type.const_int(stack_size as u64, false); let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes); - allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1) + allocate_with_refcount_help(env, basic_type, alignment_bytes, value_bytes_intvalue, rc1) } pub fn allocate_with_refcount<'a, 'ctx, 'env>( @@ -1676,17 +1835,17 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>( pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout: &Layout<'a>, + value_type: impl BasicType<'ctx>, + alignment_bytes: u32, number_of_data_bytes: IntValue<'ctx>, initial_refcount: IntValue<'ctx>, ) -> PointerValue<'ctx> { let builder = env.builder; let ctx = env.context; - let value_type = basic_type_from_layout(env, layout); let len_type = env.ptr_int(); - let extra_bytes = layout.alignment_bytes(env.ptr_bytes).max(env.ptr_bytes); + let extra_bytes = alignment_bytes.max(env.ptr_bytes); let ptr = { // number of bytes we will allocated @@ -1696,7 +1855,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( "add_extra_bytes", ); - env.call_alloc(number_of_bytes, layout.alignment_bytes(env.ptr_bytes)) + env.call_alloc(number_of_bytes, alignment_bytes) }; // We must return a pointer to the first element: @@ -1718,7 +1877,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( let index_intvalue = int_type.const_int(index, false); - let ptr_type = get_ptr_type(&value_type, AddressSpace::Generic); + let ptr_type = value_type.ptr_type(AddressSpace::Generic); unsafe { builder @@ -1756,7 +1915,6 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( fn list_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - inplace: InPlace, scope: &Scope<'a, 'ctx>, elem_layout: &Layout<'a>, elems: &&[Symbol], @@ -1770,7 +1928,7 @@ fn list_literal<'a, 'ctx, 'env>( let len_type = env.ptr_int(); let len = len_type.const_int(len_u64, false); - allocate_list(env, inplace, elem_layout, len) + allocate_list(env, elem_layout, len) }; // Copy the elements from the list literal into the array @@ -1816,15 +1974,17 @@ fn list_literal<'a, 'ctx, 'env>( fn invoke_roc_function<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, symbol: Symbol, layout: Layout<'a>, - function_value: Either, PointerValue<'ctx>>, + function_value: FunctionValue<'ctx>, arguments: &[Symbol], closure_argument: Option>, pass: &'a roc_mono::ir::Stmt<'a>, fail: &'a roc_mono::ir::Stmt<'a>, + exception_id: ExceptionId, ) -> BasicValueEnum<'ctx> { let context = env.context; @@ -1847,14 +2007,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>( "tmp", ); - match function_value { - Either::Left(function) => { - call.set_call_convention(function.get_call_conventions()); - } - Either::Right(_) => { - call.set_call_convention(FAST_CALL_CONV); - } - } + call.set_call_convention(function_value.get_call_conventions()); call.try_as_basic_value() .left() @@ -1866,7 +2019,7 @@ fn invoke_roc_function<'a, 'ctx, 'env>( scope.insert(symbol, (layout, call_result)); - build_exp_stmt(env, layout_ids, scope, parent, pass); + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass); scope.remove(&symbol); } @@ -1881,16 +2034,22 @@ fn invoke_roc_function<'a, 'ctx, 'env>( context.struct_type(&[exception_ptr, selector_value], false) }; - env.builder - .build_catch_all_landing_pad( - &landing_pad_type, - &BasicValueEnum::IntValue(context.i8_type().const_zero()), - context.i8_type().ptr_type(AddressSpace::Generic), - "invoke_landing_pad", - ) - .into_struct_value(); + let personality_function = get_gxx_personality_v0(env); - build_exp_stmt(env, layout_ids, scope, parent, fail); + let exception_object = env.builder.build_landing_pad( + landing_pad_type, + personality_function, + &[], + true, + "invoke_landing_pad", + ); + + scope.insert( + exception_id.into_inner(), + (Layout::Struct(&[]), exception_object), + ); + + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail); } call_result @@ -1925,6 +2084,7 @@ fn decrement_with_size_check<'a, 'ctx, 'env>( pub fn build_exp_stmt<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, stmt: &roc_mono::ir::Stmt<'a>, @@ -1949,7 +2109,15 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( for (symbol, expr, layout) in queue { debug_assert!(layout != &Layout::RecursivePointer); - let val = build_exp_expr(env, layout_ids, scope, parent, layout, &expr); + let val = build_exp_expr( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + layout, + &expr, + ); // Make a new scope which includes the binding we just encountered. // This should be done *after* compiling the bound expr, since any @@ -1962,7 +2130,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( stack.push(*symbol); } - let result = build_exp_stmt(env, layout_ids, scope, parent, cont); + let result = build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont); for symbol in stack { scope.remove(&symbol); @@ -1987,12 +2155,13 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( call, layout, pass, - fail: roc_mono::ir::Stmt::Rethrow, + fail: roc_mono::ir::Stmt::Resume(_), + exception_id: _, } => { // when the fail case is just Rethrow, there is no cleanup work to do // so we can just treat this invoke as a normal call let stmt = roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), *layout, pass); - build_exp_stmt(env, layout_ids, scope, parent, &stmt) + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, &stmt) } Invoke { @@ -2001,26 +2170,36 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( layout, pass, fail, + exception_id, } => match call.call_type { CallType::ByName { name, - ref full_layout, + arg_layouts, + ref ret_layout, + specialization_id, .. } => { - let function_value = function_value_by_name(env, layout_ids, *full_layout, name); + let bytes = specialization_id.to_bytes(); + let callee_var = CalleeSpecVar(&bytes); + let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap(); + + let function_value = + function_value_by_func_spec(env, func_spec, name, arg_layouts, ret_layout); invoke_roc_function( env, layout_ids, + func_spec_solutions, scope, parent, *symbol, *layout, - function_value.into(), + function_value, call.arguments, None, pass, fail, + *exception_id, ) } @@ -2030,6 +2209,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( } => build_foreign_symbol( env, layout_ids, + func_spec_solutions, scope, parent, foreign_symbol, @@ -2039,6 +2219,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( symbol: *symbol, pass, fail, + exception_id: *exception_id, }, ), @@ -2051,11 +2232,9 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( } }, - Rethrow => { - cxa_rethrow_exception(env); - - // used in exception handling - env.builder.build_unreachable(); + Resume(exception_id) => { + let exception_object = scope.get(&exception_id.into_inner()).unwrap().1; + env.builder.build_resume(exception_object); env.context.i64_type().const_zero().into() } @@ -2077,13 +2256,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( ret_type, }; - build_switch_ir(env, layout_ids, scope, parent, switch_args) + build_switch_ir( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + switch_args, + ) } Join { id, parameters, remainder, - continuation, + body: continuation, } => { let builder = env.builder; let context = env.context; @@ -2108,7 +2294,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( scope.join_points.insert(*id, (cont_block, joinpoint_args)); // construct the blocks that may jump to this join point - build_exp_stmt(env, layout_ids, scope, parent, remainder); + build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + remainder, + ); let phi_block = builder.get_insert_block().unwrap(); @@ -2121,7 +2314,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( } // put the continuation in - let result = build_exp_stmt(env, layout_ids, scope, parent, continuation); + let result = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + continuation, + ); // remove this join point again scope.join_points.remove(&id); @@ -2165,7 +2365,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( ); } - build_exp_stmt(env, layout_ids, scope, parent, cont) + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont) } Dec(symbol) => { let (value, layout) = load_symbol_and_layout(scope, symbol); @@ -2174,13 +2374,13 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( decrement_refcount_layout(env, parent, layout_ids, value, layout); } - build_exp_stmt(env, layout_ids, scope, parent, cont) + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont) } DecRef(symbol) => { let (value, layout) = load_symbol_and_layout(scope, symbol); match layout { - Layout::Builtin(Builtin::List(_, _)) => { + Layout::Builtin(Builtin::List(_)) => { debug_assert!(value.is_struct_value()); // because of how we insert DECREF for lists, we can't guarantee that @@ -2226,7 +2426,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( } } - build_exp_stmt(env, layout_ids, scope, parent, cont) + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont) } } } @@ -2327,8 +2527,6 @@ pub fn complex_bitcast<'ctx>( to_type: BasicTypeEnum<'ctx>, name: &str, ) -> BasicValueEnum<'ctx> { - use inkwell::types::BasicType; - // builder.build_bitcast(from_value, to_type, "cast_basic_basic") // because this does not allow some (valid) bitcasts @@ -2359,39 +2557,29 @@ pub fn complex_bitcast<'ctx>( } } -fn extract_tag_discriminant_struct<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - from_value: StructValue<'ctx>, -) -> IntValue<'ctx> { - let struct_type = env - .context - .struct_type(&[env.context.i64_type().into()], false); - - let struct_value = complex_bitcast_struct_struct( - env.builder, - from_value, - struct_type, - "extract_tag_discriminant_struct", - ); - - env.builder - .build_extract_value(struct_value, 0, "") - .expect("desired field did not decode") - .into_int_value() -} - -fn extract_tag_discriminant_ptr<'a, 'ctx, 'env>( +/// get the tag id out of a pointer to a wrapped (i.e. stores the tag id at runtime) layout +fn get_tag_id_wrapped<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, from_value: PointerValue<'ctx>, ) -> IntValue<'ctx> { - let tag_id_ptr_type = env.context.i64_type().ptr_type(AddressSpace::Generic); - - let ptr = env + let tag_id_ptr = env .builder - .build_bitcast(from_value, tag_id_ptr_type, "extract_tag_discriminant_ptr") - .into_pointer_value(); + .build_struct_gep(from_value, TAG_ID_INDEX, "tag_id_ptr") + .unwrap(); - env.builder.build_load(ptr, "load_tag_id").into_int_value() + env.builder + .build_load(tag_id_ptr, "load_tag_id") + .into_int_value() +} + +pub fn get_tag_id_non_recursive<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + tag: StructValue<'ctx>, +) -> IntValue<'ctx> { + env.builder + .build_extract_value(tag, TAG_ID_INDEX, "get_tag_id") + .unwrap() + .into_int_value() } struct SwitchArgsIr<'a, 'ctx> { @@ -2418,6 +2606,7 @@ fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValu fn build_switch_ir<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, switch_args: SwitchArgsIr<'a, 'ctx>, @@ -2470,57 +2659,9 @@ fn build_switch_ir<'a, 'ctx, 'env>( .into_int_value() } Layout::Union(variant) => { - use UnionLayout::*; + cond_layout = Layout::Builtin(Builtin::Int64); - match variant { - NonRecursive(_) => { - // we match on the discriminant, not the whole Tag - cond_layout = Layout::Builtin(Builtin::Int64); - let full_cond = cond_value.into_struct_value(); - - extract_tag_discriminant_struct(env, full_cond) - } - Recursive(_) => { - // we match on the discriminant, not the whole Tag - cond_layout = Layout::Builtin(Builtin::Int64); - - debug_assert!(cond_value.is_pointer_value()); - extract_tag_discriminant_ptr(env, cond_value.into_pointer_value()) - } - NonNullableUnwrapped(_) => unreachable!("there is no tag to switch on"), - NullableWrapped { nullable_id, .. } => { - // we match on the discriminant, not the whole Tag - cond_layout = Layout::Builtin(Builtin::Int64); - let full_cond_ptr = cond_value.into_pointer_value(); - - let comparison: IntValue = - env.builder.build_is_null(full_cond_ptr, "is_null_cond"); - - let when_null = || { - env.context - .i64_type() - .const_int(nullable_id as u64, false) - .into() - }; - let when_not_null = || extract_tag_discriminant_ptr(env, full_cond_ptr).into(); - - crate::llvm::build_list::build_basic_phi2( - env, - parent, - comparison, - when_null, - when_not_null, - BasicTypeEnum::IntType(env.context.i64_type()), - ) - .into_int_value() - } - NullableUnwrapped { .. } => { - // there are only two options, so we do a `tag_id == 0` check and branch on that - unreachable!( - "we never switch on the tag id directly for NullableUnwrapped unions" - ) - } - } + get_tag_id(env, parent, &variant, cond_value) } Layout::Builtin(_) => cond_value.into_int_value(), other => todo!("Build switch value from layout: {:?}", other), @@ -2540,7 +2681,14 @@ fn build_switch_ir<'a, 'ctx, 'env>( { builder.position_at_end(then_block); - let branch_val = build_exp_stmt(env, layout_ids, scope, parent, true_branch); + let branch_val = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + true_branch, + ); if then_block.get_terminator().is_none() { builder.build_unconditional_branch(cont_block); @@ -2551,7 +2699,14 @@ fn build_switch_ir<'a, 'ctx, 'env>( { builder.position_at_end(else_block); - let branch_val = build_exp_stmt(env, layout_ids, scope, parent, false_branch); + let branch_val = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + false_branch, + ); if else_block.get_terminator().is_none() { builder.build_unconditional_branch(cont_block); @@ -2579,18 +2734,14 @@ fn build_switch_ir<'a, 'ctx, 'env>( // ] // // they either need to all be i8, or i64 - let int_val = match cond_layout { - Layout::Builtin(Builtin::Usize) => { - ptr_int(env.context, env.ptr_bytes).const_int(*int as u64, false) - } - Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false), - Layout::Builtin(Builtin::Int128) => const_i128(env, *int as i128), - Layout::Builtin(Builtin::Int32) => context.i32_type().const_int(*int as u64, false), - Layout::Builtin(Builtin::Int16) => context.i16_type().const_int(*int as u64, false), - Layout::Builtin(Builtin::Int8) => context.i8_type().const_int(*int as u64, false), - Layout::Builtin(Builtin::Int1) => context.bool_type().const_int(*int as u64, false), - _ => panic!("Can't cast to cond_layout = {:?}", cond_layout), + let condition_int_type = cond.get_type(); + + let int_val = if condition_int_type == context.i128_type() { + const_i128(env, *int as i128) + } else { + condition_int_type.const_int(*int as u64, false) }; + let block = context.append_basic_block(parent, format!("branch{}", int).as_str()); cases.push((int_val, block)); @@ -2601,7 +2752,14 @@ fn build_switch_ir<'a, 'ctx, 'env>( for ((_, _, branch_expr), (_, block)) in branches.iter().zip(cases) { builder.position_at_end(block); - let branch_val = build_exp_stmt(env, layout_ids, scope, parent, branch_expr); + let branch_val = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + branch_expr, + ); if block.get_terminator().is_none() { builder.build_unconditional_branch(cont_block); @@ -2612,7 +2770,14 @@ fn build_switch_ir<'a, 'ctx, 'env>( // The block for the conditional's default branch. builder.position_at_end(default_block); - let default_val = build_exp_stmt(env, layout_ids, scope, parent, default_branch); + let default_val = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + scope, + parent, + default_branch, + ); if default_block.get_terminator().is_none() { builder.build_unconditional_branch(cont_block); @@ -2640,18 +2805,6 @@ fn build_switch_ir<'a, 'ctx, 'env>( } } -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -pub fn set_name(bv_enum: BasicValueEnum<'_>, name: &str) { - match bv_enum { - ArrayValue(val) => val.set_name(name), - IntValue(val) => val.set_name(name), - FloatValue(val) => val.set_name(name), - PointerValue(val) => val.set_name(name), - StructValue(val) => val.set_name(name), - VectorValue(val) => val.set_name(name), - } -} - /// Creates a new stack allocation instruction in the entry block of the function. pub fn create_entry_block_alloca<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, @@ -2672,21 +2825,22 @@ pub fn create_entry_block_alloca<'a, 'ctx>( fn expose_function_to_host<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + symbol: Symbol, roc_function: FunctionValue<'ctx>, ) { - let c_function_name: String = - format!("roc_{}_exposed", roc_function.get_name().to_str().unwrap()); + // Assumption: there is only one specialization of a host-exposed function + let ident_string = symbol.ident_string(&env.interns); + let c_function_name: String = format!("roc__{}_1_exposed", ident_string); - expose_function_to_host_help(env, roc_function, &c_function_name); + expose_function_to_host_help(env, ident_string, roc_function, &c_function_name); } fn expose_function_to_host_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + ident_string: &inlinable_string::InlinableString, roc_function: FunctionValue<'ctx>, c_function_name: &str, ) -> FunctionValue<'ctx> { - use inkwell::types::BasicType; - let roc_wrapper_function = make_exception_catcher(env, roc_function); let roc_function_type = roc_wrapper_function.get_type(); @@ -2744,8 +2898,7 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>( // STEP 3: build a {} -> u64 function that gives the size of the return type let size_function_type = env.context.i64_type().fn_type(&[], false); - let size_function_name: String = - format!("roc_{}_size", roc_function.get_name().to_str().unwrap()); + let size_function_name: String = format!("roc__{}_size", ident_string); let size_function = add_func( env.module, @@ -2774,18 +2927,17 @@ fn invoke_and_catch<'a, 'ctx, 'env, F, T>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, function: F, + calling_convention: u32, arguments: &[BasicValueEnum<'ctx>], return_type: T, ) -> BasicValueEnum<'ctx> where - F: Into, PointerValue<'ctx>>>, T: inkwell::types::BasicType<'ctx>, + F: Into>, { let context = env.context; let builder = env.builder; - let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); - let call_result_type = context.struct_type( &[context.i64_type().into(), return_type.as_basic_type_enum()], false, @@ -2806,7 +2958,7 @@ where catch_block, "call_roc_function", ); - call.set_call_convention(FAST_CALL_CONV); + call.set_call_convention(calling_convention); call.try_as_basic_value().left().unwrap() }; @@ -2814,71 +2966,7 @@ where { builder.position_at_end(catch_block); - let landing_pad_type = { - let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into(); - let selector_value = context.i32_type().into(); - - context.struct_type(&[exception_ptr, selector_value], false) - }; - - let info = builder - .build_catch_all_landing_pad( - &landing_pad_type, - &BasicValueEnum::IntValue(context.i8_type().const_zero()), - context.i8_type().ptr_type(AddressSpace::Generic), - "main_landing_pad", - ) - .into_struct_value(); - - let exception_ptr = builder - .build_extract_value(info, 0, "exception_ptr") - .unwrap(); - - let thrown = cxa_begin_catch(env, exception_ptr); - - let error_msg = { - let exception_type = u8_ptr; - let ptr = builder.build_bitcast( - thrown, - exception_type.ptr_type(AddressSpace::Generic), - "cast", - ); - - builder.build_load(ptr.into_pointer_value(), "error_msg") - }; - - let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false); - - let return_value = { - let v1 = return_type.const_zero(); - - // flag is non-zero, indicating failure - let flag = context.i64_type().const_int(1, false); - - let v2 = builder - .build_insert_value(v1, flag, 0, "set_error") - .unwrap(); - - let v3 = builder - .build_insert_value(v2, error_msg, 1, "set_exception") - .unwrap(); - - v3 - }; - - // bitcast result alloca so we can store our concrete type { flag, error_msg } in there - let result_alloca_bitcast = builder - .build_bitcast( - result_alloca, - return_type.ptr_type(AddressSpace::Generic), - "result_alloca_bitcast", - ) - .into_pointer_value(); - - // store our return value - builder.build_store(result_alloca_bitcast, return_value); - - cxa_end_catch(env); + build_catch_all_landing_pad(env, result_alloca); builder.build_unconditional_branch(cont_block); } @@ -2911,14 +2999,89 @@ where builder.position_at_end(cont_block); - let result = builder.build_load(result_alloca, "result"); + builder.build_load(result_alloca, "result") +} - // MUST set the personality at the very end; - // doing it earlier can cause the personality to be ignored - let personality_func = get_gxx_personality_v0(env); - parent.set_personality_function(personality_func); +fn build_catch_all_landing_pad<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + result_alloca: PointerValue<'ctx>, +) { + let context = env.context; + let builder = env.builder; - result + let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); + + let landing_pad_type = { + let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into(); + let selector_value = context.i32_type().into(); + + context.struct_type(&[exception_ptr, selector_value], false) + }; + + // null pointer functions as a catch-all catch clause + let null = u8_ptr.const_zero(); + + let personality_function = get_gxx_personality_v0(env); + + let info = builder + .build_landing_pad( + landing_pad_type, + personality_function, + &[null.into()], + false, + "main_landing_pad", + ) + .into_struct_value(); + + let exception_ptr = builder + .build_extract_value(info, 0, "exception_ptr") + .unwrap(); + + let thrown = cxa_begin_catch(env, exception_ptr); + + let error_msg = { + let exception_type = u8_ptr; + let ptr = builder.build_bitcast( + thrown, + exception_type.ptr_type(AddressSpace::Generic), + "cast", + ); + + builder.build_load(ptr.into_pointer_value(), "error_msg") + }; + + let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false); + + let return_value = { + let v1 = return_type.const_zero(); + + // flag is non-zero, indicating failure + let flag = context.i64_type().const_int(1, false); + + let v2 = builder + .build_insert_value(v1, flag, 0, "set_error") + .unwrap(); + + let v3 = builder + .build_insert_value(v2, error_msg, 1, "set_exception") + .unwrap(); + + v3 + }; + + // bitcast result alloca so we can store our concrete type { flag, error_msg } in there + let result_alloca_bitcast = builder + .build_bitcast( + result_alloca, + return_type.ptr_type(AddressSpace::Generic), + "result_alloca_bitcast", + ) + .into_pointer_value(); + + // store our return value + builder.build_store(result_alloca_bitcast, return_value); + + cxa_end_catch(env); } fn make_exception_catcher<'a, 'ctx, 'env>( @@ -2984,35 +3147,197 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>( env, wrapper_function, roc_function, + roc_function.get_call_conventions(), &arguments, roc_function_type.get_return_type().unwrap(), ); builder.build_return(Some(&result)); - // MUST set the personality at the very end; - // doing it earlier can cause the personality to be ignored - let personality_func = get_gxx_personality_v0(env); - wrapper_function.set_personality_function(personality_func); - wrapper_function } -pub fn build_proc_header<'a, 'ctx, 'env>( +pub fn build_proc_headers<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, + mod_solutions: &'a ModSolutions, + procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, + scope: &mut Scope<'a, 'ctx>, + // alias_analysis_solutions: AliasAnalysisSolutions, +) -> Vec< + 'a, + ( + roc_mono::ir::Proc<'a>, + &'a [(&'a FuncSpecSolutions, FunctionValue<'ctx>)], + ), +> { + // Populate Procs further and get the low-level Expr from the canonical Expr + let mut headers = Vec::with_capacity_in(procedures.len(), env.arena); + for ((symbol, layout), proc) in procedures { + let name_bytes = roc_mono::alias_analysis::func_name_bytes(&proc); + let func_name = FuncName(&name_bytes); + + let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); + + let it = func_solutions.specs(); + let mut function_values = Vec::with_capacity_in(it.size_hint().0, env.arena); + for specialization in it { + let fn_val = build_proc_header(env, *specialization, symbol, &proc); + + if proc.args.is_empty() { + // this is a 0-argument thunk, i.e. a top-level constant definition + // it must be in-scope everywhere in the module! + scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val); + } + + let func_spec_solutions = func_solutions.spec(specialization).unwrap(); + + function_values.push((func_spec_solutions, fn_val)); + } + headers.push((proc, function_values.into_bump_slice())); + } + + headers +} + +pub fn build_procedures<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + opt_level: OptLevel, + procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, + entry_point: EntryPoint<'a>, + debug_output_file: Option<&Path>, +) { + build_procedures_help(env, opt_level, procedures, entry_point, debug_output_file); +} + +pub fn build_procedures_return_main<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + opt_level: OptLevel, + procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, + entry_point: EntryPoint<'a>, +) -> (&'static str, FunctionValue<'ctx>) { + let mod_solutions = build_procedures_help(env, opt_level, procedures, entry_point, None); + + promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout) +} + +fn build_procedures_help<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + opt_level: OptLevel, + procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, + entry_point: EntryPoint<'a>, + debug_output_file: Option<&Path>, +) -> &'a ModSolutions { + let mut layout_ids = roc_mono::layout::LayoutIds::default(); + let mut scope = Scope::default(); + + let it = procedures.iter().map(|x| x.1); + + let solutions = match roc_mono::alias_analysis::spec_program(entry_point, it) { + Err(e) => panic!("Error in alias analysis: {}", e), + Ok(solutions) => solutions, + }; + + let solutions = env.arena.alloc(solutions); + + let mod_solutions = solutions + .mod_solutions(roc_mono::alias_analysis::MOD_APP) + .unwrap(); + + // Add all the Proc headers to the module. + // We have to do this in a separate pass first, + // because their bodies may reference each other. + let headers = build_proc_headers(env, &mod_solutions, procedures, &mut scope); + + let (_, function_pass) = construct_optimization_passes(env.module, opt_level); + + for (proc, fn_vals) in headers { + for (func_spec_solutions, fn_val) in fn_vals { + let mut current_scope = scope.clone(); + + // only have top-level thunks for this proc's module in scope + // this retain is not needed for correctness, but will cause less confusion when debugging + let home = proc.name.module_id(); + current_scope.retain_top_level_thunks_for_module(home); + + build_proc( + &env, + mod_solutions, + &mut layout_ids, + func_spec_solutions, + scope.clone(), + &proc, + *fn_val, + ); + + // call finalize() before any code generation/verification + env.dibuilder.finalize(); + + if fn_val.verify(true) { + function_pass.run_on(&fn_val); + } else { + let mode = "NON-OPTIMIZED"; + + eprintln!( + "\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n", + fn_val.get_name().to_str().unwrap(), + mode, + ); + + fn_val.print_to_stderr(); + + if let Some(app_ll_file) = debug_output_file { + env.module.print_to_file(&app_ll_file).unwrap(); + + panic!( + r"😱 LLVM errors when defining function {:?}; I wrote the full LLVM IR to {:?}", + fn_val.get_name().to_str().unwrap(), + app_ll_file, + ); + } else { + panic!( + "The preceding code was from {:?}, which failed LLVM verification in {} build.", + fn_val.get_name().to_str().unwrap(), + mode, + ) + } + } + } + } + + mod_solutions +} + +fn func_spec_name<'a>( + arena: &'a Bump, + interns: &Interns, + symbol: Symbol, + func_spec: FuncSpec, +) -> bumpalo::collections::String<'a> { + use std::fmt::Write; + + let mut buf = bumpalo::collections::String::with_capacity_in(1, arena); + + let ident_string = symbol.ident_string(interns); + let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap(); + write!(buf, "{}_{}_", module_string, ident_string).unwrap(); + + for byte in func_spec.0.iter() { + write!(buf, "{:x?}", byte).unwrap(); + } + + buf +} + +fn build_proc_header<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + func_spec: FuncSpec, symbol: Symbol, - top_level: TopLevelFunctionLayout<'a>, proc: &roc_mono::ir::Proc<'a>, ) -> FunctionValue<'ctx> { - let layout = env.arena.alloc(top_level).full(); - let args = proc.args; let arena = env.arena; - let fn_name = layout_ids - .get(symbol, &layout) - .to_symbol_string(symbol, &env.interns); + let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec); let ret_type = basic_type_from_layout(env, &proc.ret_layout); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); @@ -3023,7 +3348,7 @@ pub fn build_proc_header<'a, 'ctx, 'env>( arg_basic_types.push(arg_type); } - let fn_type = get_fn_type(&ret_type, &arg_basic_types); + let fn_type = ret_type.fn_type(&arg_basic_types, false); let fn_val = add_func( env.module, @@ -3037,7 +3362,7 @@ pub fn build_proc_header<'a, 'ctx, 'env>( fn_val.set_subprogram(subprogram); if env.exposed_to_host.contains(&symbol) { - expose_function_to_host(env, fn_val); + expose_function_to_host(env, symbol, fn_val); } fn_val @@ -3052,8 +3377,6 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( lambda_set: LambdaSet<'a>, result: &Layout<'a>, ) { - use inkwell::types::BasicType; - let context = &env.context; let builder = env.builder; @@ -3061,7 +3384,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( // e.g. `roc__main_1_Fx_caller` let function_name = format!( - "roc_{}_{}_caller", + "roc__{}_{}_caller", def_name, alias_symbol.ident_string(&env.interns) ); @@ -3119,8 +3442,14 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( *param = builder.build_load(param.into_pointer_value(), "load_param"); } - let call_result = - invoke_and_catch(env, function_value, evaluator, &[closure_data], result_type); + let call_result = invoke_and_catch( + env, + function_value, + evaluator, + evaluator.get_call_conventions(), + &[closure_data], + result_type, + ); builder.build_store(output, call_result); @@ -3136,138 +3465,22 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( ); // STEP 4: build a {} -> u64 function that gives the size of the closure - let layout = Layout::Closure(arguments, lambda_set, result); - build_host_exposed_alias_size(env, def_name, alias_symbol, &layout); -} - -fn build_function_caller<'a, 'ctx, 'env>( - env: &'a Env<'a, 'ctx, 'env>, - def_name: &str, - alias_symbol: Symbol, - arguments: &[Layout<'a>], - result: &Layout<'a>, -) { - use inkwell::types::BasicType; - - let context = &env.context; - let builder = env.builder; - - // STEP 1: build function header - - // e.g. `roc__main_1_Fx_caller` - let function_name = format!( - "roc_{}_{}_caller", - def_name, - alias_symbol.ident_string(&env.interns) - ); - - let mut argument_types = Vec::with_capacity_in(arguments.len() + 3, env.arena); - - for layout in arguments { - let arg_type = basic_type_from_layout(env, layout); - let arg_ptr_type = arg_type.ptr_type(AddressSpace::Generic); - - argument_types.push(arg_ptr_type.into()); - } - - let function_pointer_type = { - let mut args = Vec::new_in(env.arena); - args.extend(arguments.iter().cloned()); - - // pretend the closure layout is empty - args.push(Layout::Struct(&[])); - - let function_layout = Layout::FunctionPointer(&args, result); - - // this is already a (function) pointer type - basic_type_from_layout(env, &function_layout) - }; - argument_types.push(function_pointer_type); - - let closure_argument_type = { - let basic_type = basic_type_from_layout(env, &Layout::Struct(&[])); - - basic_type.ptr_type(AddressSpace::Generic) - }; - argument_types.push(closure_argument_type.into()); - - let result_type = basic_type_from_layout(env, result); - - let roc_call_result_type = - context.struct_type(&[context.i64_type().into(), result_type], false); - - let output_type = { roc_call_result_type.ptr_type(AddressSpace::Generic) }; - argument_types.push(output_type.into()); - - let function_type = context.void_type().fn_type(&argument_types, false); - - let function_value = add_func( - env.module, - function_name.as_str(), - function_type, - Linkage::External, - C_CALL_CONV, - ); - - // STEP 2: build function body - - let entry = context.append_basic_block(function_value, "entry"); - - builder.position_at_end(entry); - - let mut parameters = function_value.get_params(); - let output = parameters.pop().unwrap().into_pointer_value(); - let _closure_data_ptr = parameters.pop().unwrap().into_pointer_value(); - let function_ptr = parameters.pop().unwrap().into_pointer_value(); - - // let closure_data = context.struct_type(&[], false).const_zero().into(); - - let actual_function_type = - basic_type_from_layout(env, &Layout::FunctionPointer(arguments, result)); - - let function_ptr = builder - .build_bitcast(function_ptr, actual_function_type, "cast") - .into_pointer_value(); - - let mut parameters = parameters; - - for param in parameters.iter_mut() { - debug_assert!(param.is_pointer_value()); - *param = builder.build_load(param.into_pointer_value(), "load_param"); - } - - let call_result = invoke_and_catch(env, function_value, function_ptr, ¶meters, result_type); - - builder.build_store(output, call_result); - - builder.build_return(None); - - // STEP 3: build a {} -> u64 function that gives the size of the return type - build_host_exposed_alias_size_help( - env, - def_name, - alias_symbol, - Some("result"), - roc_call_result_type.into(), - ); - - // STEP 4: build a {} -> u64 function that gives the size of the function - let layout = Layout::FunctionPointer(arguments, result); - build_host_exposed_alias_size(env, def_name, alias_symbol, &layout); + let layout = lambda_set.runtime_representation(); + build_host_exposed_alias_size(env, def_name, alias_symbol, layout); } fn build_host_exposed_alias_size<'a, 'ctx, 'env>( env: &'a Env<'a, 'ctx, 'env>, def_name: &str, alias_symbol: Symbol, - layout: &Layout<'a>, + layout: Layout<'a>, ) { build_host_exposed_alias_size_help( env, def_name, alias_symbol, None, - basic_type_from_layout(env, layout), + basic_type_from_layout(env, &layout), ) } @@ -3284,14 +3497,14 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>( let size_function_type = env.context.i64_type().fn_type(&[], false); let size_function_name: String = if let Some(label) = opt_label { format!( - "roc_{}_{}_{}_size", + "roc__{}_{}_{}_size", def_name, alias_symbol.ident_string(&env.interns), label ) } else { format!( - "roc_{}_{}_size", + "roc__{}_{}_size", def_name, alias_symbol.ident_string(&env.interns) ) @@ -3309,62 +3522,68 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>( builder.position_at_end(entry); - use inkwell::types::BasicType; let size: BasicValueEnum = basic_type.size_of().unwrap().into(); builder.build_return(Some(&size)); } pub fn build_proc<'a, 'ctx, 'env>( env: &'a Env<'a, 'ctx, 'env>, + mod_solutions: &'a ModSolutions, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, mut scope: Scope<'a, 'ctx>, - proc: roc_mono::ir::Proc<'a>, + proc: &roc_mono::ir::Proc<'a>, fn_val: FunctionValue<'ctx>, ) { use roc_mono::ir::HostExposedLayouts; + use roc_mono::layout::RawFunctionLayout; let copy = proc.host_exposed_layouts.clone(); - let fn_name = fn_val.get_name().to_string_lossy(); match copy { HostExposedLayouts::NotHostExposed => {} HostExposedLayouts::HostExposed { rigids: _, aliases } => { for (name, (symbol, top_level, layout)) in aliases { match layout { - Layout::Closure(arguments, closure, result) => { + RawFunctionLayout::Function(arguments, closure, result) => { // define closure size and return value size, e.g. // // * roc__mainForHost_1_Update_size() -> i64 // * roc__mainForHost_1_Update_result_size() -> i64 - let evaluator_layout = env.arena.alloc(top_level).full(); - let evaluator_name = layout_ids - .get(symbol, &evaluator_layout) - .to_symbol_string(symbol, &env.interns); - - let evaluator = function_value_by_name_help( - env, - evaluator_layout, + let it = top_level.arguments.iter().copied(); + let bytes = roc_mono::alias_analysis::func_name_bytes_help( symbol, - &evaluator_name, + it, + top_level.result, ); + let func_name = FuncName(&bytes); + let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); + + let mut it = func_solutions.specs(); + let func_spec = it.next().unwrap(); + debug_assert!( + it.next().is_none(), + "we expect only one specialization of this symbol" + ); + + let evaluator = function_value_by_func_spec( + env, + *func_spec, + symbol, + top_level.arguments, + &top_level.result, + ); + + let ident_string = proc.name.ident_string(&env.interns); + let fn_name: String = format!("{}_1", ident_string); build_closure_caller( env, &fn_name, evaluator, name, arguments, closure, result, ) } - Layout::FunctionPointer(arguments, result) => { - // define function size (equal to pointer size) and return value size, e.g. - // - // * roc__mainForHost_1_Update_size() -> i64 - // * roc__mainForHost_1_Update_result_size() -> i64 - build_function_caller(env, &fn_name, name, arguments, result) - } - Layout::Builtin(_) => {} - Layout::PhantomEmptyStruct => {} - Layout::Struct(_) => {} - Layout::Union(_) => {} - Layout::RecursivePointer => {} - Layout::Pointer(_) => {} + RawFunctionLayout::ZeroArgumentThunk(_) => { + // do nothing + } } } } @@ -3383,11 +3602,18 @@ pub fn build_proc<'a, 'ctx, 'env>( // Add args to scope for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) { - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); scope.insert(*arg_symbol, (*layout, arg_val)); } - let body = build_exp_stmt(env, layout_ids, &mut scope, fn_val, &proc.body); + let body = build_exp_stmt( + env, + layout_ids, + func_spec_solutions, + &mut scope, + fn_val, + &proc.body, + ); // only add a return if codegen did not already add one if let Some(block) = builder.get_insert_block() { @@ -3407,23 +3633,23 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) { } } -fn function_value_by_name<'a, 'ctx, 'env>( +fn function_value_by_func_spec<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - layout: Layout<'a>, + func_spec: FuncSpec, symbol: Symbol, + arguments: &[Layout<'a>], + result: &Layout<'a>, ) -> FunctionValue<'ctx> { - let fn_name = layout_ids - .get(symbol, &layout) - .to_symbol_string(symbol, &env.interns); + let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec); let fn_name = fn_name.as_str(); - function_value_by_name_help(env, layout, symbol, fn_name) + function_value_by_name_help(env, arguments, result, symbol, fn_name) } fn function_value_by_name_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout: Layout<'a>, + arguments: &[Layout<'a>], + result: &Layout<'a>, symbol: Symbol, fn_name: &str, ) -> FunctionValue<'ctx> { @@ -3431,7 +3657,8 @@ fn function_value_by_name_help<'a, 'ctx, 'env>( if symbol.is_builtin() { eprintln!( "Unrecognized builtin function: {:?}\nLayout: {:?}\n", - fn_name, layout + fn_name, + (arguments, result) ); eprintln!("Is the function defined? If so, maybe there is a problem with the layout"); @@ -3443,7 +3670,9 @@ fn function_value_by_name_help<'a, 'ctx, 'env>( // Unrecognized non-builtin function: eprintln!( "Unrecognized non-builtin function: {:?}\n\nSymbol: {:?}\nLayout: {:?}\n", - fn_name, symbol, layout + fn_name, + symbol, + (arguments, result) ); eprintln!("Is the function defined? If so, maybe there is a problem with the layout"); @@ -3457,15 +3686,16 @@ fn function_value_by_name_help<'a, 'ctx, 'env>( // #[allow(clippy::cognitive_complexity)] #[inline(always)] -fn call_with_args<'a, 'ctx, 'env>( +fn roc_call_with_args<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - layout: &Layout<'a>, + argument_layouts: &[Layout<'a>], + result_layout: &Layout<'a>, symbol: Symbol, - _parent: FunctionValue<'ctx>, + func_spec: FuncSpec, args: &[BasicValueEnum<'ctx>], ) -> BasicValueEnum<'ctx> { - let fn_val = function_value_by_name(env, layout_ids, *layout, symbol); + let fn_val = + function_value_by_func_spec(env, func_spec, symbol, argument_layouts, result_layout); let call = env.builder.build_call(fn_val, args, "call"); @@ -3478,8 +3708,8 @@ fn call_with_args<'a, 'ctx, 'env>( /// Translates a target_lexicon::Triple to a LLVM calling convention u32 /// as described in https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html -pub fn get_call_conventions(cc: CallingConvention) -> u32 { - use CallingConvention::*; +pub fn get_call_conventions(cc: target_lexicon::CallingConvention) -> u32 { + use target_lexicon::CallingConvention::*; // For now, we're returning 0 for the C calling convention on all of these. // Not sure if we should be picking something more specific! @@ -3491,9 +3721,9 @@ pub fn get_call_conventions(cc: CallingConvention) -> u32 { } /// Source: https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html -pub static C_CALL_CONV: u32 = 0; -pub static FAST_CALL_CONV: u32 = 8; -pub static COLD_CALL_CONV: u32 = 9; +pub const C_CALL_CONV: u32 = 0; +pub const FAST_CALL_CONV: u32 = 8; +pub const COLD_CALL_CONV: u32 = 9; pub struct RocFunctionCall<'ctx> { pub caller: PointerValue<'ctx>, @@ -3547,7 +3777,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( scope: &Scope<'a, 'ctx>, return_layout: &Layout<'a>, op: LowLevel, - function_layout: Layout<'a>, + func_spec: FuncSpec, + argument_layouts: &[Layout<'a>], + result_layout: &Layout<'a>, function_owns_closure_data: bool, args: &[Symbol], ) -> BasicValueEnum<'ctx> { @@ -3560,18 +3792,13 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( ($index:expr) => {{ let function_symbol = args[$index]; - let fn_name = layout_ids - .get(function_symbol, &function_layout) - .to_symbol_string(function_symbol, &env.interns); - - env.module - .get_function(fn_name.as_str()) - .unwrap_or_else(|| { - panic!( - "Could not get pointer to unknown function {:?} {:?}", - fn_name, function_layout - ) - }) + function_value_by_func_spec( + env, + func_spec, + function_symbol, + argument_layouts, + return_layout, + ) }}; } @@ -3587,7 +3814,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => default, - Layout::Builtin(Builtin::List(_, element_layout)) => { + Layout::Builtin(Builtin::List(element_layout)) => { let argument_layouts = &[**element_layout, *default_layout]; let roc_function_call = roc_function_call( @@ -3604,6 +3831,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( env, layout_ids, roc_function_call, + result_layout, list, element_layout, default, @@ -3629,8 +3857,8 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match (list_layout, return_layout) { (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), ( - Layout::Builtin(Builtin::List(_, element_layout)), - Layout::Builtin(Builtin::List(_, result_layout)), + Layout::Builtin(Builtin::List(element_layout)), + Layout::Builtin(Builtin::List(result_layout)), ) => { let argument_layouts = &[**element_layout]; @@ -3660,9 +3888,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match (list1_layout, list2_layout, return_layout) { ( - Layout::Builtin(Builtin::List(_, element1_layout)), - Layout::Builtin(Builtin::List(_, element2_layout)), - Layout::Builtin(Builtin::List(_, result_layout)), + Layout::Builtin(Builtin::List(element1_layout)), + Layout::Builtin(Builtin::List(element2_layout)), + Layout::Builtin(Builtin::List(result_layout)), ) => { let argument_layouts = &[**element1_layout, **element2_layout]; @@ -3704,10 +3932,10 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match (list1_layout, list2_layout, list3_layout, return_layout) { ( - Layout::Builtin(Builtin::List(_, element1_layout)), - Layout::Builtin(Builtin::List(_, element2_layout)), - Layout::Builtin(Builtin::List(_, element3_layout)), - Layout::Builtin(Builtin::List(_, result_layout)), + Layout::Builtin(Builtin::List(element1_layout)), + Layout::Builtin(Builtin::List(element2_layout)), + Layout::Builtin(Builtin::List(element3_layout)), + Layout::Builtin(Builtin::List(result_layout)), ) => { let argument_layouts = &[**element1_layout, **element2_layout, **element3_layout]; @@ -3754,8 +3982,8 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match (list_layout, return_layout) { (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), ( - Layout::Builtin(Builtin::List(_, element_layout)), - Layout::Builtin(Builtin::List(_, result_layout)), + Layout::Builtin(Builtin::List(element_layout)), + Layout::Builtin(Builtin::List(result_layout)), ) => { let argument_layouts = &[Layout::Builtin(Builtin::Usize), **element_layout]; @@ -3786,7 +4014,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => { + Layout::Builtin(Builtin::List(element_layout)) => { let argument_layouts = &[**element_layout]; let roc_function_call = roc_function_call( @@ -3818,8 +4046,8 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( (_, Layout::Builtin(Builtin::EmptyList)) | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), ( - Layout::Builtin(Builtin::List(_, before_layout)), - Layout::Builtin(Builtin::List(_, after_layout)), + Layout::Builtin(Builtin::List(before_layout)), + Layout::Builtin(Builtin::List(after_layout)), ) => { let argument_layouts = &[**before_layout]; @@ -3837,7 +4065,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( env, layout_ids, roc_function_call, - &function_layout, + result_layout, list, before_layout, after_layout, @@ -3862,8 +4090,8 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( (_, Layout::Builtin(Builtin::EmptyList)) | (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), ( - Layout::Builtin(Builtin::List(_, before_layout)), - Layout::Builtin(Builtin::List(_, after_layout)), + Layout::Builtin(Builtin::List(before_layout)), + Layout::Builtin(Builtin::List(after_layout)), ) => { let argument_layouts = &[**before_layout]; @@ -3881,7 +4109,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( env, layout_ids, roc_function_call, - &function_layout, + result_layout, list, before_layout, after_layout, @@ -3913,7 +4141,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => { + Layout::Builtin(Builtin::List(element_layout)) => { use crate::llvm::bitcode::build_compare_wrapper; let argument_layouts = &[**element_layout, **element_layout]; @@ -3972,7 +4200,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( dict_walk( env, - layout_ids, roc_function_call, dict, default, @@ -3997,6 +4224,7 @@ fn run_low_level<'a, 'ctx, 'env>( layout: &Layout<'a>, op: LowLevel, args: &[Symbol], + update_mode: Option, ) -> BasicValueEnum<'ctx> { use LowLevel::*; @@ -4007,13 +4235,13 @@ fn run_low_level<'a, 'ctx, 'env>( // Str.concat : Str, Str -> Str debug_assert_eq!(args.len(), 2); - str_concat(env, layout.in_place(), scope, args[0], args[1]) + str_concat(env, scope, args[0], args[1]) } StrJoinWith => { // Str.joinWith : List Str, Str -> Str debug_assert_eq!(args.len(), 2); - str_join_with(env, layout.in_place(), scope, args[0], args[1]) + str_join_with(env, scope, args[0], args[1]) } StrStartsWith => { // Str.startsWith : Str, Str -> Bool @@ -4067,7 +4295,7 @@ fn run_low_level<'a, 'ctx, 'env>( // Str.split : Str, Str -> List Str debug_assert_eq!(args.len(), 2); - str_split(env, scope, layout.in_place(), args[0], args[1]) + str_split(env, scope, args[0], args[1]) } StrIsEmpty => { // Str.isEmpty : Str -> Str @@ -4102,7 +4330,7 @@ fn run_low_level<'a, 'ctx, 'env>( let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]); - list_single(env, layout.in_place(), arg, arg_layout) + list_single(env, arg, arg_layout) } ListRepeat => { // List.repeat : Int, elem -> List elem @@ -4119,7 +4347,7 @@ fn run_low_level<'a, 'ctx, 'env>( let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - list_reverse(env, layout.in_place(), list, list_layout) + list_reverse(env, list, list_layout) } ListConcat => { debug_assert_eq!(args.len(), 2); @@ -4128,14 +4356,7 @@ fn run_low_level<'a, 'ctx, 'env>( let second_list = load_symbol(scope, &args[1]); - list_concat( - env, - layout.in_place(), - parent, - first_list, - second_list, - list_layout, - ) + list_concat(env, parent, first_list, second_list, list_layout) } ListContains => { // List.contains : List elem, elem -> Bool @@ -4168,7 +4389,29 @@ fn run_low_level<'a, 'ctx, 'env>( let original_wrapper = load_symbol(scope, &args[0]).into_struct_value(); let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]); - list_append(env, layout.in_place(), original_wrapper, elem, elem_layout) + list_append(env, original_wrapper, elem, elem_layout) + } + ListSwap => { + // List.swap : List elem, Nat, Nat -> List elem + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + let original_wrapper = list.into_struct_value(); + + let index_1 = load_symbol(scope, &args[1]); + let index_2 = load_symbol(scope, &args[2]); + + match list_layout { + Layout::Builtin(Builtin::EmptyList) => empty_list(env), + Layout::Builtin(Builtin::List(element_layout)) => list_swap( + env, + original_wrapper, + index_1.into_int_value(), + index_2.into_int_value(), + element_layout, + ), + _ => unreachable!("Invalid layout {:?} in List.swap", list_layout), + } } ListDrop => { // List.drop : List elem, Nat -> List elem @@ -4181,7 +4424,7 @@ fn run_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => empty_list(env), - Layout::Builtin(Builtin::List(_, element_layout)) => list_drop( + Layout::Builtin(Builtin::List(element_layout)) => list_drop( env, layout_ids, original_wrapper, @@ -4198,7 +4441,7 @@ fn run_low_level<'a, 'ctx, 'env>( let original_wrapper = load_symbol(scope, &args[0]).into_struct_value(); let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]); - list_prepend(env, layout.in_place(), original_wrapper, elem, elem_layout) + list_prepend(env, original_wrapper, elem, elem_layout) } ListJoin => { // List.join : List (List elem) -> List elem @@ -4206,7 +4449,7 @@ fn run_low_level<'a, 'ctx, 'env>( let (list, outer_list_layout) = load_symbol_and_layout(scope, &args[0]); - list_join(env, layout.in_place(), parent, list, outer_list_layout) + list_join(env, parent, list, outer_list_layout) } NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos | NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => { @@ -4424,7 +4667,7 @@ fn run_low_level<'a, 'ctx, 'env>( BasicValueEnum::IntValue(bool_val) } ListGetUnsafe => { - // List.get : List elem, Int -> [ Ok elem, OutOfBounds ]* + // List.get : List elem, Nat -> [ Ok elem, OutOfBounds ]* debug_assert_eq!(args.len(), 2); let (wrapper_struct, list_layout) = load_symbol_and_layout(scope, &args[0]); @@ -4440,27 +4683,6 @@ fn run_low_level<'a, 'ctx, 'env>( wrapper_struct, ) } - ListSetInPlace => { - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - let (index, _) = load_symbol_and_layout(scope, &args[1]); - let (element, _) = load_symbol_and_layout(scope, &args[2]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => { - // no elements, so nothing to remove - empty_list(env) - } - Layout::Builtin(Builtin::List(_, element_layout)) => list_set( - env, - layout_ids, - list, - index.into_int_value(), - element, - element_layout, - ), - _ => unreachable!("invalid dict layout"), - } - } ListSet => { let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); let (index, _) = load_symbol_and_layout(scope, &args[1]); @@ -4471,13 +4693,14 @@ fn run_low_level<'a, 'ctx, 'env>( // no elements, so nothing to remove empty_list(env) } - Layout::Builtin(Builtin::List(_, element_layout)) => list_set( + Layout::Builtin(Builtin::List(element_layout)) => list_set( env, layout_ids, list, index.into_int_value(), element, element_layout, + update_mode.unwrap(), ), _ => unreachable!("invalid dict layout"), } @@ -4648,7 +4871,7 @@ fn run_low_level<'a, 'ctx, 'env>( match list_layout { Layout::Builtin(Builtin::EmptyList) => dict_empty(env), - Layout::Builtin(Builtin::List(_, key_layout)) => { + Layout::Builtin(Builtin::List(key_layout)) => { set_from_list(env, layout_ids, list, key_layout) } _ => unreachable!("invalid dict layout"), @@ -4693,6 +4916,7 @@ enum ForeignCallOrInvoke<'a> { Call, Invoke { symbol: Symbol, + exception_id: ExceptionId, pass: &'a roc_mono::ir::Stmt<'a>, fail: &'a roc_mono::ir::Stmt<'a>, }, @@ -4716,7 +4940,7 @@ fn build_foreign_symbol_return_result<'a, 'ctx, 'env>( arg_types.push(arg_type); } - let function_type = get_fn_type(&return_type, &arg_types); + let function_type = return_type.fn_type(&arg_types, false); let function = get_foreign_symbol(env, foreign.clone(), function_type); (function, arg_vals.into_bump_slice()) @@ -4753,6 +4977,7 @@ fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>( fn build_foreign_symbol<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + func_spec_solutions: &FuncSpecSolutions, scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, foreign: &roc_module::ident::ForeignSymbol, @@ -4787,7 +5012,12 @@ fn build_foreign_symbol<'a, 'ctx, 'env>( call.try_as_basic_value().left().unwrap() } } - ForeignCallOrInvoke::Invoke { symbol, pass, fail } => { + ForeignCallOrInvoke::Invoke { + symbol, + pass, + fail, + exception_id, + } => { let pass_block = env.context.append_basic_block(parent, "invoke_pass"); let fail_block = env.context.append_basic_block(parent, "invoke_fail"); @@ -4811,7 +5041,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>( scope.insert(symbol, (*ret_layout, call_result)); - build_exp_stmt(env, layout_ids, scope, parent, pass); + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, pass); scope.remove(&symbol); } @@ -4828,16 +5058,22 @@ fn build_foreign_symbol<'a, 'ctx, 'env>( .struct_type(&[exception_ptr, selector_value], false) }; - env.builder - .build_catch_all_landing_pad( - &landing_pad_type, - &BasicValueEnum::IntValue(env.context.i8_type().const_zero()), - env.context.i8_type().ptr_type(AddressSpace::Generic), - "invoke_landing_pad", - ) - .into_struct_value(); + let personality_function = get_gxx_personality_v0(env); - build_exp_stmt(env, layout_ids, scope, parent, fail); + let exception_object = env.builder.build_landing_pad( + landing_pad_type, + personality_function, + &[], + true, + "invoke_landing_pad", + ); + + scope.insert( + exception_id.into_inner(), + (Layout::Struct(&[]), exception_object), + ); + + build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, fail); } call_result @@ -5004,7 +5240,7 @@ fn build_int_binop<'a, 'ctx, 'env>( // rem == 0 // } // - // NOTE we'd like the branches to be swapped for better branch prediciton, + // NOTE we'd like the branches to be swapped for better branch prediction, // but llvm normalizes to the above ordering in -O3 let zero = rhs.get_type().const_zero(); let neg_1 = rhs.get_type().const_int(-1i64 as u64, false); @@ -5351,7 +5587,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>( int_abs_raise_on_overflow(env, arg, arg_layout) } NumToFloat => { - // TODO: Handle differnt sized numbers + // TODO: Handle different sized numbers // This is an Int, so we need to convert it. bd.build_cast( InstructionOpcode::SIToFP, @@ -5475,7 +5711,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>( let bd = env.builder; - // TODO: Handle differnt sized floats + // TODO: Handle different sized floats match op { NumNeg => bd.build_float_neg(arg, "negate_float").into(), NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]), @@ -5616,7 +5852,7 @@ fn throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, message: &str) { let builder = env.builder; let info = { - // we represend both void and char pointers with `u8*` + // we represented both void and char pointers with `u8*` let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic); // allocate an exception (that can hold a pointer to a string) @@ -5737,32 +5973,6 @@ fn cxa_throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, info: BasicVal call.set_call_convention(C_CALL_CONV); } -fn cxa_rethrow_exception(env: &Env<'_, '_, '_>) { - let name = "__cxa_rethrow"; - - let module = env.module; - let context = env.context; - - let function = match module.get_function(&name) { - Some(gvalue) => gvalue, - None => { - let cxa_rethrow = add_func( - module, - name, - context.void_type().fn_type(&[], false), - Linkage::External, - C_CALL_CONV, - ); - - cxa_rethrow - } - }; - let call = env.builder.build_call(function, &[], "rethrow"); - - call.set_call_convention(C_CALL_CONV); - // call.try_as_basic_value().left().unwrap() -} - fn get_foreign_symbol<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, foreign_symbol: roc_module::ident::ForeignSymbol, @@ -5797,7 +6007,7 @@ fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> Function None => { let personality_func = add_func( module, - "__gxx_personality_v0", + name, context.i64_type().fn_type(&[], false), Linkage::External, C_CALL_CONV, @@ -5849,7 +6059,7 @@ fn cxa_begin_catch<'a, 'ctx, 'env>( let cxa_begin_catch = add_func( module, - "__cxa_begin_catch", + name, u8_ptr.fn_type(&[u8_ptr.into()], false), Linkage::External, C_CALL_CONV, diff --git a/compiler/gen/src/llvm/build_dict.rs b/compiler/gen_llvm/src/llvm/build_dict.rs similarity index 97% rename from compiler/gen/src/llvm/build_dict.rs rename to compiler/gen_llvm/src/llvm/build_dict.rs index d95a557ad5..73f799466e 100644 --- a/compiler/gen/src/llvm/build_dict.rs +++ b/compiler/gen_llvm/src/llvm/build_dict.rs @@ -3,14 +3,14 @@ use crate::llvm::bitcode::{ build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, call_bitcode_fn, call_void_bitcode_fn, }; use crate::llvm::build::{ - complex_bitcast, load_symbol, load_symbol_and_layout, set_name, Env, RocFunctionCall, Scope, + complex_bitcast, load_symbol, load_symbol_and_layout, Env, RocFunctionCall, Scope, }; use crate::llvm::build_list::{layout_width, pass_as_opaque}; -use crate::llvm::convert::{as_const_zero, basic_type_from_layout}; +use crate::llvm::convert::basic_type_from_layout; use crate::llvm::refcounting::Mode; use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::types::BasicType; -use inkwell::values::{BasicValueEnum, FunctionValue, StructValue}; +use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, StructValue}; use inkwell::AddressSpace; use roc_builtins::bitcode; use roc_module::symbol::Symbol; @@ -326,7 +326,7 @@ pub fn dict_get<'a, 'ctx, 'env>( let done_block = env.context.append_basic_block(parent, "done"); let value_bt = basic_type_from_layout(env, value_layout); - let default = as_const_zero(&value_bt); + let default = value_bt.const_zero(); env.builder .build_conditional_branch(flag, if_not_null, done_block); @@ -635,7 +635,6 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>( #[allow(clippy::too_many_arguments)] pub fn dict_walk<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, roc_function_call: RocFunctionCall<'ctx>, dict: BasicValueEnum<'ctx>, accum: BasicValueEnum<'ctx>, @@ -660,9 +659,6 @@ pub fn dict_walk<'a, 'ctx, 'env>( let output_ptr = builder.build_alloca(accum_bt, "output_ptr"); - let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout); - let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout); - call_void_bitcode_fn( env, &[ @@ -676,8 +672,6 @@ pub fn dict_walk<'a, 'ctx, 'env>( layout_width(env, key_layout), layout_width(env, value_layout), layout_width(env, accum_layout), - inc_key_fn.as_global_value().as_pointer_value().into(), - inc_value_fn.as_global_value().as_pointer_value().into(), env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"), ], &bitcode::DICT_WALK, @@ -836,8 +830,8 @@ fn build_hash_wrapper<'a, 'ctx, 'env>( let seed_arg = it.next().unwrap().into_int_value(); let value_ptr = it.next().unwrap().into_pointer_value(); - set_name(seed_arg.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value_ptr.into(), Symbol::ARG_2.ident_string(&env.interns)); + seed_arg.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value_ptr.set_name(Symbol::ARG_2.ident_string(&env.interns)); let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic); diff --git a/compiler/gen/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs similarity index 83% rename from compiler/gen/src/llvm/build_hash.rs rename to compiler/gen_llvm/src/llvm/build_hash.rs index 95db536a88..61a74dd232 100644 --- a/compiler/gen/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -1,11 +1,13 @@ use crate::debug_info_init; use crate::llvm::bitcode::call_bitcode_fn; use crate::llvm::build::Env; -use crate::llvm::build::{cast_block_of_memory_to_tag, complex_bitcast, set_name, FAST_CALL_CONV}; +use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX}; use crate::llvm::build_str; use crate::llvm::convert::basic_type_from_layout; use bumpalo::collections::Vec; -use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; +use inkwell::values::{ + BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, +}; use roc_builtins::bitcode; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; @@ -56,11 +58,6 @@ fn build_hash_layout<'a, 'ctx, 'env>( val.into_struct_value(), ), - Layout::PhantomEmptyStruct => { - // just does nothing and returns the seed - seed - } - Layout::Union(union_layout) => { build_hash_tag(env, layout_ids, layout, union_layout, seed, val) } @@ -91,11 +88,7 @@ fn build_hash_layout<'a, 'ctx, 'env>( } }, - Layout::Pointer(_) => { - unreachable!("unused") - } - - Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => { + Layout::Closure(_, _, _) => { unreachable!("the type system will guarantee these are never hashed") } } @@ -157,7 +150,7 @@ fn hash_builtin<'a, 'ctx, 'env>( Builtin::Set(_) => { todo!("Implement Hash for Set") } - Builtin::List(_, element_layout) => build_hash_list( + Builtin::List(element_layout) => build_hash_list( env, layout_ids, layout, @@ -241,8 +234,8 @@ fn build_hash_struct_help<'a, 'ctx, 'env>( let seed = it.next().unwrap().into_int_value(); let value = it.next().unwrap().into_struct_value(); - set_name(seed.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value.into(), Symbol::ARG_2.ident_string(&env.interns)); + seed.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); env.builder.position_at_end(entry); @@ -382,8 +375,8 @@ fn build_hash_tag_help<'a, 'ctx, 'env>( let seed = it.next().unwrap().into_int_value(); let value = it.next().unwrap(); - set_name(seed.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value, Symbol::ARG_2.ident_string(&env.interns)); + seed.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); env.builder.position_at_end(entry); @@ -408,13 +401,15 @@ fn hash_tag<'a, 'ctx, 'env>( let merge_block = env.context.append_basic_block(parent, "merge_block"); env.builder.position_at_end(merge_block); - let merge_phi = env.builder.build_phi(env.context.i64_type(), "merge_hash"); + let tag_id_layout = union_layout.tag_id_layout(); + let tag_id_basic_type = basic_type_from_layout(env, &tag_id_layout); + + let merge_phi = env.builder.build_phi(seed.get_type(), "merge_hash"); env.builder.position_at_end(entry_block); match union_layout { NonRecursive(tags) => { - // SAFETY we know that non-recursive tags cannot be NULL - let tag_id = nonrec_tag_id(env, tag.into_struct_value()); + let current_tag_id = get_tag_id(env, parent, union_layout, tag); let mut cases = Vec::with_capacity_in(tags.len(), env.arena); @@ -422,7 +417,6 @@ fn hash_tag<'a, 'ctx, 'env>( let block = env.context.append_basic_block(parent, "tag_id_modify"); env.builder.position_at_end(block); - // TODO drop tag id? let struct_layout = Layout::Struct(field_layouts); let wrapper_type = basic_type_from_layout(env, &struct_layout); @@ -431,6 +425,24 @@ fn hash_tag<'a, 'ctx, 'env>( let as_struct = cast_block_of_memory_to_tag(env.builder, tag.into_struct_value(), wrapper_type); + // hash the tag id + let hash_bytes = store_and_use_as_u8_ptr( + env, + tag_id_basic_type + .into_int_type() + .const_int(tag_id as u64, false) + .into(), + &tag_id_layout, + ); + + let seed = hash_bitcode_fn( + env, + seed, + hash_bytes, + tag_id_layout.stack_size(env.ptr_bytes), + ); + + // hash the tag data let answer = build_hash_struct( env, layout_ids, @@ -444,7 +456,7 @@ fn hash_tag<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(merge_block); cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), + current_tag_id.get_type().const_int(tag_id as u64, false), block, )); } @@ -453,11 +465,10 @@ fn hash_tag<'a, 'ctx, 'env>( let default = cases.pop().unwrap().1; - env.builder.build_switch(tag_id, default, &cases); + env.builder.build_switch(current_tag_id, default, &cases); } Recursive(tags) => { - // SAFETY recursive tag unions are not NULL - let tag_id = unsafe { rec_tag_id_unsafe(env, tag.into_pointer_value()) }; + let current_tag_id = get_tag_id(env, parent, union_layout, tag); let mut cases = Vec::with_capacity_in(tags.len(), env.arena); @@ -465,6 +476,23 @@ fn hash_tag<'a, 'ctx, 'env>( let block = env.context.append_basic_block(parent, "tag_id_modify"); env.builder.position_at_end(block); + // hash the tag id + let hash_bytes = store_and_use_as_u8_ptr( + env, + tag_id_basic_type + .into_int_type() + .const_int(tag_id as u64, false) + .into(), + &tag_id_layout, + ); + let seed = hash_bitcode_fn( + env, + seed, + hash_bytes, + tag_id_layout.stack_size(env.ptr_bytes), + ); + + // hash the tag data let answer = hash_ptr_to_struct( env, layout_ids, @@ -478,7 +506,7 @@ fn hash_tag<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(merge_block); cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), + current_tag_id.get_type().const_int(tag_id as u64, false), block, )); } @@ -487,11 +515,10 @@ fn hash_tag<'a, 'ctx, 'env>( let default = cases.pop().unwrap().1; - env.builder.build_switch(tag_id, default, &cases); + env.builder.build_switch(current_tag_id, default, &cases); } NullableUnwrapped { other_fields, .. } => { let tag = tag.into_pointer_value(); - let other_fields = &other_fields[1..]; let is_null = env.builder.build_is_null(tag, "is_null"); @@ -520,7 +547,10 @@ fn hash_tag<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(merge_block); } } - NullableWrapped { other_tags, .. } => { + NullableWrapped { + other_tags, + nullable_id, + } => { let tag = tag.into_pointer_value(); let is_null = env.builder.build_is_null(tag, "is_null"); @@ -541,31 +571,57 @@ fn hash_tag<'a, 'ctx, 'env>( } { - env.builder.position_at_end(hash_other_block); - - // SAFETY recursive tag unions are not NULL - let tag_id = unsafe { rec_tag_id_unsafe(env, tag) }; - let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena); - for (tag_id, field_layouts) in other_tags.iter().enumerate() { + for (mut tag_id, field_layouts) in other_tags.iter().enumerate() { + if tag_id >= *nullable_id as usize { + tag_id += 1; + } + let block = env.context.append_basic_block(parent, "tag_id_modify"); env.builder.position_at_end(block); - let answer = - hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag); + // hash the tag id + let hash_bytes = store_and_use_as_u8_ptr( + env, + tag_id_basic_type + .into_int_type() + .const_int(tag_id as u64, false) + .into(), + &tag_id_layout, + ); + let seed1 = hash_bitcode_fn( + env, + seed, + hash_bytes, + tag_id_layout.stack_size(env.ptr_bytes), + ); + + // hash tag data + let answer = hash_ptr_to_struct( + env, + layout_ids, + union_layout, + field_layouts, + seed1, + tag, + ); merge_phi.add_incoming(&[(&answer, block)]); env.builder.build_unconditional_branch(merge_block); cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), + tag_id_basic_type + .into_int_type() + .const_int(tag_id as u64, false), block, )); } env.builder.position_at_end(hash_other_block); + let tag_id = get_tag_id(env, parent, union_layout, tag.into()); + let default = cases.pop().unwrap().1; env.builder.build_switch(tag_id, default, &cases); @@ -662,8 +718,8 @@ fn build_hash_list_help<'a, 'ctx, 'env>( let seed = it.next().unwrap().into_int_value(); let value = it.next().unwrap().into_struct_value(); - set_name(seed.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(value.into(), Symbol::ARG_2.ident_string(&env.interns)); + seed.set_name(Symbol::ARG_1.ident_string(&env.interns)); + value.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); env.builder.position_at_end(entry); @@ -772,18 +828,27 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>( ) -> IntValue<'ctx> { use inkwell::types::BasicType; - let struct_layout = Layout::Struct(field_layouts); - - let wrapper_type = basic_type_from_layout(env, &struct_layout); - debug_assert!(wrapper_type.is_struct_type()); + let wrapper_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); // cast the opaque pointer to a pointer of the correct shape + let wrapper_ptr = env + .builder + .build_bitcast(tag, wrapper_type, "opaque_to_correct") + .into_pointer_value(); + + let struct_ptr = env + .builder + .build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data") + .unwrap(); + + let struct_layout = Layout::Struct(field_layouts); + let struct_type = basic_type_from_layout(env, &struct_layout); let struct_ptr = env .builder .build_bitcast( - tag, - wrapper_type.ptr_type(inkwell::AddressSpace::Generic), - "opaque_to_correct", + struct_ptr, + struct_type.ptr_type(inkwell::AddressSpace::Generic), + "cast_tag_data", ) .into_pointer_value(); @@ -811,14 +876,13 @@ fn store_and_use_as_u8_ptr<'a, 'ctx, 'env>( let alloc = env.builder.build_alloca(basic_type, "store"); env.builder.build_store(alloc, value); + let u8_ptr = env + .context + .i8_type() + .ptr_type(inkwell::AddressSpace::Generic); + env.builder - .build_bitcast( - alloc, - env.context - .i8_type() - .ptr_type(inkwell::AddressSpace::Generic), - "as_u8_ptr", - ) + .build_bitcast(alloc, u8_ptr, "as_u8_ptr") .into_pointer_value() } @@ -837,34 +901,3 @@ fn hash_bitcode_fn<'a, 'ctx, 'env>( ) .into_int_value() } - -fn nonrec_tag_id<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - tag: StructValue<'ctx>, -) -> IntValue<'ctx> { - complex_bitcast( - env.builder, - tag.into(), - env.context.i64_type().into(), - "load_tag_id", - ) - .into_int_value() -} - -unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - tag: PointerValue<'ctx>, -) -> IntValue<'ctx> { - let ptr = env - .builder - .build_bitcast( - tag, - env.context - .i64_type() - .ptr_type(inkwell::AddressSpace::Generic), - "cast_for_tag_id", - ) - .into_pointer_value(); - - env.builder.build_load(ptr, "load_tag_id").into_int_value() -} diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs similarity index 88% rename from compiler/gen/src/llvm/build_list.rs rename to compiler/gen_llvm/src/llvm/build_list.rs index f71e4a507a..7aa7e07ead 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -1,20 +1,21 @@ #![allow(clippy::too_many_arguments)] use crate::llvm::bitcode::{ - build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, build_inc_wrapper, - build_transform_caller, call_bitcode_fn, call_void_bitcode_fn, + build_dec_wrapper, build_eq_wrapper, build_has_tag_id, build_inc_n_wrapper, build_inc_wrapper, + call_bitcode_fn, call_void_bitcode_fn, }; use crate::llvm::build::{ allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, RocFunctionCall, }; -use crate::llvm::convert::{basic_type_from_layout, get_ptr_type}; +use crate::llvm::convert::basic_type_from_layout; use crate::llvm::refcounting::increment_refcount_layout; use inkwell::builder::Builder; use inkwell::context::Context; -use inkwell::types::{BasicTypeEnum, PointerType}; +use inkwell::types::{BasicType, BasicTypeEnum, PointerType}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::{AddressSpace, IntPredicate}; +use morphic_lib::UpdateMode; use roc_builtins::bitcode; -use roc_mono::layout::{Builtin, InPlace, Layout, LayoutIds, MemoryMode}; +use roc_mono::layout::{Builtin, Layout, LayoutIds}; fn list_returned_from_zig<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -83,7 +84,6 @@ pub fn pass_as_opaque<'a, 'ctx, 'env>( /// List.single : a -> List a pub fn list_single<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _inplace: InPlace, element: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { @@ -124,7 +124,6 @@ pub fn list_repeat<'a, 'ctx, 'env>( /// List.prepend : List elem, elem -> List elem pub fn list_prepend<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - inplace: InPlace, original_wrapper: StructValue<'ctx>, elem: BasicValueEnum<'ctx>, elem_layout: &Layout<'a>, @@ -135,7 +134,7 @@ pub fn list_prepend<'a, 'ctx, 'env>( // Load the usize length from the wrapper. let len = list_len(builder, original_wrapper); let elem_type = basic_type_from_layout(env, elem_layout); - let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); + let ptr_type = elem_type.ptr_type(AddressSpace::Generic); let list_ptr = load_list_ptr(builder, original_wrapper, ptr_type); // The output list length, which is the old list length + 1 @@ -146,7 +145,7 @@ pub fn list_prepend<'a, 'ctx, 'env>( ); // Allocate space for the new array that we'll copy into. - let clone_ptr = allocate_list(env, inplace, elem_layout, new_list_len); + let clone_ptr = allocate_list(env, elem_layout, new_list_len); builder.build_store(clone_ptr, elem); @@ -189,19 +188,18 @@ pub fn list_prepend<'a, 'ctx, 'env>( /// List.join : List (List elem) -> List elem pub fn list_join<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _inplace: InPlace, _parent: FunctionValue<'ctx>, outer_list: BasicValueEnum<'ctx>, outer_list_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { match outer_list_layout { Layout::Builtin(Builtin::EmptyList) - | Layout::Builtin(Builtin::List(_, Layout::Builtin(Builtin::EmptyList))) => { + | Layout::Builtin(Builtin::List(Layout::Builtin(Builtin::EmptyList))) => { // If the input list is empty, or if it is a list of empty lists // then simply return an empty list empty_list(env) } - Layout::Builtin(Builtin::List(_, Layout::Builtin(Builtin::List(_, element_layout)))) => { + Layout::Builtin(Builtin::List(Layout::Builtin(Builtin::List(element_layout)))) => { call_bitcode_fn_returns_list( env, &[ @@ -221,23 +219,16 @@ pub fn list_join<'a, 'ctx, 'env>( /// List.reverse : List elem -> List elem pub fn list_reverse<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _output_inplace: InPlace, list: BasicValueEnum<'ctx>, list_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - let (_, element_layout) = match *list_layout { - Layout::Builtin(Builtin::EmptyList) => ( - InPlace::InPlace, + let element_layout = match *list_layout { + Layout::Builtin(Builtin::EmptyList) => { // this pointer will never actually be dereferenced - Layout::Builtin(Builtin::Int64), - ), - Layout::Builtin(Builtin::List(memory_mode, elem_layout)) => ( - match memory_mode { - MemoryMode::Unique => InPlace::InPlace, - MemoryMode::Refcounted => InPlace::Clone, - }, - *elem_layout, - ), + Layout::Builtin(Builtin::Int64) + } + + Layout::Builtin(Builtin::List(elem_layout)) => *elem_layout, _ => unreachable!("Invalid layout {:?} in List.reverse", list_layout), }; @@ -264,9 +255,9 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>( let builder = env.builder; match list_layout { - Layout::Builtin(Builtin::List(_, elem_layout)) => { + Layout::Builtin(Builtin::List(elem_layout)) => { let elem_type = basic_type_from_layout(env, elem_layout); - let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic); + let ptr_type = elem_type.ptr_type(AddressSpace::Generic); // Load the pointer to the array data let array_data_ptr = load_list_ptr(builder, wrapper_struct, ptr_type); @@ -293,7 +284,6 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>( /// List.append : List elem, elem -> List elem pub fn list_append<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _inplace: InPlace, original_wrapper: StructValue<'ctx>, element: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, @@ -310,7 +300,28 @@ pub fn list_append<'a, 'ctx, 'env>( ) } -/// List.drop : List elem, Nat -> List elem +/// List.swap : List elem, Nat, Nat -> List elem +pub fn list_swap<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + original_wrapper: StructValue<'ctx>, + index_1: IntValue<'ctx>, + index_2: IntValue<'ctx>, + element_layout: &Layout<'a>, +) -> BasicValueEnum<'ctx> { + call_bitcode_fn_returns_list( + env, + &[ + pass_list_as_i128(env, original_wrapper.into()), + env.alignment_intvalue(&element_layout), + layout_width(env, &element_layout), + index_1.into(), + index_2.into(), + ], + &bitcode::LIST_SWAP, + ) +} + +/// List.drop : List elem, Nat, Nat -> List elem pub fn list_drop<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, @@ -340,6 +351,7 @@ pub fn list_set<'a, 'ctx, 'env>( index: IntValue<'ctx>, element: BasicValueEnum<'ctx>, element_layout: &'a Layout<'a>, + update_mode: UpdateMode, ) -> BasicValueEnum<'ctx> { let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); @@ -349,19 +361,32 @@ pub fn list_set<'a, 'ctx, 'env>( env.context.i8_type().ptr_type(AddressSpace::Generic), ); - let new_bytes = call_bitcode_fn( - env, - &[ - bytes.into(), - length.into(), - env.alignment_intvalue(&element_layout), - index.into(), - pass_element_as_opaque(env, element), - layout_width(env, element_layout), - dec_element_fn.as_global_value().as_pointer_value().into(), - ], - &bitcode::LIST_SET, - ); + let new_bytes = match update_mode { + UpdateMode::InPlace => call_bitcode_fn( + env, + &[ + bytes.into(), + index.into(), + pass_element_as_opaque(env, element), + layout_width(env, element_layout), + dec_element_fn.as_global_value().as_pointer_value().into(), + ], + bitcode::LIST_SET_IN_PLACE, + ), + UpdateMode::Immutable => call_bitcode_fn( + env, + &[ + bytes.into(), + length.into(), + env.alignment_intvalue(&element_layout), + index.into(), + pass_element_as_opaque(env, element), + layout_width(env, element_layout), + dec_element_fn.as_global_value().as_pointer_value().into(), + ], + bitcode::LIST_SET, + ), + }; store_list(env, new_bytes.into_pointer_value(), length) } @@ -400,6 +425,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, roc_function_call: RocFunctionCall<'ctx>, + function_call_return_layout: &Layout<'a>, list: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, default: BasicValueEnum<'ctx>, @@ -440,6 +466,18 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( ); } ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => { + let function = env + .builder + .get_insert_block() + .unwrap() + .get_parent() + .unwrap(); + + let has_tag_id = match function_call_return_layout { + Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout), + _ => unreachable!(), + }; + let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); call_void_bitcode_fn( env, @@ -452,7 +490,9 @@ pub fn list_walk_generic<'a, 'ctx, 'env>( pass_as_opaque(env, default_ptr), env.alignment_intvalue(&element_layout), layout_width(env, element_layout), + layout_width(env, function_call_return_layout), layout_width(env, default_layout), + has_tag_id.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(), pass_as_opaque(env, result_ptr), ], @@ -586,20 +626,26 @@ pub fn list_keep_oks<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, roc_function_call: RocFunctionCall<'ctx>, - function_layout: &Layout<'a>, + // Layout of the `Result after *` + result_layout: &Layout<'a>, list: BasicValueEnum<'ctx>, before_layout: &Layout<'a>, after_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - // Layout of the `Result after *` - let result_layout = match function_layout { - Layout::FunctionPointer(_, ret) => ret, - Layout::Closure(_, _, ret) => ret, - _ => unreachable!("not a callable layout"), - }; - let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); + let function = env + .builder + .get_insert_block() + .unwrap() + .get_parent() + .unwrap(); + + let has_tag_id = match result_layout { + Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout), + _ => unreachable!(), + }; + call_bitcode_fn( env, &[ @@ -612,6 +658,7 @@ pub fn list_keep_oks<'a, 'ctx, 'env>( layout_width(env, before_layout), layout_width(env, result_layout), layout_width(env, after_layout), + has_tag_id.as_global_value().as_pointer_value().into(), dec_result_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_KEEP_OKS, @@ -623,20 +670,26 @@ pub fn list_keep_errs<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, roc_function_call: RocFunctionCall<'ctx>, - function_layout: &Layout<'a>, + // Layout of the `Result * err` + result_layout: &Layout<'a>, list: BasicValueEnum<'ctx>, before_layout: &Layout<'a>, after_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - // Layout of the `Result after *` - let result_layout = match function_layout { - Layout::FunctionPointer(_, ret) => ret, - Layout::Closure(_, _, ret) => ret, - _ => unreachable!("not a callable layout"), - }; - let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); + let function = env + .builder + .get_insert_block() + .unwrap() + .get_parent() + .unwrap(); + + let has_tag_id = match result_layout { + Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout), + _ => unreachable!(), + }; + call_bitcode_fn( env, &[ @@ -649,60 +702,13 @@ pub fn list_keep_errs<'a, 'ctx, 'env>( layout_width(env, before_layout), layout_width(env, result_layout), layout_width(env, after_layout), + has_tag_id.as_global_value().as_pointer_value().into(), dec_result_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_KEEP_ERRS, ) } -pub fn list_keep_result<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - transform: FunctionValue<'ctx>, - transform_layout: Layout<'a>, - closure_data: BasicValueEnum<'ctx>, - closure_data_layout: Layout<'a>, - list: BasicValueEnum<'ctx>, - before_layout: &Layout<'a>, - after_layout: &Layout<'a>, - op: &str, -) -> BasicValueEnum<'ctx> { - let builder = env.builder; - - let result_layout = match transform_layout { - Layout::FunctionPointer(_, ret) => ret, - Layout::Closure(_, _, ret) => ret, - _ => unreachable!("not a callable layout"), - }; - - let closure_data_ptr = builder.build_alloca(closure_data.get_type(), "closure_data_ptr"); - env.builder.build_store(closure_data_ptr, closure_data); - - let stepper_caller = - build_transform_caller(env, transform, closure_data_layout, &[*before_layout]) - .as_global_value() - .as_pointer_value(); - - let inc_closure = build_inc_wrapper(env, layout_ids, &transform_layout); - let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); - - call_bitcode_fn( - env, - &[ - pass_list_as_i128(env, list), - pass_as_opaque(env, closure_data_ptr), - stepper_caller.into(), - env.alignment_intvalue(&before_layout), - layout_width(env, before_layout), - layout_width(env, after_layout), - layout_width(env, result_layout), - inc_closure.as_global_value().as_pointer_value().into(), - dec_result_fn.as_global_value().as_pointer_value().into(), - ], - op, - ) -} - /// List.sortWith : List a, (a, a -> Ordering) -> List a pub fn list_sort_with<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -849,7 +855,6 @@ pub fn list_map3<'a, 'ctx, 'env>( /// List.concat : List elem, List elem -> List elem pub fn list_concat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _inplace: InPlace, _parent: FunctionValue<'ctx>, first_list: BasicValueEnum<'ctx>, second_list: BasicValueEnum<'ctx>, @@ -861,7 +866,7 @@ pub fn list_concat<'a, 'ctx, 'env>( // then simply return an empty list empty_list(env) } - Layout::Builtin(Builtin::List(_, elem_layout)) => call_bitcode_fn_returns_list( + Layout::Builtin(Builtin::List(elem_layout)) => call_bitcode_fn_returns_list( env, &[ pass_list_as_i128(env, first_list), @@ -1115,7 +1120,6 @@ pub fn load_list_ptr<'ctx>( pub fn allocate_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - inplace: InPlace, elem_layout: &Layout<'a>, number_of_elements: IntValue<'ctx>, ) -> PointerValue<'ctx> { @@ -1128,16 +1132,13 @@ pub fn allocate_list<'a, 'ctx, 'env>( let number_of_data_bytes = builder.build_int_mul(bytes_per_element, number_of_elements, "data_length"); - let rc1 = match inplace { - InPlace::InPlace => number_of_elements, - InPlace::Clone => { - // the refcount of a new list is initially 1 - // we assume that the list is indeed used (dead variables are eliminated) - crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes) - } - }; + // the refcount of a new list is initially 1 + // we assume that the list is indeed used (dead variables are eliminated) + let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes); - allocate_with_refcount_help(env, elem_layout, number_of_data_bytes, rc1) + let basic_type = basic_type_from_layout(env, elem_layout); + let alignment_bytes = elem_layout.alignment_bytes(env.ptr_bytes); + allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes, rc1) } pub fn store_list<'a, 'ctx, 'env>( diff --git a/compiler/gen/src/llvm/build_str.rs b/compiler/gen_llvm/src/llvm/build_str.rs similarity index 95% rename from compiler/gen/src/llvm/build_str.rs rename to compiler/gen_llvm/src/llvm/build_str.rs index 4692f5eaa8..ee8576ceb8 100644 --- a/compiler/gen/src/llvm/build_str.rs +++ b/compiler/gen_llvm/src/llvm/build_str.rs @@ -6,7 +6,7 @@ use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, Str use inkwell::AddressSpace; use roc_builtins::bitcode; use roc_module::symbol::Symbol; -use roc_mono::layout::{Builtin, InPlace, Layout}; +use roc_mono::layout::{Builtin, Layout}; use super::build::load_symbol; @@ -16,7 +16,6 @@ pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8); pub fn str_split<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, - inplace: InPlace, str_symbol: Symbol, delimiter_symbol: Symbol, ) -> BasicValueEnum<'ctx> { @@ -33,7 +32,7 @@ pub fn str_split<'a, 'ctx, 'env>( .into_int_value(); // a pointer to the elements - let ret_list_ptr = allocate_list(env, inplace, &Layout::Builtin(Builtin::Str), segment_count); + let ret_list_ptr = allocate_list(env, &Layout::Builtin(Builtin::Str), segment_count); // get the RocStr type defined by zig let roc_str_type = env.module.get_struct_type("str.RocStr").unwrap(); @@ -109,7 +108,6 @@ pub fn destructure<'ctx>( /// Str.concat : Str, Str -> Str pub fn str_concat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - inplace: InPlace, scope: &Scope<'a, 'ctx>, str1_symbol: Symbol, str2_symbol: Symbol, @@ -120,14 +118,7 @@ pub fn str_concat<'a, 'ctx, 'env>( call_bitcode_fn( env, - &[ - env.context - .i8_type() - .const_int(inplace as u64, false) - .into(), - str1_i128.into(), - str2_i128.into(), - ], + &[str1_i128.into(), str2_i128.into()], &bitcode::STR_CONCAT, ) } @@ -135,7 +126,6 @@ pub fn str_concat<'a, 'ctx, 'env>( /// Str.join : List Str, Str -> Str pub fn str_join_with<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - _inplace: InPlace, scope: &Scope<'a, 'ctx>, list_symbol: Symbol, str_symbol: Symbol, diff --git a/compiler/gen/src/llvm/compare.rs b/compiler/gen_llvm/src/llvm/compare.rs similarity index 91% rename from compiler/gen/src/llvm/compare.rs rename to compiler/gen_llvm/src/llvm/compare.rs index 0cf32263d0..2a9d1d3fa3 100644 --- a/compiler/gen/src/llvm/compare.rs +++ b/compiler/gen_llvm/src/llvm/compare.rs @@ -1,10 +1,13 @@ use crate::llvm::build::Env; -use crate::llvm::build::{cast_block_of_memory_to_tag, complex_bitcast, set_name, FAST_CALL_CONV}; +use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV}; use crate::llvm::build_list::{list_len, load_list_ptr}; use crate::llvm::build_str::str_equal; -use crate::llvm::convert::{basic_type_from_layout, get_ptr_type}; +use crate::llvm::convert::basic_type_from_layout; use bumpalo::collections::Vec; -use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; +use inkwell::types::BasicType; +use inkwell::values::{ + BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, +}; use inkwell::{AddressSpace, FloatPredicate, IntPredicate}; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; @@ -99,7 +102,7 @@ fn build_eq_builtin<'a, 'ctx, 'env>( Builtin::Float16 => float_cmp(FloatPredicate::OEQ, "eq_f16"), Builtin::Str => str_equal(env, lhs_val, rhs_val), - Builtin::List(_, elem) => build_list_eq( + Builtin::List(elem) => build_list_eq( env, layout_ids, &Layout::Builtin(*builtin), @@ -159,11 +162,6 @@ fn build_eq<'a, 'ctx, 'env>( rhs_val, ), - Layout::PhantomEmptyStruct => { - // always equal to itself - env.context.bool_type().const_int(1, false).into() - } - Layout::RecursivePointer => match when_recursive { WhenRecursive::Unreachable => { unreachable!("recursion pointers should never be compared directly") @@ -197,11 +195,7 @@ fn build_eq<'a, 'ctx, 'env>( } }, - Layout::Pointer(_) => { - unreachable!("unused") - } - - Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => { + Layout::Closure(_, _, _) => { unreachable!("the type system will guarantee these are never compared") } } @@ -258,7 +252,7 @@ fn build_neq_builtin<'a, 'ctx, 'env>( result.into() } - Builtin::List(_, elem) => { + Builtin::List(elem) => { let is_equal = build_list_eq( env, layout_ids, @@ -338,20 +332,11 @@ fn build_neq<'a, 'ctx, 'env>( result.into() } - Layout::PhantomEmptyStruct => { - // always equal to itself - env.context.bool_type().const_int(1, false).into() - } - Layout::RecursivePointer => { unreachable!("recursion pointers should never be compared directly") } - Layout::Pointer(_) => { - unreachable!("unused") - } - - Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => { + Layout::Closure(_, _, _) => { unreachable!("the type system will guarantee these are never compared") } } @@ -446,8 +431,8 @@ fn build_list_eq_help<'a, 'ctx, 'env>( let list1 = it.next().unwrap().into_struct_value(); let list2 = it.next().unwrap().into_struct_value(); - set_name(list1.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(list2.into(), Symbol::ARG_2.ident_string(&env.interns)); + list1.set_name(Symbol::ARG_1.ident_string(&env.interns)); + list2.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); env.builder.position_at_end(entry); @@ -475,7 +460,7 @@ fn build_list_eq_help<'a, 'ctx, 'env>( let builder = env.builder; let element_type = basic_type_from_layout(env, element_layout); - let ptr_type = get_ptr_type(&element_type, AddressSpace::Generic); + let ptr_type = element_type.ptr_type(AddressSpace::Generic); let ptr1 = load_list_ptr(env.builder, list1, ptr_type); let ptr2 = load_list_ptr(env.builder, list2, ptr_type); @@ -654,8 +639,8 @@ fn build_struct_eq_help<'a, 'ctx, 'env>( let struct1 = it.next().unwrap().into_struct_value(); let struct2 = it.next().unwrap().into_struct_value(); - set_name(struct1.into(), Symbol::ARG_1.ident_string(&env.interns)); - set_name(struct2.into(), Symbol::ARG_2.ident_string(&env.interns)); + struct1.set_name(Symbol::ARG_1.ident_string(&env.interns)); + struct2.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); let start = ctx.append_basic_block(parent, "start"); @@ -835,8 +820,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( let tag1 = it.next().unwrap(); let tag2 = it.next().unwrap(); - set_name(tag1, Symbol::ARG_1.ident_string(&env.interns)); - set_name(tag2, Symbol::ARG_2.ident_string(&env.interns)); + tag1.set_name(Symbol::ARG_1.ident_string(&env.interns)); + tag2.set_name(Symbol::ARG_2.ident_string(&env.interns)); let entry = ctx.append_basic_block(parent, "entry"); @@ -861,9 +846,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( match union_layout { NonRecursive(tags) => { - // SAFETY we know that non-recursive tags cannot be NULL - let id1 = nonrec_tag_id(env, tag1.into_struct_value()); - let id2 = nonrec_tag_id(env, tag2.into_struct_value()); + let id1 = get_tag_id(env, parent, union_layout, tag1); + let id2 = get_tag_id(env, parent, union_layout, tag2); let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); @@ -912,10 +896,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.build_return(Some(&answer)); - cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), - block, - )); + cases.push((id1.get_type().const_int(tag_id as u64, false), block)); } env.builder.position_at_end(compare_tag_fields); @@ -941,9 +922,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.position_at_end(compare_tag_ids); - // SAFETY we know that non-recursive tags cannot be NULL - let id1 = unsafe { rec_tag_id_unsafe(env, tag1.into_pointer_value()) }; - let id2 = unsafe { rec_tag_id_unsafe(env, tag2.into_pointer_value()) }; + let id1 = get_tag_id(env, parent, union_layout, tag1); + let id2 = get_tag_id(env, parent, union_layout, tag2); let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); @@ -975,10 +955,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.build_return(Some(&answer)); - cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), - block, - )); + cases.push((id1.get_type().const_int(tag_id as u64, false), block)); } env.builder.position_at_end(compare_tag_fields); @@ -988,9 +965,6 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.build_switch(id1, default, &cases); } NullableUnwrapped { other_fields, .. } => { - // drop the tag id; it is not stored - let other_fields = &other_fields[1..]; - let ptr_equal = env.builder.build_int_compare( IntPredicate::EQ, env.builder @@ -1096,9 +1070,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.position_at_end(compare_other); - // SAFETY we know at this point that tag1/tag2 are not NULL - let id1 = unsafe { rec_tag_id_unsafe(env, tag1.into_pointer_value()) }; - let id2 = unsafe { rec_tag_id_unsafe(env, tag2.into_pointer_value()) }; + let id1 = get_tag_id(env, parent, union_layout, tag1); + let id2 = get_tag_id(env, parent, union_layout, tag2); let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); @@ -1131,10 +1104,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( env.builder.build_return(Some(&answer)); - cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), - block, - )); + cases.push((id1.get_type().const_int(tag_id as u64, false), block)); } env.builder.position_at_end(compare_tag_fields); @@ -1182,8 +1152,6 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>( tag1: PointerValue<'ctx>, tag2: PointerValue<'ctx>, ) -> IntValue<'ctx> { - use inkwell::types::BasicType; - let struct_layout = Layout::Struct(field_layouts); let wrapper_type = basic_type_from_layout(env, &struct_layout); @@ -1228,32 +1196,3 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>( ) .into_int_value() } - -fn nonrec_tag_id<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - tag: StructValue<'ctx>, -) -> IntValue<'ctx> { - complex_bitcast( - env.builder, - tag.into(), - env.context.i64_type().into(), - "load_tag_id", - ) - .into_int_value() -} - -unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - tag: PointerValue<'ctx>, -) -> IntValue<'ctx> { - let ptr = env - .builder - .build_bitcast( - tag, - env.context.i64_type().ptr_type(AddressSpace::Generic), - "cast_for_tag_id", - ) - .into_pointer_value(); - - env.builder.build_load(ptr, "load_tag_id").into_int_value() -} diff --git a/compiler/gen/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs similarity index 60% rename from compiler/gen/src/llvm/convert.rs rename to compiler/gen_llvm/src/llvm/convert.rs index 29e874c992..1cb299c0ae 100644 --- a/compiler/gen/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -1,88 +1,9 @@ use bumpalo::collections::Vec; use inkwell::context::Context; -use inkwell::types::BasicTypeEnum::{self, *}; -use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType}; -use inkwell::values::BasicValueEnum; +use inkwell::types::{BasicType, BasicTypeEnum, IntType, StructType}; use inkwell::AddressSpace; use roc_mono::layout::{Builtin, Layout, UnionLayout}; -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -pub fn get_ptr_type<'ctx>( - bt_enum: &BasicTypeEnum<'ctx>, - address_space: AddressSpace, -) -> PointerType<'ctx> { - match bt_enum { - ArrayType(typ) => typ.ptr_type(address_space), - IntType(typ) => typ.ptr_type(address_space), - FloatType(typ) => typ.ptr_type(address_space), - PointerType(typ) => typ.ptr_type(address_space), - StructType(typ) => typ.ptr_type(address_space), - VectorType(typ) => typ.ptr_type(address_space), - } -} - -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -pub fn get_fn_type<'ctx>( - bt_enum: &BasicTypeEnum<'ctx>, - arg_types: &[BasicTypeEnum<'ctx>], -) -> FunctionType<'ctx> { - match bt_enum { - ArrayType(typ) => typ.fn_type(arg_types, false), - IntType(typ) => typ.fn_type(arg_types, false), - FloatType(typ) => typ.fn_type(arg_types, false), - PointerType(typ) => typ.fn_type(arg_types, false), - StructType(typ) => typ.fn_type(arg_types, false), - VectorType(typ) => typ.fn_type(arg_types, false), - } -} - -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -pub fn get_array_type<'ctx>(bt_enum: &BasicTypeEnum<'ctx>, size: u32) -> ArrayType<'ctx> { - match bt_enum { - ArrayType(typ) => typ.array_type(size), - IntType(typ) => typ.array_type(size), - FloatType(typ) => typ.array_type(size), - PointerType(typ) => typ.array_type(size), - StructType(typ) => typ.array_type(size), - VectorType(typ) => typ.array_type(size), - } -} - -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -pub fn as_const_zero<'ctx>(bt_enum: &BasicTypeEnum<'ctx>) -> BasicValueEnum<'ctx> { - match bt_enum { - ArrayType(typ) => typ.const_zero().into(), - IntType(typ) => typ.const_zero().into(), - FloatType(typ) => typ.const_zero().into(), - PointerType(typ) => typ.const_zero().into(), - StructType(typ) => typ.const_zero().into(), - VectorType(typ) => typ.const_zero().into(), - } -} - -fn basic_type_from_function_layout<'a, 'ctx, 'env>( - env: &crate::llvm::build::Env<'a, 'ctx, 'env>, - args: &[Layout<'_>], - closure_type: Option>, - ret_layout: &Layout<'_>, -) -> BasicTypeEnum<'ctx> { - let ret_type = basic_type_from_layout(env, &ret_layout); - let mut arg_basic_types = Vec::with_capacity_in(args.len(), env.arena); - - for arg_layout in args.iter() { - arg_basic_types.push(basic_type_from_layout(env, arg_layout)); - } - - if let Some(closure) = closure_type { - arg_basic_types.push(closure); - } - - let fn_type = get_fn_type(&ret_type, arg_basic_types.into_bump_slice()); - let ptr_type = fn_type.ptr_type(AddressSpace::Generic); - - ptr_type.as_basic_type_enum() -} - fn basic_type_from_record<'a, 'ctx, 'env>( env: &crate::llvm::build::Env<'a, 'ctx, 'env>, fields: &[Layout<'_>], @@ -105,38 +26,49 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( use Layout::*; match layout { - FunctionPointer(args, ret_layout) => { - basic_type_from_function_layout(env, args, None, ret_layout) - } Closure(_args, closure_layout, _ret_layout) => { let closure_data_layout = closure_layout.runtime_representation(); basic_type_from_layout(env, &closure_data_layout) } - Pointer(layout) => basic_type_from_layout(env, &layout) - .ptr_type(AddressSpace::Generic) - .into(), - PhantomEmptyStruct => env.context.struct_type(&[], false).into(), Struct(sorted_fields) => basic_type_from_record(env, sorted_fields), Union(variant) => { use UnionLayout::*; + + let tag_id_type = basic_type_from_layout(env, &variant.tag_id_layout()); + match variant { - Recursive(tags) - | NullableWrapped { + NullableWrapped { other_tags: tags, .. } => { - let block = block_of_memory_slices(env.context, tags, env.ptr_bytes); - block.ptr_type(AddressSpace::Generic).into() + let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + + env.context + .struct_type(&[data, tag_id_type], false) + .ptr_type(AddressSpace::Generic) + .into() } NullableUnwrapped { other_fields, .. } => { let block = - block_of_memory_slices(env.context, &[&other_fields[1..]], env.ptr_bytes); + block_of_memory_slices(env.context, &[&other_fields], env.ptr_bytes); block.ptr_type(AddressSpace::Generic).into() } NonNullableUnwrapped(fields) => { let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes); block.ptr_type(AddressSpace::Generic).into() } - NonRecursive(_) => block_of_memory(env.context, layout, env.ptr_bytes), + Recursive(tags) => { + let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + + env.context + .struct_type(&[data, tag_id_type], false) + .ptr_type(AddressSpace::Generic) + .into() + } + NonRecursive(tags) => { + let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + + env.context.struct_type(&[data, tag_id_type], false).into() + } } } RecursivePointer => { @@ -174,7 +106,7 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>( Float16 => context.f16_type().as_basic_type_enum(), Dict(_, _) | EmptyDict => zig_dict_type(env).into(), Set(_) | EmptySet => zig_dict_type(env).into(), - List(_, _) | EmptyList => zig_list_type(env).into(), + List(_) | EmptyList => zig_list_type(env).into(), Str | EmptyStr => zig_str_type(env).into(), } } @@ -197,13 +129,43 @@ pub fn block_of_memory_slices<'ctx>( block_of_memory_help(context, union_size) } +pub fn union_data_is_struct<'a, 'ctx, 'env>( + env: &crate::llvm::build::Env<'a, 'ctx, 'env>, + layouts: &[Layout<'_>], +) -> StructType<'ctx> { + let data_type = basic_type_from_record(env, layouts); + union_data_is_struct_type(env.context, data_type.into_struct_type()) +} + +pub fn union_data_is_struct_type<'ctx>( + context: &'ctx Context, + struct_type: StructType<'ctx>, +) -> StructType<'ctx> { + let tag_id_type = context.i64_type(); + context.struct_type(&[struct_type.into(), tag_id_type.into()], false) +} + +pub fn union_data_block_of_memory<'ctx>( + context: &'ctx Context, + tag_id_int_type: IntType<'ctx>, + layouts: &[&[Layout<'_>]], + ptr_bytes: u32, +) -> StructType<'ctx> { + let data_type = block_of_memory_slices(context, layouts, ptr_bytes); + context.struct_type(&[data_type, tag_id_int_type.into()], false) +} + pub fn block_of_memory<'ctx>( context: &'ctx Context, layout: &Layout<'_>, ptr_bytes: u32, ) -> BasicTypeEnum<'ctx> { // TODO make this dynamic - let union_size = layout.stack_size(ptr_bytes as u32); + let mut union_size = layout.stack_size(ptr_bytes as u32); + + if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout { + union_size -= ptr_bytes; + } block_of_memory_help(context, union_size) } @@ -266,3 +228,9 @@ pub fn zig_str_type<'a, 'ctx, 'env>( ) -> StructType<'ctx> { env.module.get_struct_type("str.RocStr").unwrap() } + +pub fn zig_has_tag_id_type<'a, 'ctx, 'env>( + env: &crate::llvm::build::Env<'a, 'ctx, 'env>, +) -> StructType<'ctx> { + env.module.get_struct_type("list.HasTagId").unwrap() +} diff --git a/compiler/gen/src/llvm/externs.rs b/compiler/gen_llvm/src/llvm/externs.rs similarity index 96% rename from compiler/gen/src/llvm/externs.rs rename to compiler/gen_llvm/src/llvm/externs.rs index 2bb2a01c6e..48bea9283b 100644 --- a/compiler/gen/src/llvm/externs.rs +++ b/compiler/gen_llvm/src/llvm/externs.rs @@ -1,8 +1,9 @@ -use crate::llvm::build::{add_func, set_name, C_CALL_CONV}; +use crate::llvm::build::{add_func, C_CALL_CONV}; use crate::llvm::convert::ptr_int; use inkwell::builder::Builder; use inkwell::context::Context; use inkwell::module::{Linkage, Module}; +use inkwell::values::BasicValue; use inkwell::AddressSpace; /// Define functions for roc_alloc, roc_realloc, and roc_dealloc @@ -69,8 +70,8 @@ pub fn add_default_roc_externs<'ctx>( debug_assert!(params.next().is_none()); - set_name(ptr_arg, "ptr"); - set_name(size_arg, "size"); + ptr_arg.set_name("ptr"); + size_arg.set_name("size"); if cfg!(debug_assertions) { crate::llvm::build::verify_fn(fn_val); diff --git a/compiler/gen/src/llvm/mod.rs b/compiler/gen_llvm/src/llvm/mod.rs similarity index 100% rename from compiler/gen/src/llvm/mod.rs rename to compiler/gen_llvm/src/llvm/mod.rs diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs similarity index 88% rename from compiler/gen/src/llvm/refcounting.rs rename to compiler/gen_llvm/src/llvm/refcounting.rs index cf0a254163..4dd072f622 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1,21 +1,24 @@ use crate::debug_info_init; use crate::llvm::build::{ - add_func, cast_basic_basic, cast_block_of_memory_to_tag, set_name, Env, FAST_CALL_CONV, - LLVM_SADD_WITH_OVERFLOW_I64, + add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive, + Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX, }; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::convert::{ - basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int, + basic_type_from_layout, block_of_memory_slices, ptr_int, union_data_block_of_memory, }; use bumpalo::collections::Vec; +use inkwell::basic_block::BasicBlock; use inkwell::context::Context; use inkwell::module::Linkage; use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum}; -use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; +use inkwell::values::{ + BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, +}; use inkwell::{AddressSpace, IntPredicate}; use roc_module::symbol::Interns; use roc_module::symbol::Symbol; -use roc_mono::layout::{Builtin, Layout, LayoutIds, MemoryMode, UnionLayout}; +use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; pub const REFCOUNT_MAX: usize = 0_usize; @@ -92,6 +95,14 @@ impl<'ctx> PointerToRefcount<'ctx> { Self::from_ptr_to_data(env, data_ptr) } + pub fn is_1<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> { + let current = self.get_refcount(env); + let one = refcount_1(env.context, env.ptr_bytes); + + env.builder + .build_int_compare(IntPredicate::EQ, current, one, "is_one") + } + pub fn get_refcount<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> { env.builder .build_load(self.value, "get_refcount") @@ -390,7 +401,7 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>( let arg_symbol = Symbol::ARG_1; let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; @@ -469,21 +480,17 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( use Builtin::*; match builtin { - List(memory_mode, element_layout) => { - if let MemoryMode::Refcounted = memory_mode { - let function = modify_refcount_list( - env, - layout_ids, - mode, - when_recursive, - layout, - element_layout, - ); + List(element_layout) => { + let function = modify_refcount_list( + env, + layout_ids, + mode, + when_recursive, + layout, + element_layout, + ); - Some(function) - } else { - None - } + Some(function) } Set(element_layout) => { let key_layout = &Layout::Struct(&[]); @@ -646,6 +653,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( layout_ids, mode, &WhenRecursive::Loop(*variant), + *variant, tags, true, ); @@ -654,14 +662,13 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( } NullableUnwrapped { other_fields, .. } => { - let other_fields = &other_fields[1..]; - let function = build_rec_union( env, layout_ids, mode, &WhenRecursive::Loop(*variant), - &*env.arena.alloc([other_fields]), + *variant, + env.arena.alloc([*other_fields]), true, ); @@ -674,6 +681,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( layout_ids, mode, &WhenRecursive::Loop(*variant), + *variant, &*env.arena.alloc([*fields]), true, ); @@ -686,6 +694,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( layout_ids, mode, &WhenRecursive::Loop(*variant), + *variant, tags, false, ); @@ -724,11 +733,9 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( Some(function) } - PhantomEmptyStruct => None, - Layout::RecursivePointer => match when_recursive { WhenRecursive::Unreachable => { - unreachable!("recursion pointers should never be hashed directly") + unreachable!("recursion pointers cannot be in/decremented directly") } WhenRecursive::Loop(union_layout) => { let layout = Layout::Union(*union_layout); @@ -745,8 +752,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( Some(function) } }, - - FunctionPointer(_, _) | Pointer(_) => None, } } @@ -827,7 +832,7 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>( let arg_symbol = Symbol::ARG_1; let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; let original_wrapper = arg_val.into_struct_value(); @@ -946,7 +951,7 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>( let arg_symbol = Symbol::ARG_1; let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; @@ -1065,7 +1070,7 @@ fn modify_refcount_dict_help<'a, 'ctx, 'env>( let arg_symbol = Symbol::ARG_1; let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; @@ -1179,7 +1184,7 @@ pub enum Mode { } impl Mode { - fn to_call_mode<'ctx>(&self, function: FunctionValue<'ctx>) -> CallMode<'ctx> { + fn to_call_mode(self, function: FunctionValue<'_>) -> CallMode<'_> { match self { Mode::Inc => { let amount = function.get_nth_param(1).unwrap().into_int_value(); @@ -1202,10 +1207,11 @@ fn build_rec_union<'a, 'ctx, 'env>( layout_ids: &mut LayoutIds<'a>, mode: Mode, when_recursive: &WhenRecursive<'a>, - fields: &'a [&'a [Layout<'a>]], + union_layout: UnionLayout<'a>, + tags: &'a [&'a [Layout<'a>]], is_nullable: bool, ) -> FunctionValue<'ctx> { - let layout = Layout::Union(UnionLayout::Recursive(fields)); + let layout = Layout::Union(UnionLayout::Recursive(tags)); let (_, fn_name) = function_name_from_mode( layout_ids, @@ -1222,9 +1228,7 @@ fn build_rec_union<'a, 'ctx, 'env>( let block = env.builder.get_insert_block().expect("to be in a function"); let di_location = env.builder.get_current_debug_location().unwrap(); - let basic_type = block_of_memory_slices(env.context, fields, env.ptr_bytes) - .ptr_type(AddressSpace::Generic) - .into(); + let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout)); let function_value = build_header(env, basic_type, mode, &fn_name); build_rec_union_help( @@ -1232,7 +1236,8 @@ fn build_rec_union<'a, 'ctx, 'env>( layout_ids, mode, when_recursive, - fields, + union_layout, + tags, function_value, is_nullable, ); @@ -1248,12 +1253,14 @@ fn build_rec_union<'a, 'ctx, 'env>( function } +#[allow(clippy::too_many_arguments)] fn build_rec_union_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, when_recursive: &WhenRecursive<'a>, - tags: &[&[Layout<'a>]], + union_layout: UnionLayout<'a>, + tags: &'a [&'a [roc_mono::layout::Layout<'a>]], fn_val: FunctionValue<'ctx>, is_nullable: bool, ) { @@ -1262,8 +1269,6 @@ fn build_rec_union_help<'a, 'ctx, 'env>( let context = &env.context; let builder = env.builder; - let pick = |a, b| if let Mode::Inc = mode { a } else { b }; - // Add a basic block for the entry point let entry = context.append_basic_block(fn_val, "entry"); @@ -1276,15 +1281,101 @@ fn build_rec_union_help<'a, 'ctx, 'env>( let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; - let layout = Layout::Union(UnionLayout::Recursive(tags)); - debug_assert!(arg_val.is_pointer_value()); let value_ptr = arg_val.into_pointer_value(); + // to increment/decrement the cons-cell itself + let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr); + let call_mode = mode_to_call_mode(fn_val, mode); + + let should_recurse_block = env.context.append_basic_block(parent, "should_recurse"); + + let ctx = env.context; + if is_nullable { + let is_null = env.builder.build_is_null(value_ptr, "is_null"); + + let then_block = ctx.append_basic_block(parent, "then"); + + env.builder + .build_conditional_branch(is_null, then_block, should_recurse_block); + + { + env.builder.position_at_end(then_block); + env.builder.build_return(None); + } + } else { + env.builder.build_unconditional_branch(should_recurse_block); + } + + env.builder.position_at_end(should_recurse_block); + + let layout = Layout::Union(union_layout); + + match mode { + Mode::Inc => { + // inc is cheap; we never recurse + refcount_ptr.modify(call_mode, &layout, env); + env.builder.build_return(None); + } + + Mode::Dec => { + let do_recurse_block = env.context.append_basic_block(parent, "do_recurse"); + let no_recurse_block = env.context.append_basic_block(parent, "no_recurse"); + + builder.build_conditional_branch( + refcount_ptr.is_1(env), + do_recurse_block, + no_recurse_block, + ); + + { + env.builder.position_at_end(no_recurse_block); + + refcount_ptr.modify(call_mode, &layout, env); + env.builder.build_return(None); + } + + { + env.builder.position_at_end(do_recurse_block); + + build_rec_union_recursive_decrement( + env, + layout_ids, + when_recursive, + parent, + fn_val, + union_layout, + tags, + value_ptr, + refcount_ptr, + do_recurse_block, + ) + } + } + } +} + +#[allow(clippy::too_many_arguments)] +fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + when_recursive: &WhenRecursive<'a>, + parent: FunctionValue<'ctx>, + decrement_fn: FunctionValue<'ctx>, + union_layout: UnionLayout<'a>, + tags: &[&[Layout<'a>]], + value_ptr: PointerValue<'ctx>, + refcount_ptr: PointerToRefcount<'ctx>, + match_block: BasicBlock<'ctx>, +) { + let mode = Mode::Dec; + let call_mode = mode_to_call_mode(decrement_fn, mode); + let builder = env.builder; + // branches that are not/don't contain anything refcounted // if there is only one branch, we don't need to switch let switch_needed: bool = (|| { @@ -1300,31 +1391,12 @@ fn build_rec_union_help<'a, 'ctx, 'env>( false })(); - // to increment/decrement the cons-cell itself - let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr); - let call_mode = mode_to_call_mode(fn_val, mode); - - let ctx = env.context; - let cont_block = ctx.append_basic_block(parent, "cont"); - if is_nullable { - let is_null = env.builder.build_is_null(value_ptr, "is_null"); - - let then_block = ctx.append_basic_block(parent, "then"); - - env.builder - .build_conditional_branch(is_null, then_block, cont_block); - - { - env.builder.position_at_end(then_block); - env.builder.build_return(None); - } - } else { - env.builder.build_unconditional_branch(cont_block); - } - // next, make a jump table for all possible values of the tag_id let mut cases = Vec::with_capacity_in(tags.len(), env.arena); + let tag_id_int_type = + basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type(); + for (tag_id, field_layouts) in tags.iter().enumerate() { // if none of the fields are or contain anything refcounted, just move on if !field_layouts @@ -1334,9 +1406,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>( continue; } - let block = env - .context - .append_basic_block(parent, pick("tag_id_increment", "tag_id_decrement")); + let block = env.context.append_basic_block(parent, "tag_id_decrement"); env.builder.position_at_end(block); @@ -1372,7 +1442,23 @@ fn build_rec_union_help<'a, 'ctx, 'env>( debug_assert!(ptr_as_i64_ptr.is_pointer_value()); // therefore we must cast it to our desired type - let union_type = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let union_type = match union_layout { + UnionLayout::NonRecursive(_) => unreachable!(), + UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => { + union_data_block_of_memory( + env.context, + tag_id_int_type, + tags, + env.ptr_bytes, + ) + .into() + } + UnionLayout::NonNullableUnwrapped { .. } + | UnionLayout::NullableUnwrapped { .. } => { + block_of_memory_slices(env.context, tags, env.ptr_bytes) + } + }; + let recursive_field_ptr = cast_basic_basic( env.builder, ptr_as_i64_ptr, @@ -1386,10 +1472,9 @@ fn build_rec_union_help<'a, 'ctx, 'env>( .build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer") .unwrap(); - let field = env.builder.build_load( - elem_pointer, - pick("increment_struct_field", "decrement_struct_field"), - ); + let field = env + .builder + .build_load(elem_pointer, "decrement_struct_field"); deferred_nonrec.push((field, field_layout)); } @@ -1401,14 +1486,14 @@ fn build_rec_union_help<'a, 'ctx, 'env>( // lists. To achieve it, we must first load all fields that we want to inc/dec (done above) // and store them on the stack, then modify (and potentially free) the current cell, then // actually inc/dec the fields. - refcount_ptr.modify(call_mode, &layout, env); + refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); for (field, field_layout) in deferred_nonrec { modify_refcount_layout_help( env, parent, layout_ids, - mode.to_call_mode(fn_val), + mode.to_call_mode(decrement_fn), when_recursive, field, field_layout, @@ -1417,22 +1502,19 @@ fn build_rec_union_help<'a, 'ctx, 'env>( for ptr in deferred_rec { // recursively decrement the field - let call = call_help(env, fn_val, mode.to_call_mode(fn_val), ptr); + let call = call_help(env, decrement_fn, mode.to_call_mode(decrement_fn), ptr); call.set_tail_call(true); } // this function returns void builder.build_return(None); - cases.push(( - env.context.i64_type().const_int(tag_id as u64, false), - block, - )); + cases.push((tag_id_int_type.const_int(tag_id as u64, false), block)); } - cases.reverse(); + env.builder.position_at_end(match_block); - env.builder.position_at_end(cont_block); + cases.reverse(); if cases.len() == 1 && !switch_needed { // there is only one tag in total; we don't need a switch @@ -1443,11 +1525,9 @@ fn build_rec_union_help<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(only_branch); } else { // read the tag_id - let current_tag_id = rec_union_read_tag(env, value_ptr); + let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into()); - let merge_block = env - .context - .append_basic_block(parent, pick("increment_merge", "decrement_merge")); + let merge_block = env.context.append_basic_block(parent, "decrement_merge"); // switch on it env.builder @@ -1456,30 +1536,13 @@ fn build_rec_union_help<'a, 'ctx, 'env>( env.builder.position_at_end(merge_block); // increment/decrement the cons-cell itself - refcount_ptr.modify(call_mode, &layout, env); + refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); // this function returns void builder.build_return(None); } } -fn rec_union_read_tag<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - value_ptr: PointerValue<'ctx>, -) -> IntValue<'ctx> { - // Assumption: the tag is the first thing stored - // so cast the pointer to the data to a `i64*` - let tag_ptr_type = env.context.i64_type().ptr_type(AddressSpace::Generic); - let tag_ptr = env - .builder - .build_bitcast(value_ptr, tag_ptr_type, "cast_tag_ptr") - .into_pointer_value(); - - env.builder - .build_load(tag_ptr, "load_tag_id") - .into_int_value() -} - fn function_name_from_mode<'a>( layout_ids: &mut LayoutIds<'a>, interns: &Interns, @@ -1524,7 +1587,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>( let function = match env.module.get_function(fn_name.as_str()) { Some(function_value) => function_value, None => { - let basic_type = block_of_memory(env.context, &layout, env.ptr_bytes); + let basic_type = basic_type_from_layout(env, &layout); let function_value = build_header(env, basic_type, mode, &fn_name); modify_refcount_union_help( @@ -1571,7 +1634,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( let arg_symbol = Symbol::ARG_1; let arg_val = fn_val.get_param_iter().next().unwrap(); - set_name(arg_val, arg_symbol.ident_string(&env.interns)); + arg_val.set_name(arg_symbol.ident_string(&env.interns)); let parent = fn_val; @@ -1580,19 +1643,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( let wrapper_struct = arg_val.into_struct_value(); // read the tag_id - let tag_id = { - // the first element of the wrapping struct is an array of i64 - let first_array = env - .builder - .build_extract_value(wrapper_struct, 0, "read_tag_id") - .unwrap() - .into_array_value(); - - env.builder - .build_extract_value(first_array, 0, "read_tag_id_2") - .unwrap() - .into_int_value() - }; + let tag_id = get_tag_id_non_recursive(env, wrapper_struct); let tag_id_u8 = env .builder @@ -1620,7 +1671,12 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts)); debug_assert!(wrapper_type.is_struct_type()); - let wrapper_struct = cast_block_of_memory_to_tag(env.builder, wrapper_struct, wrapper_type); + let data_bytes = env + .builder + .build_extract_value(wrapper_struct, TAG_DATA_INDEX, "read_tag_id") + .unwrap() + .into_struct_value(); + let wrapper_struct = cast_block_of_memory_to_tag(env.builder, data_bytes, wrapper_type); for (i, field_layout) in field_layouts.iter().enumerate() { if let Layout::RecursivePointer = field_layout { @@ -1689,7 +1745,7 @@ pub fn refcount_offset<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layou let value_bytes = layout.stack_size(env.ptr_bytes) as u64; match layout { - Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64, + Layout::Builtin(Builtin::List(_)) => env.ptr_bytes as u64, Layout::Builtin(Builtin::Str) => env.ptr_bytes as u64, Layout::RecursivePointer | Layout::Union(_) => env.ptr_bytes as u64, _ => (env.ptr_bytes as u64).max(value_bytes), diff --git a/compiler/gen/src/run_roc.rs b/compiler/gen_llvm/src/run_roc.rs similarity index 95% rename from compiler/gen/src/run_roc.rs rename to compiler/gen_llvm/src/run_roc.rs index 1e1c28b9b2..884ef41fcc 100644 --- a/compiler/gen/src/run_roc.rs +++ b/compiler/gen_llvm/src/run_roc.rs @@ -38,7 +38,7 @@ macro_rules! run_jit_function { ($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{ use inkwell::context::Context; - use roc_gen::run_roc::RocCallResult; + use roc_gen_llvm::run_roc::RocCallResult; use std::mem::MaybeUninit; unsafe { @@ -77,7 +77,7 @@ macro_rules! run_jit_function_dynamic_type { ($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{ use inkwell::context::Context; - use roc_gen::run_roc::RocCallResult; + use roc_gen_llvm::run_roc::RocCallResult; unsafe { let main: libloading::Symbol = $lib @@ -86,7 +86,7 @@ macro_rules! run_jit_function_dynamic_type { .ok_or(format!("Unable to JIT compile `{}`", $main_fn_name)) .expect("errored"); - let size = roc_gen::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + $bytes; + let size = roc_gen_llvm::run_roc::ROC_CALL_RESULT_DISCRIMINANT_SIZE + $bytes; let layout = std::alloc::Layout::array::(size).unwrap(); let result = std::alloc::alloc(layout); main(result); diff --git a/compiler/load/Cargo.toml b/compiler/load/Cargo.toml index 01d0a82f5b..6ed6e3cc63 100644 --- a/compiler/load/Cargo.toml +++ b/compiler/load/Cargo.toml @@ -19,6 +19,7 @@ roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_reporting = { path = "../reporting" } +morphic_lib = { path = "../../vendor/morphic_lib" } ven_pretty = { path = "../../vendor/pretty" } bumpalo = { version = "3.6.1", features = ["collections"] } inlinable_string = "0.1" diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index c971188257..1fa5db439d 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -1,10 +1,12 @@ -use crate::docs::DocEntry::DetatchedDoc; -use crate::docs::TypeAnnotation::{Apply, BoundVariable, Record, TagUnion}; +use crate::docs::DocEntry::DetachedDoc; +use crate::docs::TypeAnnotation::{ + Apply, BoundVariable, Function, NoTypeAnn, ObscuredRecord, ObscuredTagUnion, Record, TagUnion, +}; +use crate::file::LoadedModule; use inlinable_string::InlinableString; use roc_can::scope::Scope; -use roc_collections::all::MutMap; use roc_module::ident::ModuleName; -use roc_module::symbol::{IdentIds, Interns, ModuleId}; +use roc_module::symbol::IdentIds; use roc_parse::ast; use roc_parse::ast::CommentOrNewline; use roc_parse::ast::{AssignedField, Def}; @@ -17,7 +19,7 @@ pub struct Documentation { pub name: String, pub version: String, pub docs: String, - pub modules: Vec<(MutMap, Interns)>, + pub modules: Vec, } #[derive(Debug)] @@ -30,14 +32,14 @@ pub struct ModuleDocumentation { #[derive(Debug, Clone)] pub enum DocEntry { DocDef(DocDef), - DetatchedDoc(String), + DetachedDoc(String), } #[derive(Debug, Clone)] pub struct DocDef { pub name: String, pub type_vars: Vec, - pub type_annotation: Option, + pub type_annotation: TypeAnnotation, pub docs: Option, } @@ -45,8 +47,14 @@ pub struct DocDef { pub enum TypeAnnotation { TagUnion { tags: Vec, - extension: Option>, + extension: Box, }, + Function { + args: Vec, + output: Box, + }, + ObscuredTagUnion, + ObscuredRecord, BoundVariable(String), Apply { name: String, @@ -54,7 +62,10 @@ pub enum TypeAnnotation { }, Record { fields: Vec, + extension: Box, }, + Wildcard, + NoTypeAnn, } #[derive(Debug, Clone)] pub enum RecordField { @@ -97,10 +108,10 @@ pub fn generate_module_docs<'a>( } } -fn detatched_docs_from_comments_and_new_lines<'a>( +fn detached_docs_from_comments_and_new_lines<'a>( comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>], ) -> Vec { - let mut detatched_docs: Vec = Vec::new(); + let mut detached_docs: Vec = Vec::new(); let mut docs = String::new(); @@ -112,13 +123,16 @@ fn detatched_docs_from_comments_and_new_lines<'a>( } CommentOrNewline::LineComment(_) | CommentOrNewline::Newline => { - detatched_docs.push(docs.clone()); + if !docs.is_empty() { + detached_docs.push(docs.clone()); + } + docs = String::new(); } } } - detatched_docs + detached_docs } fn generate_entry_doc<'a>( @@ -136,8 +150,8 @@ fn generate_entry_doc<'a>( Def::SpaceBefore(sub_def, comments_or_new_lines) => { // Comments before a definition are attached to the current defition - for detatched_doc in detatched_docs_from_comments_and_new_lines(comments_or_new_lines) { - acc.push(DetatchedDoc(detatched_doc)); + for detached_doc in detached_docs_from_comments_and_new_lines(comments_or_new_lines) { + acc.push(DetachedDoc(detached_doc)); } generate_entry_doc(ident_ids, acc, Some(comments_or_new_lines), sub_def) @@ -161,7 +175,7 @@ fn generate_entry_doc<'a>( { let doc_def = DocDef { name: identifier.to_string(), - type_annotation: None, + type_annotation: NoTypeAnn, type_vars: Vec::new(), docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; @@ -172,7 +186,11 @@ fn generate_entry_doc<'a>( _ => (acc, None), }, - Def::AnnotatedBody { ann_pattern, .. } => match ann_pattern.value { + Def::AnnotatedBody { + ann_pattern, + ann_type, + .. + } => match ann_pattern.value { Pattern::Identifier(identifier) => { // Check if the definition is exposed if ident_ids @@ -181,7 +199,7 @@ fn generate_entry_doc<'a>( { let doc_def = DocDef { name: identifier.to_string(), - type_annotation: None, + type_annotation: type_to_docs(false, ann_type.value), type_vars: Vec::new(), docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; @@ -204,7 +222,7 @@ fn generate_entry_doc<'a>( let doc_def = DocDef { name: name.value.to_string(), - type_annotation: type_to_docs(ann.value), + type_annotation: type_to_docs(false, ann.value), type_vars, docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; @@ -221,7 +239,7 @@ fn generate_entry_doc<'a>( } } -fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option { +fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation { match type_annotation { ast::TypeAnnotation::TagUnion { tags, @@ -233,7 +251,7 @@ fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option let mut any_tags_are_private = false; for tag in tags { - match tag_to_doc(tag.value) { + match tag_to_doc(in_func_type_ann, tag.value) { None => { any_tags_are_private = true; break; @@ -245,20 +263,24 @@ fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option } if any_tags_are_private { - None + if in_func_type_ann { + ObscuredTagUnion + } else { + NoTypeAnn + } } else { let extension = match ext { - None => None, - Some(ext_type_ann) => type_to_docs(ext_type_ann.value).map(Box::new), + None => NoTypeAnn, + Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value), }; - Some(TagUnion { + TagUnion { tags: tags_to_render, - extension, - }) + extension: Box::new(extension), + } } } - ast::TypeAnnotation::BoundVariable(var_name) => Some(BoundVariable(var_name.to_string())), + ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()), ast::TypeAnnotation::Apply(module_name, type_name, type_ann_parts) => { let mut name = String::new(); @@ -272,16 +294,14 @@ fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option let mut parts: Vec = Vec::new(); for type_ann_part in type_ann_parts { - if let Some(part) = type_to_docs(type_ann_part.value) { - parts.push(part); - } + parts.push(type_to_docs(in_func_type_ann, type_ann_part.value)); } - Some(Apply { name, parts }) + Apply { name, parts } } ast::TypeAnnotation::Record { fields, - ext: _, + ext, final_comments: _, } => { let mut doc_fields = Vec::new(); @@ -289,7 +309,7 @@ fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option let mut any_fields_include_private_tags = false; for field in fields { - match record_field_to_doc(field.value) { + match record_field_to_doc(in_func_type_ann, field.value) { None => { any_fields_include_private_tags = true; break; @@ -300,37 +320,61 @@ fn type_to_docs(type_annotation: ast::TypeAnnotation) -> Option } } if any_fields_include_private_tags { - None + if in_func_type_ann { + ObscuredRecord + } else { + NoTypeAnn + } } else { - Some(Record { fields: doc_fields }) + let extension = match ext { + None => NoTypeAnn, + Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value), + }; + + Record { + fields: doc_fields, + extension: Box::new(extension), + } } } - ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => type_to_docs(sub_type_ann), - ast::TypeAnnotation::SpaceAfter(&sub_type_ann, _) => type_to_docs(sub_type_ann), - _ => { - // TODO "Implement type to docs") - - None + ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => { + type_to_docs(in_func_type_ann, sub_type_ann) } + ast::TypeAnnotation::SpaceAfter(&sub_type_ann, _) => { + type_to_docs(in_func_type_ann, sub_type_ann) + } + ast::TypeAnnotation::Function(ast_arg_anns, output_ann) => { + let mut doc_arg_anns = Vec::new(); + + for ast_arg_ann in ast_arg_anns { + doc_arg_anns.push(type_to_docs(true, ast_arg_ann.value)); + } + + Function { + args: doc_arg_anns, + output: Box::new(type_to_docs(true, output_ann.value)), + } + } + ast::TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, + _ => NoTypeAnn, } } -fn record_field_to_doc(field: ast::AssignedField<'_, ast::TypeAnnotation>) -> Option { +fn record_field_to_doc( + in_func_ann: bool, + field: ast::AssignedField<'_, ast::TypeAnnotation>, +) -> Option { match field { - AssignedField::RequiredValue(name, _, type_ann) => { - type_to_docs(type_ann.value).map(|type_ann_docs| RecordField::RecordField { - name: name.value.to_string(), - type_annotation: type_ann_docs, - }) - } - AssignedField::SpaceBefore(&sub_field, _) => record_field_to_doc(sub_field), - AssignedField::SpaceAfter(&sub_field, _) => record_field_to_doc(sub_field), - AssignedField::OptionalValue(name, _, type_ann) => { - type_to_docs(type_ann.value).map(|type_ann_docs| RecordField::OptionalField { - name: name.value.to_string(), - type_annotation: type_ann_docs, - }) - } + AssignedField::RequiredValue(name, _, type_ann) => Some(RecordField::RecordField { + name: name.value.to_string(), + type_annotation: type_to_docs(in_func_ann, type_ann.value), + }), + AssignedField::SpaceBefore(&sub_field, _) => record_field_to_doc(in_func_ann, sub_field), + AssignedField::SpaceAfter(&sub_field, _) => record_field_to_doc(in_func_ann, sub_field), + AssignedField::OptionalValue(name, _, type_ann) => Some(RecordField::OptionalField { + name: name.value.to_string(), + type_annotation: type_to_docs(in_func_ann, type_ann.value), + }), AssignedField::LabelOnly(label) => Some(RecordField::LabelOnly { name: label.value.to_string(), }), @@ -340,7 +384,7 @@ fn record_field_to_doc(field: ast::AssignedField<'_, ast::TypeAnnotation>) -> Op // The Option here represents if it is private. Private tags // evaluate to `None`. -fn tag_to_doc(tag: ast::Tag) -> Option { +fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option { match tag { ast::Tag::Global { name, args } => Some(Tag { name: name.value.to_string(), @@ -348,17 +392,15 @@ fn tag_to_doc(tag: ast::Tag) -> Option { let mut type_vars = Vec::new(); for arg in args { - if let Some(type_var) = type_to_docs(arg.value) { - type_vars.push(type_var); - } + type_vars.push(type_to_docs(in_func_ann, arg.value)); } type_vars }, }), ast::Tag::Private { .. } => None, - ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(sub_tag), - ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(sub_tag), + ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), + ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), ast::Tag::Malformed(_) => None, } } diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 0f48ffc4be..24d07b1a47 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -19,8 +19,8 @@ use roc_module::symbol::{ Symbol, }; use roc_mono::ir::{ - CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, - TopLevelFunctionLayout, + CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, + ProcLayout, Procs, }; use roc_mono::layout::{Layout, LayoutCache, LayoutProblem}; use roc_parse::ast::{self, StrLiteral, TypeAnnotation}; @@ -621,6 +621,8 @@ pub struct LoadedModule { pub type_problems: MutMap>, pub declarations_by_id: MutMap>, pub exposed_to_host: MutMap, + pub exposed_aliases: MutMap, + pub exposed_values: Vec, pub header_sources: MutMap)>, pub sources: MutMap)>, pub timings: MutMap, @@ -660,6 +662,8 @@ enum HeaderFor<'a> { config_shorthand: &'a str, /// the type scheme of the main function (required by the platform) platform_main_type: TypedIdent<'a>, + /// provided symbol to host (commonly `mainForHost`) + main_for_host: Symbol, }, Interface, } @@ -705,7 +709,8 @@ pub struct MonomorphizedModule<'a> { pub can_problems: MutMap>, pub type_problems: MutMap>, pub mono_problems: MutMap>, - pub procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), Proc<'a>>, + pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, + pub entry_point: EntryPoint<'a>, pub exposed_to_host: MutMap, pub header_sources: MutMap)>, pub sources: MutMap)>, @@ -760,6 +765,8 @@ enum Msg<'a> { FinishedAllTypeChecking { solved_subs: Solved, exposed_vars_by_symbol: MutMap, + exposed_aliases_by_symbol: MutMap, + exposed_values: Vec, documentation: MutMap, }, FoundSpecializations { @@ -776,7 +783,7 @@ enum Msg<'a> { ident_ids: IdentIds, layout_cache: LayoutCache<'a>, external_specializations_requested: BumpMap>, - procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), Proc<'a>>, + procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, problems: Vec, module_timing: ModuleTiming, subs: Subs, @@ -804,10 +811,16 @@ enum PlatformPath<'a> { RootIsPkgConfig, } +#[derive(Debug)] +struct PlatformData { + module_id: ModuleId, + provides: Symbol, +} + #[derive(Debug)] struct State<'a> { pub root_id: ModuleId, - pub platform_id: Option, + pub platform_data: Option, pub goal_phase: Phase, pub stdlib: &'a StdLib, pub exposed_types: SubsByModule, @@ -818,7 +831,7 @@ struct State<'a> { pub module_cache: ModuleCache<'a>, pub dependencies: Dependencies<'a>, - pub procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), Proc<'a>>, + pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, pub exposed_to_host: MutMap, /// This is the "final" list of IdentIds, after canonicalization and constraint gen @@ -849,7 +862,7 @@ struct State<'a> { pub needs_specialization: MutSet, pub all_pending_specializations: - MutMap, PendingSpecialization<'a>>>, + MutMap, PendingSpecialization<'a>>>, pub specializations_in_flight: u32, @@ -1447,7 +1460,7 @@ where let mut state = State { root_id, - platform_id: None, + platform_data: None, goal_phase, stdlib, output_path: None, @@ -1501,6 +1514,8 @@ where Msg::FinishedAllTypeChecking { solved_subs, exposed_vars_by_symbol, + exposed_aliases_by_symbol, + exposed_values, documentation, } => { // We're done! There should be no more messages pending. @@ -1516,6 +1531,8 @@ where return Ok(LoadResult::TypeChecked(finish( state, solved_subs, + exposed_values, + exposed_aliases_by_symbol, exposed_vars_by_symbol, documentation, ))); @@ -1674,10 +1691,13 @@ fn update<'a>( debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); state.platform_path = PlatformPath::Valid(to_platform.clone()); } - PkgConfig { .. } => { - debug_assert_eq!(state.platform_id, None); + PkgConfig { main_for_host, .. } => { + debug_assert!(matches!(state.platform_data, None)); - state.platform_id = Some(header.module_id); + state.platform_data = Some(PlatformData { + module_id: header.module_id, + provides: main_for_host, + }); if header.is_root_module { debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); @@ -1896,9 +1916,9 @@ fn update<'a>( // if there is a platform, the Package-Config module provides host-exposed, // otherwise the App module exposes host-exposed - let is_host_exposed = match state.platform_id { + let is_host_exposed = match state.platform_data { None => module_id == state.root_id, - Some(platform_id) => module_id == platform_id, + Some(ref platform_data) => module_id == platform_data.module_id, }; if is_host_exposed { @@ -1927,6 +1947,8 @@ fn update<'a>( .send(Msg::FinishedAllTypeChecking { solved_subs, exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol, + exposed_values: solved_module.exposed_symbols, + exposed_aliases_by_symbol: solved_module.aliases, documentation, }) .map_err(|_| LoadingProblem::MsgChannelDied)?; @@ -2025,7 +2047,7 @@ fn update<'a>( } MadeSpecializations { module_id, - mut ident_ids, + ident_ids, subs, procedures, external_specializations_requested, @@ -2061,22 +2083,16 @@ fn update<'a>( println!("{}", result); } - if false { - let it = state.procedures.iter().map(|x| x.1); - - if let Err(e) = roc_mono::alias_analysis::spec_program(it) { - println!("Error in alias analysis: {:?}", e) - } - } - Proc::insert_refcount_operations(arena, &mut state.procedures); - Proc::optimize_refcount_operations( - arena, - module_id, - &mut ident_ids, - &mut state.procedures, - ); + // This is not safe with the new non-recursive RC updates that we do for tag unions + // + // Proc::optimize_refcount_operations( + // arena, + // module_id, + // &mut ident_ids, + // &mut state.procedures, + // ); state.constrained_ident_ids.insert(module_id, ident_ids); @@ -2160,6 +2176,7 @@ fn finish_specialization( module_cache, output_path, platform_path, + platform_data, .. } = state; @@ -2207,6 +2224,34 @@ fn finish_specialization( let platform_path = path_to_platform.into(); + let entry_point = { + let symbol = match platform_data { + None => { + debug_assert_eq!(exposed_to_host.len(), 1); + *exposed_to_host.iter().next().unwrap().0 + } + Some(PlatformData { provides, .. }) => provides, + }; + + match procedures.keys().find(|(s, _)| *s == symbol) { + Some((_, layout)) => EntryPoint { + layout: *layout, + symbol, + }, + None => { + // the entry point is not specialized. This can happen if the repl output + // is a function value + EntryPoint { + layout: roc_mono::ir::ProcLayout { + arguments: &[], + result: Layout::Struct(&[]), + }, + symbol, + } + } + } + }; + Ok(MonomorphizedModule { can_problems, mono_problems, @@ -2218,6 +2263,7 @@ fn finish_specialization( subs, interns, procedures, + entry_point, sources, header_sources, timings: state.timings, @@ -2227,6 +2273,8 @@ fn finish_specialization( fn finish( state: State, solved: Solved, + exposed_values: Vec, + exposed_aliases_by_symbol: MutMap, exposed_vars_by_symbol: MutMap, documentation: MutMap, ) -> LoadedModule { @@ -2261,6 +2309,8 @@ fn finish( can_problems: state.module_cache.can_problems, type_problems: state.module_cache.type_problems, declarations_by_id: state.declarations_by_id, + exposed_aliases: exposed_aliases_by_symbol, + exposed_values, exposed_to_host: exposed_vars_by_symbol.into_iter().collect(), header_sources, sources, @@ -2991,7 +3041,7 @@ fn send_header_two<'a>( HashMap::with_capacity_and_hasher(scope_size, default_hasher()); let home: ModuleId; - let ident_ids = { + let mut ident_ids = { // Lock just long enough to perform the minimal operations necessary. let mut module_ids = (*module_ids).lock(); let mut ident_ids_by_module = (*ident_ids_by_module).lock(); @@ -3114,9 +3164,17 @@ fn send_header_two<'a>( // to decrement its "pending" count. let module_name = ModuleNameEnum::PkgConfig; + let main_for_host = { + let ident_str: InlinableString = provides[0].value.as_str().into(); + let ident_id = ident_ids.get_or_insert(&ident_str); + + Symbol::new(home, ident_id) + }; + let extra = HeaderFor::PkgConfig { config_shorthand: shorthand, platform_main_type: requires[0].value.clone(), + main_for_host, }; let mut package_qualified_imported_modules = MutSet::default(); @@ -3246,6 +3304,7 @@ fn run_solve<'a>( let solved_module = SolvedModule { exposed_vars_by_symbol, + exposed_symbols: exposed_symbols.into_iter().collect::>(), solved_types, problems, aliases: solved_env.aliases, @@ -3482,7 +3541,6 @@ fn fabricate_effects_module<'a>( &mut var_store, annotation, ); - exposed_symbols.insert(symbol); declarations.push(Declaration::Declare(def)); @@ -3821,7 +3879,8 @@ fn make_specializations<'a>( ident_ids: &mut ident_ids, ptr_bytes, update_mode_counter: 0, - call_specialization_counter: 0, + // call_specialization_counter=0 is reserved + call_specialization_counter: 1, }; // TODO: for now this final specialization pass is sequential, @@ -3884,7 +3943,8 @@ fn build_pending_specializations<'a>( ident_ids: &mut ident_ids, ptr_bytes, update_mode_counter: 0, - call_specialization_counter: 0, + // call_specialization_counter=0 is reserved + call_specialization_counter: 1, }; // Add modules' decls to Procs @@ -4003,7 +4063,7 @@ fn add_def_to_module<'a>( procs.insert_exposed( symbol, - TopLevelFunctionLayout::from_layout(mono_env.arena, layout), + ProcLayout::from_layout(mono_env.arena, layout), mono_env.arena, mono_env.subs, def.annotation, @@ -4034,14 +4094,14 @@ fn add_def_to_module<'a>( if is_exposed { let annotation = def.expr_var; - let layout = match layout_cache.from_var( + let top_level = match layout_cache.from_var( mono_env.arena, annotation, mono_env.subs, ) { Ok(l) => { // remember, this is a 0-argument thunk - Layout::FunctionPointer(&[], mono_env.arena.alloc(l)) + ProcLayout::new(mono_env.arena, &[], l) } Err(LayoutProblem::Erroneous) => { let message = "top level function has erroneous type"; @@ -4062,7 +4122,7 @@ fn add_def_to_module<'a>( procs.insert_exposed( symbol, - TopLevelFunctionLayout::from_layout(mono_env.arena, layout), + top_level, mono_env.arena, mono_env.subs, def.annotation, diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 2ed35be875..98a1f48cf8 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -18,7 +18,6 @@ pub enum LowLevel { ListLen, ListGetUnsafe, ListSet, - ListSetInPlace, ListSingle, ListRepeat, ListReverse, @@ -40,6 +39,7 @@ pub enum LowLevel { ListKeepErrs, ListSortWith, ListDrop, + ListSwap, DictSize, DictEmpty, DictInsert, @@ -124,7 +124,6 @@ impl LowLevel { | ListLen | ListGetUnsafe | ListSet - | ListSetInPlace | ListDrop | ListSingle | ListRepeat @@ -135,6 +134,7 @@ impl LowLevel { | ListPrepend | ListJoin | ListRange + | ListSwap | DictSize | DictEmpty | DictInsert diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 90fc894970..d107bdffe4 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -278,10 +278,10 @@ impl ModuleId { // This is a no-op that should get DCE'd } - pub fn to_string<'a>(&self, interns: &'a Interns) -> &'a InlinableString { + pub fn to_string(self, interns: &Interns) -> &InlinableString { interns .module_ids - .get_name(*self) + .get_name(self) .unwrap_or_else(|| panic!("Could not find ModuleIds for {:?}", self)) } } @@ -396,17 +396,15 @@ impl<'a> PackageModuleIds<'a> { fn insert_debug_name(module_id: ModuleId, module_name: &PQModuleName) { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); - if !names.contains_key(&module_id.0) { - match module_name { - PQModuleName::Unqualified(module) => { - names.insert(module_id.0, module.to_string().into()); - } + names + .entry(module_id.0) + .or_insert_with(|| match module_name { + PQModuleName::Unqualified(module) => module.to_string().into(), PQModuleName::Qualified(package, module) => { let name = format!("{}.{}", package, module).into(); - names.insert(module_id.0, name); + name } - } - } + }); } #[cfg(not(debug_assertions))] @@ -464,9 +462,9 @@ impl ModuleIds { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); // TODO make sure modules are never added more than once! - if !names.contains_key(&module_id.0) { - names.insert(module_id.0, module_name.to_string().into()); - } + names + .entry(module_id.0) + .or_insert_with(|| module_name.to_string().into()); } #[cfg(not(debug_assertions))] @@ -904,6 +902,7 @@ define_builtins! { 14 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias 15 STR_TO_BYTES: "toBytes" 16 STR_STARTS_WITH_CODE_POINT: "startsWithCodePoint" + 17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime } 4 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias @@ -939,6 +938,7 @@ define_builtins! { 30 LIST_RANGE: "range" 31 LIST_SORT_WITH: "sortWith" 32 LIST_DROP: "drop" + 33 LIST_SWAP: "swap" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index ebca28d1a5..2b4354d26e 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -1,8 +1,8 @@ use morphic_lib::TypeContext; use morphic_lib::{ - BlockExpr, BlockId, CalleeSpecVar, EntryPointName, ExprContext, FuncDef, FuncDefBuilder, - FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, TypeName, UpdateModeVar, - ValueId, + BlockExpr, BlockId, CalleeSpecVar, ConstDefBuilder, ConstName, EntryPointName, ExprContext, + FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, + UpdateModeVar, ValueId, }; use roc_collections::all::MutMap; use roc_module::low_level::LowLevel; @@ -13,25 +13,144 @@ use crate::ir::{Call, CallType, Expr, Literal, ModifyRc, Proc, Stmt}; use crate::layout::{Builtin, Layout, ListLayout, UnionLayout}; // just using one module for now -const MOD_LIST: ModName = ModName(b"UserApp"); -const MOD_APP: ModName = ModName(b"UserApp"); +pub const MOD_APP: ModName = ModName(b"UserApp"); -pub fn spec_program<'a, I>(procs: I) -> Result +pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STATIC.to_ne_bytes()); + +const ENTRY_POINT_NAME: &[u8] = b"mainForHost"; + +pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] { + func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout) +} + +const DEBUG: bool = false; +const SIZE: usize = if DEBUG { 50 } else { 16 }; + +pub fn func_name_bytes_help<'a, I>( + symbol: Symbol, + argument_layouts: I, + return_layout: Layout<'a>, +) -> [u8; SIZE] +where + I: Iterator>, +{ + let mut name_bytes = [0u8; SIZE]; + + use std::collections::hash_map::DefaultHasher; + use std::hash::Hash; + use std::hash::Hasher; + + let layout_hash = { + let mut hasher = DefaultHasher::new(); + + for layout in argument_layouts { + match layout { + Layout::Closure(_, lambda_set, _) => { + lambda_set.runtime_representation().hash(&mut hasher); + } + _ => { + layout.hash(&mut hasher); + } + } + } + + match return_layout { + Layout::Closure(_, lambda_set, _) => { + lambda_set.runtime_representation().hash(&mut hasher); + } + _ => { + return_layout.hash(&mut hasher); + } + } + + hasher.finish() + }; + + let sbytes = symbol.to_ne_bytes(); + let lbytes = layout_hash.to_ne_bytes(); + + let it = sbytes + .iter() + .chain(lbytes.iter()) + .zip(name_bytes.iter_mut()); + + for (source, target) in it { + *target = *source; + } + + if DEBUG { + for (i, c) in (format!("{:?}", symbol)).chars().take(25).enumerate() { + name_bytes[25 + i] = c as u8; + } + } + + name_bytes +} + +fn bytes_as_ascii(bytes: &[u8]) -> String { + use std::fmt::Write; + + let mut buf = String::new(); + + for byte in bytes { + write!(buf, "{:02X}", byte).unwrap(); + } + + buf +} + +pub fn spec_program<'a, I>( + entry_point: crate::ir::EntryPoint<'a>, + procs: I, +) -> Result where I: Iterator>, { - let mut main_function = None; let main_module = { let mut m = ModDefBuilder::new(); + // a const that models all static strings + let static_str_def = { + let mut cbuilder = ConstDefBuilder::new(); + let block = cbuilder.add_block(); + let cell = cbuilder.add_new_heap_cell(block)?; + let value_id = cbuilder.add_make_tuple(block, &[cell])?; + let root = BlockExpr(block, value_id); + let str_type_id = str_type(&mut cbuilder)?; + + cbuilder.build(str_type_id, root)? + }; + m.add_const(STATIC_STR_NAME, static_str_def)?; + + // the entry point wrapper + let roc_main_bytes = func_name_bytes_help( + entry_point.symbol, + entry_point.layout.arguments.iter().copied(), + entry_point.layout.result, + ); + let roc_main = FuncName(&roc_main_bytes); + + let entry_point_function = build_entry_point(entry_point.layout, roc_main)?; + let entry_point_name = FuncName(ENTRY_POINT_NAME); + m.add_func(entry_point_name, entry_point_function)?; + + // all other functions for proc in procs { + let bytes = func_name_bytes(proc); + let func_name = FuncName(&bytes); + + if DEBUG { + eprintln!( + "{:?}: {:?} with {:?} args", + proc.name, + bytes_as_ascii(&bytes), + (proc.args, proc.ret_layout), + ); + } + let spec = proc_spec(proc)?; - m.add_func(FuncName(&proc.name.to_ne_bytes()), spec)?; - - if format!("{:?}", proc.name).contains("mainForHost") { - main_function = Some(proc.name); - } + m.add_func(func_name, spec)?; } m.build()? @@ -40,18 +159,42 @@ where let program = { let mut p = ProgramBuilder::new(); p.add_mod(MOD_APP, main_module)?; - p.add_entry_point( - EntryPointName(b"mainForHost"), - MOD_APP, - FuncName(&main_function.unwrap().to_ne_bytes()), - )?; + + let entry_point_name = FuncName(ENTRY_POINT_NAME); + p.add_entry_point(EntryPointName(ENTRY_POINT_NAME), MOD_APP, entry_point_name)?; p.build()? }; + if DEBUG { + eprintln!("{}", program.to_source_string()); + } + morphic_lib::solve(program) } +fn build_entry_point(layout: crate::ir::ProcLayout, func_name: FuncName) -> Result { + let mut builder = FuncDefBuilder::new(); + let block = builder.add_block(); + + // to the modelling language, the arguments appear out of thin air + let argument_type = build_tuple_type(&mut builder, layout.arguments)?; + let argument = builder.add_unknown_with(block, &[], argument_type)?; + + let name_bytes = [0; 16]; + let spec_var = CalleeSpecVar(&name_bytes); + let result = builder.add_call(block, spec_var, MOD_APP, func_name, argument)?; + + // to the modelling language, the result disappears into the void + let unit_type = builder.add_tuple_type(&[])?; + let unit_value = builder.add_unknown_with(block, &[result], unit_type)?; + + let root = BlockExpr(block, unit_value); + let spec = builder.build(unit_type, unit_type, root)?; + + Ok(spec) +} + fn proc_spec(proc: &Proc) -> Result { let mut builder = FuncDefBuilder::new(); let mut env = Env::default(); @@ -72,13 +215,16 @@ fn proc_spec(proc: &Proc) -> Result { let root = BlockExpr(block, value_id); let arg_type_id = layout_spec(&mut builder, &Layout::Struct(&argument_layouts))?; let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?; - builder.build(arg_type_id, ret_type_id, root) + + let spec = builder.build(arg_type_id, ret_type_id, root)?; + + Ok(spec) } #[derive(Default)] struct Env { symbols: MutMap, - join_points: MutMap, + join_points: MutMap, } fn stmt_spec( @@ -91,11 +237,25 @@ fn stmt_spec( use Stmt::*; match stmt { - Let(symbol, expr, layout, continuation) => { - let value_id = expr_spec(builder, env, block, layout, expr)?; + Let(symbol, expr, expr_layout, mut continuation) => { + let value_id = expr_spec(builder, env, block, expr_layout, expr)?; env.symbols.insert(*symbol, value_id); + + let mut queue = vec![symbol]; + + while let Let(symbol, expr, expr_layout, c) = continuation { + let value_id = expr_spec(builder, env, block, expr_layout, expr)?; + env.symbols.insert(*symbol, value_id); + + queue.push(symbol); + continuation = c; + } + let result = stmt_spec(builder, env, block, layout, continuation)?; - env.symbols.remove(symbol); + + for symbol in queue { + env.symbols.remove(symbol); + } Ok(result) } @@ -105,6 +265,7 @@ fn stmt_spec( layout: call_layout, pass, fail, + exception_id: _, } => { // a call that might throw an exception @@ -127,7 +288,7 @@ fn stmt_spec( cond_layout: _, branches, default_branch, - ret_layout, + ret_layout: _lies, } => { let mut cases = Vec::with_capacity(branches.len() + 1); @@ -138,7 +299,7 @@ fn stmt_spec( for branch in it { let block = builder.add_block(); - let value_id = stmt_spec(builder, env, block, ret_layout, branch)?; + let value_id = stmt_spec(builder, env, block, layout, branch)?; cases.push(BlockExpr(block, value_id)); } @@ -146,12 +307,27 @@ fn stmt_spec( } Ret(symbol) => Ok(env.symbols[symbol]), Refcounting(modify_rc, continuation) => match modify_rc { - ModifyRc::Inc(symbol, _) | ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => { - let result_type = builder.add_tuple_type(&[])?; + ModifyRc::Inc(symbol, _) => { let argument = env.symbols[symbol]; - // this is how RC is modelled; it recursively touches all heap cells - builder.add_unknown_with(block, &[argument], result_type)?; + // a recursive touch is never worse for optimizations than a normal touch + // and a bit more permissive in its type + builder.add_recursive_touch(block, argument)?; + + stmt_spec(builder, env, block, layout, continuation) + } + + ModifyRc::Dec(symbol) => { + let argument = env.symbols[symbol]; + + builder.add_recursive_touch(block, argument)?; + + stmt_spec(builder, env, block, layout, continuation) + } + ModifyRc::DecRef(symbol) => { + let argument = env.symbols[symbol]; + + builder.add_recursive_touch(block, argument)?; stmt_spec(builder, env, block, layout, continuation) } @@ -159,7 +335,7 @@ fn stmt_spec( Join { id, parameters, - continuation, + body, remainder, } => { let mut type_ids = Vec::new(); @@ -173,31 +349,37 @@ fn stmt_spec( let jp_arg_type_id = builder.add_tuple_type(&type_ids)?; let (jpid, jp_argument) = - builder.declare_join_point(block, jp_arg_type_id, ret_type_id)?; + builder.declare_continuation(block, jp_arg_type_id, ret_type_id)?; + // NOTE join point arguments can shadow variables from the outer scope + // the ordering of steps here is important + + // add this ID so both body and remainder can reference it + env.join_points.insert(*id, jpid); + + // first, with the current variable bindings, process the remainder + let cont_block = builder.add_block(); + let cont_value_id = stmt_spec(builder, env, cont_block, layout, remainder)?; + + // only then introduce variables bound by the jump point, and process its body let join_body_sub_block = { - env.join_points.insert(*id, jpid); let jp_body_block = builder.add_block(); // unpack the argument for (i, p) in parameters.iter().enumerate() { let value_id = builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?; + env.symbols.insert(p.symbol, value_id); } - let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?; + let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, body)?; + BlockExpr(jp_body_block, jp_body_value_id) }; - // NOTE the symbols bound by the join point can shadow the argument symbols of the - // surrounding function, so we don't remove them from the env here - - let cont_block = builder.add_block(); - let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?; - env.join_points.remove(id); - builder.define_join_point(jpid, join_body_sub_block)?; + builder.define_continuation(jpid, join_body_sub_block)?; builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id)) } @@ -208,7 +390,7 @@ fn stmt_spec( let jpid = env.join_points[id]; builder.add_jump(block, jpid, argument, ret_type_id) } - Rethrow | RuntimeError(_) => { + Resume(_) | RuntimeError(_) => { let type_id = layout_spec(builder, layout)?; builder.add_terminate(block, type_id) @@ -260,17 +442,17 @@ fn call_spec( match &call.call_type { ByName { name: symbol, - full_layout: _, - ret_layout: _, - arg_layouts: _, + ret_layout, + arg_layouts, specialization_id, } => { let array = specialization_id.to_bytes(); let spec_var = CalleeSpecVar(&array); let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?; - let slice = &symbol.to_ne_bytes(); - let name = FuncName(slice); + let it = arg_layouts.iter().copied(); + let bytes = func_name_bytes_help(*symbol, it, *ret_layout); + let name = FuncName(&bytes); let module = MOD_APP; builder.add_call(block, spec_var, module, name, arg_value_id) } @@ -297,7 +479,223 @@ fn call_spec( *update_mode, call.arguments, ), - HigherOrderLowLevel { .. } => todo!(), + HigherOrderLowLevel { + specialization_id, + closure_env_layout, + op, + arg_layouts, + ret_layout, + .. + } => { + let array = specialization_id.to_bytes(); + let spec_var = CalleeSpecVar(&array); + + let symbol = { + use roc_module::low_level::LowLevel::*; + + match op { + ListMap | ListMapWithIndex => call.arguments[1], + ListMap2 => call.arguments[2], + ListMap3 => call.arguments[3], + ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => call.arguments[2], + ListKeepIf | ListKeepOks | ListKeepErrs => call.arguments[1], + ListSortWith => call.arguments[1], + _ => unreachable!(), + } + }; + + let it = arg_layouts.iter().copied(); + let bytes = func_name_bytes_help(symbol, it, *ret_layout); + let name = FuncName(&bytes); + let module = MOD_APP; + + { + use roc_module::low_level::LowLevel::*; + + match op { + DictWalk => { + let dict = env.symbols[&call.arguments[0]]; + let default = env.symbols[&call.arguments[1]]; + let closure_env = env.symbols[&call.arguments[3]]; + + let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?; + let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?; + + let first = builder.add_bag_get(block, bag)?; + + let key = builder.add_get_tuple_field(block, first, 0)?; + let val = builder.add_get_tuple_field(block, first, 1)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[key, val, default])? + } else { + builder.add_make_tuple(block, &[key, val, default, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListWalk | ListWalkBackwards | ListWalkUntil => { + let list = env.symbols[&call.arguments[0]]; + let default = env.symbols[&call.arguments[1]]; + let closure_env = env.symbols[&call.arguments[3]]; + + let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; + + let first = builder.add_bag_get(block, bag)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[first, default])? + } else { + builder.add_make_tuple(block, &[first, default, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListMapWithIndex => { + let list = env.symbols[&call.arguments[0]]; + let closure_env = env.symbols[&call.arguments[2]]; + + let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; + + let first = builder.add_bag_get(block, bag)?; + let index = builder.add_make_tuple(block, &[])?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[first, index])? + } else { + builder.add_make_tuple(block, &[first, index, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListMap => { + let list1 = env.symbols[&call.arguments[0]]; + let closure_env = env.symbols[&call.arguments[2]]; + + let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; + let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; + + let elem1 = builder.add_bag_get(block, bag1)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[elem1])? + } else { + builder.add_make_tuple(block, &[elem1, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListSortWith => { + let list1 = env.symbols[&call.arguments[0]]; + let closure_env = env.symbols[&call.arguments[2]]; + + let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; + let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; + + let elem1 = builder.add_bag_get(block, bag1)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[elem1, elem1])? + } else { + builder.add_make_tuple(block, &[elem1, elem1, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListMap2 => { + let list1 = env.symbols[&call.arguments[0]]; + let list2 = env.symbols[&call.arguments[1]]; + let closure_env = env.symbols[&call.arguments[3]]; + + let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; + let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; + let elem1 = builder.add_bag_get(block, bag1)?; + + let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?; + let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?; + let elem2 = builder.add_bag_get(block, bag2)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[elem1, elem2])? + } else { + builder.add_make_tuple(block, &[elem1, elem2, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListMap3 => { + let list1 = env.symbols[&call.arguments[0]]; + let list2 = env.symbols[&call.arguments[1]]; + let list3 = env.symbols[&call.arguments[2]]; + let closure_env = env.symbols[&call.arguments[4]]; + + let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?; + let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?; + let elem1 = builder.add_bag_get(block, bag1)?; + + let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?; + let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?; + let elem2 = builder.add_bag_get(block, bag2)?; + + let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?; + let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?; + let elem3 = builder.add_bag_get(block, bag3)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[elem1, elem2, elem3])? + } else { + builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])? + }; + builder.add_call(block, spec_var, module, name, argument)?; + } + + ListKeepIf | ListKeepOks | ListKeepErrs => { + let list = env.symbols[&call.arguments[0]]; + let closure_env = env.symbols[&call.arguments[2]]; + + let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; + // let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; + + let first = builder.add_bag_get(block, bag)?; + + let argument = if closure_env_layout.is_none() { + builder.add_make_tuple(block, &[first])? + } else { + builder.add_make_tuple(block, &[first, closure_env])? + }; + let result = builder.add_call(block, spec_var, module, name, argument)?; + let unit = builder.add_tuple_type(&[])?; + builder.add_unknown_with(block, &[result], unit)?; + } + + _ => { + // fake a call to the function argument + // to make sure the function is specialized + + // very invalid + let arg_value_id = build_tuple_value(builder, env, block, &[])?; + + builder.add_call(block, spec_var, module, name, arg_value_id)?; + } + } + } + + // TODO overly pessimstic + // filter_map because one of the arguments is a function name, which + // is not defined in the env + let arguments: Vec<_> = call + .arguments + .iter() + .filter_map(|symbol| env.symbols.get(symbol)) + .copied() + .collect(); + + let result_type = layout_spec(builder, layout)?; + + builder.add_unknown_with(block, &arguments, result_type) + } } } @@ -341,12 +739,17 @@ fn lowlevel_spec( builder.add_sub_block(block, sub_block) } - Eq | NotEq => new_bool(builder, block), - NumLte | NumLt | NumGt | NumGte => new_order(builder, block), + Eq | NotEq => { + // just dream up a unit value + builder.add_make_tuple(block, &[]) + } + NumLte | NumLt | NumGt | NumGte => { + // just dream up a unit value + builder.add_make_tuple(block, &[]) + } ListLen => { - let list = env.symbols[&arguments[0]]; - - builder.add_get_tuple_field(block, list, LIST_LEN_INDEX) + // just dream up a unit value + builder.add_make_tuple(block, &[]) } ListGetUnsafe => { // NOTE the ListGet lowlevel op is only evaluated if the index is in-bounds @@ -372,21 +775,18 @@ fn lowlevel_spec( Ok(list) } - other => todo!("lowlevel op not implemented: {:?}", other), + _other => { + // TODO overly pessimstic + let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect(); + + let result_type = layout_spec(builder, layout)?; + + builder.add_unknown_with(block, &arguments, result_type) + } } } fn build_variant_types( - builder: &mut FuncDefBuilder, - layout: &Layout, -) -> Option>> { - match layout { - Layout::Union(union_layout) => Some(build_variant_types_help(builder, union_layout)), - _ => None, - } -} - -fn build_variant_types_help( builder: &mut FuncDefBuilder, union_layout: &UnionLayout, ) -> Result> { @@ -400,24 +800,29 @@ fn build_variant_types_help( result.push(build_tuple_type(builder, tag)?); } } - Recursive(_) => todo!(), - NonNullableUnwrapped(_) => todo!(), + Recursive(_) => unreachable!(), + NonNullableUnwrapped(_) => unreachable!(), NullableWrapped { nullable_id: _, other_tags: _, - } => todo!(), + } => unreachable!(), NullableUnwrapped { nullable_id: _, other_fields: _, - } => todo!(), + } => unreachable!(), } Ok(result) } +fn worst_case_type(context: &mut impl TypeContext) -> Result { + let cell = context.add_heap_cell_type(); + context.add_bag_type(cell) +} + fn expr_spec( builder: &mut FuncDefBuilder, - env: &Env, + env: &mut Env, block: BlockId, layout: &Layout, expr: &Expr, @@ -433,37 +838,48 @@ fn expr_spec( tag_id, union_size: _, arguments, - } => { - let value_id = build_tuple_value(builder, env, block, arguments)?; - let variant_types = build_variant_types(builder, tag_layout).unwrap()?; - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) - } - Struct(fields) => build_tuple_value(builder, env, block, fields), - AccessAtIndex { - index, - field_layouts: _, - structure, - wrapped, - } => { - use crate::ir::Wrapped; - - let value_id = env.symbols[structure]; - - match wrapped { - Wrapped::EmptyRecord => { - // this is a unit value - builder.add_make_tuple(block, &[]) - } - Wrapped::SingleElementRecord => { - todo!("do we unwrap single-element records still?") - } - Wrapped::RecordOrSingleTagUnion => { - builder.add_get_tuple_field(block, value_id, *index as u32) - } - Wrapped::MultiTagUnion => { - builder.add_get_tuple_field(block, value_id, *index as u32) - } + } => match tag_layout { + UnionLayout::NonRecursive(_) => { + let value_id = build_tuple_value(builder, env, block, arguments)?; + let variant_types = build_variant_types(builder, tag_layout)?; + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) } + UnionLayout::Recursive(_) + | UnionLayout::NonNullableUnwrapped(_) + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NullableUnwrapped { .. } => { + let result_type = worst_case_type(builder)?; + let value_id = build_tuple_value(builder, env, block, arguments)?; + builder.add_unknown_with(block, &[value_id], result_type) + } + }, + Struct(fields) => build_tuple_value(builder, env, block, fields), + UnionAtIndex { + index, + tag_id, + structure, + union_layout, + } => match union_layout { + UnionLayout::NonRecursive(_) => { + let index = (*index) as u32; + let tag_value_id = env.symbols[structure]; + let tuple_value_id = + builder.add_unwrap_union(block, tag_value_id, *tag_id as u32)?; + + builder.add_get_tuple_field(block, tuple_value_id, index) + } + _ => { + // for the moment recursive tag unions don't quite work + let value_id = env.symbols[structure]; + let result_type = layout_spec(builder, layout)?; + builder.add_unknown_with(block, &[value_id], result_type) + } + }, + StructAtIndex { + index, structure, .. + } => { + let value_id = env.symbols[structure]; + builder.add_get_tuple_field(block, value_id, *index as u32) } Array { elem_layout, elems } => { let type_id = layout_spec(builder, elem_layout)?; @@ -478,7 +894,9 @@ fn expr_spec( bag = builder.add_bag_insert(block, bag, value_id)?; } - Ok(bag) + let cell = builder.add_new_heap_cell(block)?; + + builder.add_make_tuple(block, &[cell, bag]) } EmptyArray => { @@ -504,6 +922,7 @@ fn expr_spec( builder.add_terminate(block, type_id) } + GetTagId { .. } => builder.add_make_tuple(block, &[]), } } @@ -525,16 +944,25 @@ fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result match layout { Builtin(builtin) => builtin_spec(builder, builtin), - PhantomEmptyStruct => todo!(), Struct(fields) => build_tuple_type(builder, fields), - Union(union_layout) => { - let variant_types = build_variant_types_help(builder, union_layout)?; - builder.add_union_type(&variant_types) - } - RecursivePointer => todo!(), - FunctionPointer(_, _) => todo!(), - Closure(_, _, _) => todo!(), - Pointer(_) => todo!(), + Union(union_layout) => match union_layout { + UnionLayout::NonRecursive(_) => { + let variant_types = build_variant_types(builder, union_layout)?; + builder.add_union_type(&variant_types) + } + UnionLayout::Recursive(_) => worst_case_type(builder), + UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), + UnionLayout::NullableWrapped { + nullable_id: _, + other_tags: _, + } => worst_case_type(builder), + UnionLayout::NullableUnwrapped { + nullable_id: _, + other_fields: _, + } => worst_case_type(builder), + }, + RecursivePointer => worst_case_type(builder), + Closure(_, lambda_set, _) => layout_spec(builder, &lambda_set.runtime_representation()), } } @@ -543,69 +971,80 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result builder.add_tuple_type(&[]), - Float128 => todo!(), - Float64 => todo!(), - Float32 => todo!(), - Float16 => todo!(), - Str => todo!(), - Dict(_, _) => todo!(), - Set(_) => todo!(), - List(_, _) => { - // TODO should incorporate the element type into the name - Ok(builder.add_named_type(MOD_LIST, TypeName(b"List"))) + Float128 | Float64 | Float32 | Float16 => builder.add_tuple_type(&[]), + Str | EmptyStr => str_type(builder), + Dict(key_layout, value_layout) => { + let value_type = layout_spec(builder, value_layout)?; + let key_type = layout_spec(builder, key_layout)?; + let element_type = builder.add_tuple_type(&[key_type, value_type])?; + + let cell = builder.add_heap_cell_type(); + let bag = builder.add_bag_type(element_type)?; + builder.add_tuple_type(&[cell, bag]) + } + Set(key_layout) => { + let value_type = builder.add_tuple_type(&[])?; + let key_type = layout_spec(builder, key_layout)?; + let element_type = builder.add_tuple_type(&[key_type, value_type])?; + + let cell = builder.add_heap_cell_type(); + let bag = builder.add_bag_type(element_type)?; + builder.add_tuple_type(&[cell, bag]) + } + List(element_layout) => { + let element_type = layout_spec(builder, element_layout)?; + + let cell = builder.add_heap_cell_type(); + let bag = builder.add_bag_type(element_type)?; + + builder.add_tuple_type(&[cell, bag]) + } + EmptyList => { + // TODO make sure that we consistently treat the EmptyList as a list of unit values + let element_type = builder.add_tuple_type(&[])?; + + let cell = builder.add_heap_cell_type(); + let bag = builder.add_bag_type(element_type)?; + + builder.add_tuple_type(&[cell, bag]) + } + EmptyDict | EmptySet => { + // TODO make sure that we consistently treat the these as a dict of unit values + let unit = builder.add_tuple_type(&[])?; + let element_type = builder.add_tuple_type(&[unit, unit])?; + + let cell = builder.add_heap_cell_type(); + let bag = builder.add_bag_type(element_type)?; + + builder.add_tuple_type(&[cell, bag]) } - EmptyStr => todo!(), - EmptyList => todo!(), - EmptyDict => todo!(), - EmptySet => todo!(), } } +fn str_type(builder: &mut TC) -> Result { + let cell_id = builder.add_heap_cell_type(); + builder.add_tuple_type(&[cell_id]) +} + // const OK_TAG_ID: u8 = 1u8; // const ERR_TAG_ID: u8 = 0u8; const LIST_CELL_INDEX: u32 = 0; const LIST_BAG_INDEX: u32 = 1; -const LIST_LEN_INDEX: u32 = 2; + +const DICT_CELL_INDEX: u32 = LIST_CELL_INDEX; +const DICT_BAG_INDEX: u32 = LIST_BAG_INDEX; fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result { let cell = builder.add_new_heap_cell(block)?; let bag = builder.add_empty_bag(block, element_type)?; - let length = new_usize(builder, block)?; - builder.add_make_tuple(block, &[cell, bag, length]) -} - -fn new_usize(builder: &mut FuncDefBuilder, block: BlockId) -> Result { - new_num(builder, block) + builder.add_make_tuple(block, &[cell, bag]) } fn new_static_string(builder: &mut FuncDefBuilder, block: BlockId) -> Result { - let cell = builder.add_new_heap_cell(block)?; + let module = MOD_APP; - // immediately mutate the cell, so any future updates on this value are invalid - // updating a static string would cause a crash at runtime - let _ = builder.add_update(block, UpdateModeVar(&[]), cell)?; - - let length = new_usize(builder, block)?; - builder.add_make_tuple(block, &[cell, length]) -} - -fn new_order(builder: &mut FuncDefBuilder, block: BlockId) -> Result { - // always generats EQ - let tag_id = 0; - - let unit = builder.add_tuple_type(&[])?; - let unit_value = builder.add_make_tuple(block, &[])?; - builder.add_make_union(block, &[unit, unit, unit], tag_id, unit_value) -} - -fn new_bool(builder: &mut FuncDefBuilder, block: BlockId) -> Result { - // always generats False - let tag_id = 0; - - let unit = builder.add_tuple_type(&[])?; - let unit_value = builder.add_make_tuple(block, &[])?; - builder.add_make_union(block, &[unit, unit], tag_id, unit_value) + builder.add_const_ref(block, module, STATIC_STR_NAME) } fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result { diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 443bfb20df..be0aa9bb1d 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -1,4 +1,4 @@ -use crate::ir::{Expr, JoinPointId, Param, Proc, Stmt, TopLevelFunctionLayout}; +use crate::ir::{Expr, JoinPointId, Param, Proc, ProcLayout, Stmt}; use crate::layout::Layout; use bumpalo::collections::Vec; use bumpalo::Bump; @@ -18,15 +18,14 @@ fn should_borrow_layout(layout: &Layout) -> bool { pub fn infer_borrow<'a>( arena: &'a Bump, - procs: &MutMap<(Symbol, TopLevelFunctionLayout<'a>), Proc<'a>>, + procs: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, ) -> ParamMap<'a> { let mut param_map = ParamMap { items: MutMap::default(), }; - for ((s, top_level), proc) in procs { - let key = (*s, arena.alloc(*top_level).full()); - param_map.visit_proc(arena, proc, key); + for (key, proc) in procs { + param_map.visit_proc(arena, proc, *key); } let mut env = BorrowInfState { @@ -40,7 +39,7 @@ pub fn infer_borrow<'a>( // This is a fixed-point analysis // - // all functions initiall own all their paramters + // all functions initiall own all their parameters // through a series of checks and heuristics, some arguments are set to borrowed // when that doesn't lead to conflicts the change is kept, otherwise it may be reverted // @@ -51,8 +50,7 @@ pub fn infer_borrow<'a>( // mutually recursive functions (or just make all their arguments owned) for (key, proc) in procs { - let layout = arena.alloc(key.1).full(); - env.collect_proc(proc, layout); + env.collect_proc(proc, key.1); } if !env.modified { @@ -69,7 +67,7 @@ pub fn infer_borrow<'a>( #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Key<'a> { - Declaration(Symbol, Layout<'a>), + Declaration(Symbol, ProcLayout<'a>), JoinPoint(JoinPointId), } @@ -98,7 +96,7 @@ impl<'a> IntoIterator for &'a ParamMap<'a> { } impl<'a> ParamMap<'a> { - pub fn get_symbol(&self, symbol: Symbol, layout: Layout<'a>) -> Option<&'a [Param<'a>]> { + pub fn get_symbol(&self, symbol: Symbol, layout: ProcLayout<'a>) -> Option<&'a [Param<'a>]> { let key = Key::Declaration(symbol, layout); self.items.get(&key).copied() @@ -153,7 +151,7 @@ impl<'a> ParamMap<'a> { .into_bump_slice() } - fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>, key: (Symbol, Layout<'a>)) { + fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>, key: (Symbol, ProcLayout<'a>)) { if proc.must_own_arguments { self.visit_proc_always_owned(arena, proc, key); return; @@ -171,7 +169,7 @@ impl<'a> ParamMap<'a> { &mut self, arena: &'a Bump, proc: &Proc<'a>, - key: (Symbol, Layout<'a>), + key: (Symbol, ProcLayout<'a>), ) { let already_in_there = self.items.insert( Key::Declaration(proc.name, key.1), @@ -193,12 +191,16 @@ impl<'a> ParamMap<'a> { id: j, parameters: xs, remainder: v, - continuation: b, + body: b, } => { let already_in_there = self .items .insert(Key::JoinPoint(*j), Self::init_borrow_params(arena, xs)); - debug_assert!(already_in_there.is_none()); + debug_assert!( + already_in_there.is_none(), + "join point {:?} is already defined!", + j + ); stack.push(v); stack.push(b); @@ -220,7 +222,7 @@ impl<'a> ParamMap<'a> { } Refcounting(_, _) => unreachable!("these have not been introduced yet"), - Ret(_) | Rethrow | Jump(_, _) | RuntimeError(_) => { + Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => { // these are terminal, do nothing } } @@ -348,7 +350,7 @@ impl<'a> BorrowInfState<'a> { /// let z = e in ... /// /// and determines whether z and which of the symbols used in e - /// must be taken as owned paramters + /// must be taken as owned parameters fn collect_call(&mut self, z: Symbol, e: &crate::ir::Call<'a>) { use crate::ir::CallType::*; @@ -359,12 +361,17 @@ impl<'a> BorrowInfState<'a> { match call_type { ByName { - name, full_layout, .. + name, + ret_layout, + arg_layouts, + .. } => { + let top_level = ProcLayout::new(self.arena, arg_layouts, *ret_layout); + // get the borrow signature of the applied function let ps = self .param_map - .get_symbol(*name, *full_layout) + .get_symbol(*name, top_level) .expect("function is defined"); // the return value will be owned @@ -393,15 +400,23 @@ impl<'a> BorrowInfState<'a> { } HigherOrderLowLevel { - op, closure_layout, .. + op, + arg_layouts, + ret_layout, + .. } => { use roc_module::low_level::LowLevel::*; debug_assert!(op.is_higher_order()); + let closure_layout = ProcLayout { + arguments: arg_layouts, + result: *ret_layout, + }; + match op { ListMap | ListKeepIf | ListKeepOks | ListKeepErrs => { - match self.param_map.get_symbol(arguments[1], *closure_layout) { + match self.param_map.get_symbol(arguments[1], closure_layout) { Some(function_ps) => { // own the list if the function wants to own the element if !function_ps[0].borrow { @@ -417,7 +432,7 @@ impl<'a> BorrowInfState<'a> { } } ListMapWithIndex => { - match self.param_map.get_symbol(arguments[1], *closure_layout) { + match self.param_map.get_symbol(arguments[1], closure_layout) { Some(function_ps) => { // own the list if the function wants to own the element if !function_ps[1].borrow { @@ -432,7 +447,7 @@ impl<'a> BorrowInfState<'a> { None => unreachable!(), } } - ListMap2 => match self.param_map.get_symbol(arguments[2], *closure_layout) { + ListMap2 => match self.param_map.get_symbol(arguments[2], closure_layout) { Some(function_ps) => { // own the lists if the function wants to own the element if !function_ps[0].borrow { @@ -450,7 +465,7 @@ impl<'a> BorrowInfState<'a> { } None => unreachable!(), }, - ListMap3 => match self.param_map.get_symbol(arguments[3], *closure_layout) { + ListMap3 => match self.param_map.get_symbol(arguments[3], closure_layout) { Some(function_ps) => { // own the lists if the function wants to own the element if !function_ps[0].borrow { @@ -471,7 +486,7 @@ impl<'a> BorrowInfState<'a> { None => unreachable!(), }, ListSortWith => { - match self.param_map.get_symbol(arguments[1], *closure_layout) { + match self.param_map.get_symbol(arguments[1], closure_layout) { Some(function_ps) => { // always own the input list self.own_var(arguments[0]); @@ -485,7 +500,7 @@ impl<'a> BorrowInfState<'a> { } } ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => { - match self.param_map.get_symbol(arguments[2], *closure_layout) { + match self.param_map.get_symbol(arguments[2], closure_layout) { Some(function_ps) => { // own the data structure if the function wants to own the element if !function_ps[0].borrow { @@ -554,7 +569,12 @@ impl<'a> BorrowInfState<'a> { EmptyArray => { self.own_var(z); } - AccessAtIndex { structure: x, .. } => { + + Call(call) => self.collect_call(z, call), + + Literal(_) | RuntimeErrorFunction(_) => {} + + StructAtIndex { structure: x, .. } => { // if the structure (record/tag/array) is owned, the extracted value is if self.is_owned(*x) { self.own_var(z); @@ -566,9 +586,29 @@ impl<'a> BorrowInfState<'a> { } } - Call(call) => self.collect_call(z, call), + UnionAtIndex { structure: x, .. } => { + // if the structure (record/tag/array) is owned, the extracted value is + if self.is_owned(*x) { + self.own_var(z); + } - Literal(_) | RuntimeErrorFunction(_) => {} + // if the extracted value is owned, the structure must be too + if self.is_owned(z) { + self.own_var(*x); + } + } + + GetTagId { structure: x, .. } => { + // if the structure (record/tag/array) is owned, the extracted value is + if self.is_owned(*x) { + self.own_var(z); + } + + // if the extracted value is owned, the structure must be too + if self.is_owned(z) { + self.own_var(*x); + } + } } } @@ -579,7 +619,8 @@ impl<'a> BorrowInfState<'a> { call_type: crate::ir::CallType::ByName { name: g, - full_layout, + arg_layouts, + ret_layout, .. }, arguments: ys, @@ -588,10 +629,12 @@ impl<'a> BorrowInfState<'a> { Stmt::Ret(z), ) = (v, b) { + let top_level = ProcLayout::new(self.arena, arg_layouts, *ret_layout); + if self.current_proc == *g && x == *z { // anonymous functions (for which the ps may not be known) // can never be tail-recursive, so this is fine - if let Some(ps) = self.param_map.get_symbol(*g, *full_layout) { + if let Some(ps) = self.param_map.get_symbol(*g, top_level) { self.own_params_using_args(ys, ps) } } @@ -618,7 +661,7 @@ impl<'a> BorrowInfState<'a> { id: j, parameters: ys, remainder: v, - continuation: b, + body: b, } => { let old = self.param_set.clone(); self.update_param_set(ys); @@ -641,6 +684,7 @@ impl<'a> BorrowInfState<'a> { layout: _, pass, fail, + exception_id: _, } => { self.collect_stmt(pass); self.collect_stmt(fail); @@ -672,13 +716,13 @@ impl<'a> BorrowInfState<'a> { } Refcounting(_, _) => unreachable!("these have not been introduced yet"), - Ret(_) | RuntimeError(_) | Rethrow => { + Ret(_) | RuntimeError(_) | Resume(_) => { // these are terminal, do nothing } } } - fn collect_proc(&mut self, proc: &Proc<'a>, layout: Layout<'a>) { + fn collect_proc(&mut self, proc: &Proc<'a>, layout: ProcLayout<'a>) { let old = self.param_set.clone(); let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice(); @@ -720,7 +764,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { match op { ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]), ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), - ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListConcat => arena.alloc_slice_copy(&[owned, owned]), StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), @@ -748,6 +791,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { // List.append should own its first argument ListAppend => arena.alloc_slice_copy(&[owned, owned]), ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]), + ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]), diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index 3aa4a7f22d..ef6a508c54 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -1,6 +1,6 @@ use crate::exhaustive::{Ctor, RenderAs, TagId, Union}; use crate::ir::{ - BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, Wrapped, + BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, }; use crate::layout::{Builtin, Layout, LayoutCache, UnionLayout}; use roc_collections::all::{MutMap, MutSet}; @@ -17,12 +17,13 @@ const RECORD_TAG_NAME: &str = "#Record"; /// some normal branches and gives out a decision tree that has "labels" at all /// the leafs and a dictionary that maps these "labels" to the code that should /// run. -pub fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> DecisionTree<'a> { +fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> DecisionTree<'a> { let formatted = raw_branches .into_iter() .map(|(guard, pattern, index)| Branch { goal: index, - patterns: vec![(Vec::new(), guard, pattern)], + guard, + patterns: vec![(Vec::new(), pattern)], }) .collect(); @@ -33,9 +34,8 @@ pub fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> Decision pub enum Guard<'a> { NoGuard, Guard { - /// Symbol that stores a boolean - /// when true this branch is picked, otherwise skipped - symbol: Symbol, + /// pattern + pattern: Pattern<'a>, /// after assigning to symbol, the stmt jumps to this label id: JoinPointId, stmt: Stmt<'a>, @@ -49,17 +49,35 @@ impl<'a> Guard<'a> { } #[derive(Clone, Debug, PartialEq)] -pub enum DecisionTree<'a> { +enum DecisionTree<'a> { Match(Label), Decision { path: Vec, - edges: Vec<(Test<'a>, DecisionTree<'a>)>, + edges: Vec<(GuardedTest<'a>, DecisionTree<'a>)>, default: Option>>, }, } #[derive(Clone, Debug, PartialEq)] -pub enum Test<'a> { +enum GuardedTest<'a> { + // e.g. `_ if True -> ...` + GuardedNoTest { + /// pattern + pattern: Pattern<'a>, + /// after assigning to symbol, the stmt jumps to this label + id: JoinPointId, + /// body + stmt: Stmt<'a>, + }, + TestNotGuarded { + test: Test<'a>, + }, + Placeholder, +} + +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::enum_variant_names)] +enum Test<'a> { IsCtor { tag_id: u8, tag_name: TagName, @@ -75,16 +93,6 @@ pub enum Test<'a> { tag_id: u8, num_alts: usize, }, - // A pattern that always succeeds (like `_`) can still have a guard - Guarded { - opt_test: Option>>, - /// Symbol that stores a boolean - /// when true this branch is picked, otherwise skipped - symbol: Symbol, - /// after assigning to symbol, the stmt jumps to this label - id: JoinPointId, - stmt: Stmt<'a>, - }, } use std::hash::{Hash, Hasher}; impl<'a> Hash for Test<'a> { @@ -118,15 +126,23 @@ impl<'a> Hash for Test<'a> { tag_id.hash(state); num_alts.hash(state); } - Guarded { opt_test: None, .. } => { - state.write_u8(6); + } + } +} + +impl<'a> Hash for GuardedTest<'a> { + fn hash(&self, state: &mut H) { + match self { + GuardedTest::GuardedNoTest { id, .. } => { + state.write_u8(1); + id.hash(state); } - Guarded { - opt_test: Some(nested), - .. - } => { - state.write_u8(7); - nested.hash(state); + GuardedTest::TestNotGuarded { test } => { + state.write_u8(0); + test.hash(state); + } + GuardedTest::Placeholder => { + state.write_u8(2); } } } @@ -137,59 +153,156 @@ impl<'a> Hash for Test<'a> { #[derive(Clone, Debug, PartialEq)] struct Branch<'a> { goal: Label, - patterns: Vec<(Vec, Guard<'a>, Pattern<'a>)>, + guard: Guard<'a>, + patterns: Vec<(Vec, Pattern<'a>)>, } fn to_decision_tree(raw_branches: Vec) -> DecisionTree { let branches: Vec<_> = raw_branches.into_iter().map(flatten_patterns).collect(); + debug_assert!(!branches.is_empty()); + match check_for_match(&branches) { - Some(goal) => DecisionTree::Match(goal), - None => { + Match::Exact(goal) => DecisionTree::Match(goal), + + Match::GuardOnly => { + // the first branch has no more tests to do, but it has an if-guard + + let mut branches = branches; + let first = branches.remove(0); + + match first.guard { + Guard::NoGuard => unreachable!(), + + Guard::Guard { id, stmt, pattern } => { + let guarded_test = GuardedTest::GuardedNoTest { id, stmt, pattern }; + + // the guard test does not have a path + let path = vec![]; + + // we expect none of the patterns need tests, those decisions should have been made already + debug_assert!(first + .patterns + .iter() + .all(|(_, pattern)| !needs_tests(pattern))); + + let default = if branches.is_empty() { + None + } else { + Some(Box::new(to_decision_tree(branches))) + }; + + DecisionTree::Decision { + path, + edges: vec![(guarded_test, DecisionTree::Match(first.goal))], + default, + } + } + } + } + + Match::None => { // must clone here to release the borrow on `branches` let path = pick_path(&branches).clone(); + + let bs = branches.clone(); let (edges, fallback) = gather_edges(branches, &path); let mut decision_edges: Vec<_> = edges .into_iter() - .map(|(a, b)| (a, to_decision_tree(b))) + .map(|(test, branches)| { + if bs == branches { + panic!(); + } else { + (test, to_decision_tree(branches)) + } + }) .collect(); - match (decision_edges.split_last_mut(), fallback.split_last()) { - (Some(((_tag, decision_tree), rest)), None) if rest.is_empty() => { - // TODO remove clone - decision_tree.clone() + match (decision_edges.as_slice(), fallback.as_slice()) { + ([(_test, _decision_tree)], []) => { + // only one test with no fallback: we will always enter this branch + + // get the `_decision_tree` without cloning + decision_edges.pop().unwrap().1 } - (_, None) => DecisionTree::Decision { - path: path.clone(), - edges: decision_edges, - default: None, - }, - (None, Some(_)) => to_decision_tree(fallback), - _ => DecisionTree::Decision { - path: path.clone(), - edges: decision_edges, - default: Some(Box::new(to_decision_tree(fallback))), - }, + (_, []) => break_out_guard(path, decision_edges, None), + ([], _) => { + // should be guaranteed by the patterns + debug_assert!(!fallback.is_empty()); + to_decision_tree(fallback) + } + (_, _) => break_out_guard( + path, + decision_edges, + Some(Box::new(to_decision_tree(fallback))), + ), } } } } -fn is_complete(tests: &[Test]) -> bool { +/// Give a guard it's own Decision +fn break_out_guard<'a>( + path: Vec, + mut edges: Vec<(GuardedTest<'a>, DecisionTree<'a>)>, + default: Option>>, +) -> DecisionTree<'a> { + match edges + .iter() + .position(|(t, _)| matches!(t, GuardedTest::Placeholder)) + { + None => DecisionTree::Decision { + path, + edges, + default, + }, + Some(index) => { + let (a, b) = edges.split_at_mut(index + 1); + + let new_default = break_out_guard(path.clone(), b.to_vec(), default); + + let mut left = a.to_vec(); + let guard = left.pop().unwrap(); + + let help = DecisionTree::Decision { + path: path.clone(), + edges: vec![guard], + default: Some(Box::new(new_default)), + }; + + DecisionTree::Decision { + path, + edges: left, + default: Some(Box::new(help)), + } + } + } +} + +fn guarded_tests_are_complete(tests: &[GuardedTest]) -> bool { let length = tests.len(); debug_assert!(length > 0); - match tests.last() { - None => unreachable!("should never happen"), - Some(v) => match v { - Test::IsCtor { union, .. } => length == union.alternatives.len(), - Test::IsByte { num_alts, .. } => length == *num_alts, - Test::IsBit(_) => length == 2, - Test::IsInt(_) => false, - Test::IsFloat(_) => false, - Test::IsStr(_) => false, - Test::Guarded { .. } => false, - }, + + let no_guard = tests + .iter() + .all(|t| matches!(t, GuardedTest::TestNotGuarded { .. })); + + match tests.last().unwrap() { + GuardedTest::Placeholder => false, + GuardedTest::GuardedNoTest { .. } => false, + GuardedTest::TestNotGuarded { test } => no_guard && tests_are_complete_help(test, length), + } +} + +fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool { + match last_test { + Test::IsCtor { union, .. } => number_of_tests == union.alternatives.len(), + Test::IsByte { num_alts, .. } => number_of_tests == *num_alts, + Test::IsBit(_) => number_of_tests == 2, + Test::IsInt(_) => false, + Test::IsFloat(_) => false, + Test::IsStr(_) => false, } } @@ -201,29 +314,23 @@ fn flatten_patterns(branch: Branch) -> Branch { } Branch { - goal: branch.goal, patterns: result, + ..branch } } fn flatten<'a>( - path_pattern: (Vec, Guard<'a>, Pattern<'a>), - path_patterns: &mut Vec<(Vec, Guard<'a>, Pattern<'a>)>, + path_pattern: (Vec, Pattern<'a>), + path_patterns: &mut Vec<(Vec, Pattern<'a>)>, ) { - match path_pattern.2 { + match path_pattern.1 { Pattern::AppliedTag { union, arguments, tag_id, tag_name, layout, - } if union.alternatives.len() == 1 - && !matches!( - layout, - Layout::Union(UnionLayout::NullableWrapped { .. }) - | Layout::Union(UnionLayout::NullableUnwrapped { .. }) - ) => - { + } if union.alternatives.len() == 1 && !layout.is_nullable() => { // TODO ^ do we need to check that guard.is_none() here? let path = path_pattern.0; @@ -233,7 +340,6 @@ fn flatten<'a>( // NOTE here elm will unbox, but we don't use that path_patterns.push(( path, - path_pattern.1.clone(), Pattern::AppliedTag { union, arguments, @@ -250,15 +356,7 @@ fn flatten<'a>( tag_id, }); - flatten( - ( - new_path, - // same guard here? - path_pattern.1.clone(), - arg_pattern.clone(), - ), - path_patterns, - ); + flatten((new_path, arg_pattern.clone()), path_patterns); } } } @@ -275,28 +373,40 @@ fn flatten<'a>( /// path. If that is the case we give the resulting label and a mapping from free /// variables to "how to get their value". So a pattern like (Just (x,_)) will give /// us something like ("x" => value.0.0) -fn check_for_match(branches: &[Branch]) -> Option