diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24bc7be2c..a6432ee95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,128 +3,128 @@ name: CI on: push: branches: - - main + - main paths-ignore: - - '*.md' + - "*.md" pull_request: paths-ignore: - - '*.md' + - "*.md" env: - CARGO_MAKE_TOOLCHAIN: nightly-2024-03-10 + CARGO_MAKE_TOOLCHAIN: nightly-2024-05-07 jobs: compiler: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} - override: true - - name: Cache Cargo - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} - - name: Install cargo-make - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-make - - name: Build - uses: actions-rs/cargo@v1 - with: - command: make - args: build - - name: Test - uses: actions-rs/cargo@v1 - with: - command: make - args: test + - uses: actions/checkout@v2 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} + override: true + - name: Cache Cargo + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} + - name: Install cargo-make + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-make + - name: Build + uses: actions-rs/cargo@v1 + with: + command: make + args: build + - name: Test + uses: actions-rs/cargo@v1 + with: + command: make + args: test cargo_miden_test_clean_env: # Run cargo-miden test in the clean env to simulate user's first run. # To test the installation of the required dependencies (e.g. rust-src, wasm target, etc.) - name: cargo-miden tests + name: cargo-miden tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} - override: true - - name: Cache Cargo - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: -p cargo-miden --test integration + - uses: actions/checkout@v2 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} + override: true + - name: Cache Cargo + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + args: -p cargo-miden --test integration clippy: name: clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} - override: true - - name: Cache Cargo - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} - - name: Install cargo-make - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-make - - name: Test - uses: actions-rs/cargo@v1 - with: - command: make - args: clippy + - uses: actions/checkout@v2 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} + override: true + - name: Cache Cargo + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} + - name: Install cargo-make + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-make + - name: Test + uses: actions-rs/cargo@v1 + with: + command: make + args: clippy rustfmt: name: rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} - override: true - - name: Cache Cargo - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} - - name: Install cargo-make - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-make - - name: rustfmt - uses: actions-rs/cargo@v1 - with: - command: make - args: check-format + - uses: actions/checkout@v2 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.CARGO_MAKE_TOOLCHAIN }} + override: true + - name: Cache Cargo + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ github.workflow }}-${{ github.job }}-toolchain-${{ env.CARGO_MAKE_TOOLCHAIN }} + - name: Install cargo-make + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-make + - name: rustfmt + uses: actions-rs/cargo@v1 + with: + command: make + args: check-format diff --git a/Cargo.lock b/Cargo.lock index aac82931a..aab98ea59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,11 +14,27 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "gimli", + "gimli 0.29.0", +] + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "cpp_demangle", + "fallible-iterator", + "gimli 0.31.0", + "memmap2", + "object", + "rustc-demangle", + "smallvec", + "typed-arena", ] [[package]] @@ -29,14 +45,13 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] @@ -66,18 +81,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "aligned" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a21b9440a626c7fc8573a9e3d3a06b75c7c97754c2949bc7857b90353ca655" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" dependencies = [ "as-slice", ] @@ -99,47 +114,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -147,9 +163,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "anymap2" @@ -159,9 +175,9 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -199,12 +215,11 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -212,15 +227,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.8.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.2.0", + "fastrand 2.1.0", + "futures-lite 2.3.0", "slab", ] @@ -258,18 +272,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "parking", - "polling 3.3.2", - "rustix 0.38.32", + "polling 3.7.2", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -286,11 +300,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.3.1", "event-listener-strategy", "pin-project-lite", ] @@ -308,54 +322,54 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" dependencies = [ - "async-io 2.3.0", - "async-lock 2.8.0", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -377,9 +391,9 @@ dependencies = [ [[package]] name = "auth-git2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e7771d4ab6635cbd685ce8db215b29c78a468098126de77c57f3b2e6eb3757" +checksum = "e51bd0e4592409df8631ca807716dc1e5caafae5d01ce0157c966c71c7e49c3c" dependencies = [ "dirs", "git2", @@ -388,17 +402,17 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ - "addr2line", + "addr2line 0.22.0", "cc", "cfg-if", "libc", @@ -434,6 +448,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -469,15 +489,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210" dependencies = [ "arrayref", "arrayvec", @@ -496,53 +516,43 @@ dependencies = [ ] [[package]] -name = "block-modes" -version = "0.8.1" +name = "block-padding" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "block-padding", - "cipher", + "generic-array", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock 3.3.0", "async-task", - "fastrand 2.0.1", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] name = "bstr" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.7", "serde", ] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -552,15 +562,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -579,7 +589,7 @@ dependencies = [ "clap", "futures", "heck 0.4.1", - "indexmap 2.1.0", + "indexmap 2.2.6", "libc", "log", "p256", @@ -587,12 +597,12 @@ dependencies = [ "pretty_env_logger", "rand_core", "rpassword", - "semver 1.0.21", + "semver 1.0.23", "serde", "serde_json", "tokio", "tokio-util", - "toml_edit 0.21.0", + "toml_edit 0.21.1", "url", "warg-client", "warg-crypto", @@ -614,15 +624,15 @@ dependencies = [ "anyhow", "clap", "futures", - "indexmap 2.1.0", + "indexmap 2.2.6", "keyring", "libc", "log", "owo-colors", - "semver 1.0.21", + "semver 1.0.23", "serde", "tokio", - "toml_edit 0.21.0", + "toml_edit 0.21.1", "unicode-width", "url", "warg-client", @@ -635,14 +645,14 @@ dependencies = [ [[package]] name = "cargo-config2" -version = "0.1.18" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a283792e857b3ca787f26db6b8164d6ee61eca69b4fa5daa5fa51b24d59917" +checksum = "d83ce0be8bd1479e5de6202def660e6c7e27e4e0599bffa4fed05bd380ec2ede" dependencies = [ "home", "serde", "serde_derive", - "toml_edit 0.21.0", + "toml_edit 0.22.18", ] [[package]] @@ -656,14 +666,14 @@ dependencies = [ "clap", "console", "dialoguer", - "env_logger 0.11.2", + "env_logger 0.11.5", "fs-err", "git2", "gix-config", "heck 0.5.0", "home", "ignore", - "indexmap 2.1.0", + "indexmap 2.2.6", "indicatif", "liquid", "liquid-core", @@ -677,7 +687,7 @@ dependencies = [ "remove_dir_all", "rhai", "sanitize-filename", - "semver 1.0.21", + "semver 1.0.23", "serde", "tempfile", "thiserror", @@ -695,30 +705,29 @@ dependencies = [ "cargo-generate", "cargo_metadata", "clap", - "env_logger 0.11.2", + "env_logger 0.11.5", "log", - "miden-diagnostics", "midenc-compile", "midenc-session", "parse_arg", "path-absolutize", - "semver 1.0.21", + "semver 1.0.23", ] [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] [[package]] name = "cargo-util" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2d9a9a8d3e0b61b1110c49ab8f6ed7a76ce4f2b1d53ae48a83152d3d5e8f5b" +checksum = "14104698cb1694d43c2ff73492468ccf2bb0b047071a9838d999eeba9e984ffa" dependencies = [ "anyhow", "core-foundation", @@ -745,17 +754,26 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.21", + "semver 1.0.23", "serde", "serde_json", "thiserror", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" -version = "1.0.83" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", @@ -767,33 +785,40 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -801,33 +826,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "codespan" @@ -850,9 +875,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "concat-idents" @@ -861,14 +886,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -894,9 +919,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const-random" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ "const-random-macro", ] @@ -940,6 +965,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -964,6 +998,15 @@ version = "0.108.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eabb8d36b0ca8906bec93c78ea516741cac2d7e6b266fa7b0ffddcc09004990" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -985,9 +1028,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1028,9 +1071,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -1038,34 +1081,34 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.48", + "strsim", + "syn 2.0.72", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -1095,15 +1138,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -1181,9 +1224,9 @@ dependencies = [ [[package]] name = "dissimilar" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "doc-comment" @@ -1207,9 +1250,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -1233,9 +1276,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -1248,18 +1291,18 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "enumflags2" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", "serde", @@ -1267,20 +1310,20 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", "regex", @@ -1301,9 +1344,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "anstream", "anstyle", @@ -1320,9 +1363,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1347,9 +1390,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -1358,19 +1401,19 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.3.1", "pin-project-lite", ] [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -1399,9 +1442,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "ff" @@ -1421,7 +1464,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -1431,6 +1474,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flurry" version = "0.4.0" @@ -1484,16 +1537,16 @@ dependencies = [ [[package]] name = "fs_at" -version = "0.1.10" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982f82cc75107eef84f417ad6c53ae89bf65b561937ca4a3b3b0fd04d0aa2425" +checksum = "14af6c9694ea25db25baa2a1788703b9e7c6648dcaeeebeb98f7561b5384c036" dependencies = [ "aligned", "cfg-if", "cvt", "libc", - "nix", - "windows-sys 0.48.0", + "nix 0.29.0", + "windows-sys 0.52.0", ] [[package]] @@ -1561,11 +1614,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-core", "futures-io", "parking", @@ -1580,7 +1633,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -1613,6 +1666,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "979f00864edc7516466d6b3157706e06c032f22715700ddd878228a91d02bc56" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1626,9 +1692,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1637,9 +1703,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "gimli" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" dependencies = [ "fallible-iterator", "stable_deref_trait", @@ -1647,11 +1719,11 @@ dependencies = [ [[package]] name = "git2" -version = "0.18.1" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" +checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", "libgit2-sys", "log", @@ -1662,23 +1734,23 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.31.1" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3a3bde455ad2ee8ba8a195745241ce0b770a8a26faae59fcf409d01b28c46" +checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2" dependencies = [ "bstr", "gix-date", "gix-utils", "itoa", "thiserror", - "winnow 0.6.5", + "winnow 0.6.16", ] [[package]] name = "gix-config" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62129c75e4b6229fe15fb9838cdc00c655e87105b651e4edd7c183fc5288b5d1" +checksum = "7580e05996e893347ad04e1eaceb92e1c0e6a3ffe517171af99bf6b6df0ca6e5" dependencies = [ "bstr", "gix-config-value", @@ -1692,16 +1764,16 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.6.5", + "winnow 0.6.16", ] [[package]] name = "gix-config-value" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd06203b1a9b33a78c88252a625031b094d9e1b647260070c25b09910c0a804" +checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "bstr", "gix-path", "libc", @@ -1710,9 +1782,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180b130a4a41870edfbd36ce4169c7090bca70e195da783dea088dd973daa59c" +checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" dependencies = [ "bstr", "itoa", @@ -1722,9 +1794,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4254037d20a247a0367aa79333750146a369719f0c6617fec4f5752cc62b37" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" dependencies = [ "gix-hash", "gix-trace", @@ -1737,9 +1809,9 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634b8a743b0aae03c1a74ee0ea24e8c5136895efac64ce52b3ea106e1c6f0613" +checksum = "e2184c40e7910529677831c8b481acf788ffd92427ed21fad65b6aa637e631b8" dependencies = [ "gix-features", "gix-utils", @@ -1747,11 +1819,11 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.16.2" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "682bdc43cb3c00dbedfcc366de2a849b582efd8d886215dbad2ea662ec156bb5" +checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "bstr", "gix-features", "gix-path", @@ -1780,9 +1852,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.42.1" +version = "0.42.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4f8efae72030df1c4a81d02dbe2348e748d9b9a11e108ed6efbd846326e051" +checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386" dependencies = [ "bstr", "gix-actor", @@ -1794,14 +1866,14 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.6.5", + "winnow 0.6.16", ] [[package]] name = "gix-path" -version = "0.10.7" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23623cf0f475691a6d943f898c4d0b89f5c1a2a64d0f92bce0e0322ee6528783" +checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" dependencies = [ "bstr", "gix-trace", @@ -1829,16 +1901,16 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.6.5", + "winnow 0.6.16", ] [[package]] name = "gix-sec" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddc27984a643b20dd03e97790555804f98cf07404e0e552c0ad8133266a79a1" +checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "gix-path", "libc", "windows-sys 0.52.0", @@ -1859,25 +1931,25 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" +checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" [[package]] name = "gix-utils" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.1.0", "unicode-normalization", ] [[package]] name = "gix-validate" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e39fc6e06044985eac19dd34d474909e517307582e462b2eb4c8fa51b6241545" +checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" dependencies = [ "bstr", "thiserror", @@ -1898,8 +1970,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -1915,9 +1987,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1925,7 +1997,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1940,9 +2012,13 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "serde", +] [[package]] name = "heck" @@ -1970,9 +2046,15 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -2009,9 +2091,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -2031,9 +2113,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2043,9 +2125,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "human-panic" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c5d0e9120f6bca6120d142c7ede1ba376dd6bf276d69dd3dbe6cbeb7824179" +checksum = "1c5a08ed290eac04006e21e63d32e90086b6182c7cd0452d10f4264def1fec9a" dependencies = [ "anstream", "anstyle", @@ -2065,9 +2147,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2080,7 +2162,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -2102,16 +2184,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2155,7 +2237,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.7", "same-file", "walkdir", "winapi-util", @@ -2180,20 +2262,20 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -2202,11 +2284,21 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -2217,14 +2309,14 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b694dc9f70c3bda874626d2aed13b780f137aab435f4e9814121955cf706122e" dependencies = [ - "memoffset 0.9.0", + "memoffset 0.9.1", ] [[package]] name = "inventory" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "io-lifetimes" @@ -2232,7 +2324,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -2245,12 +2337,12 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.4", - "rustix 0.38.32", + "hermit-abi 0.3.9", + "libc", "windows-sys 0.52.0", ] @@ -2260,6 +2352,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2278,26 +2376,35 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2313,9 +2420,9 @@ dependencies = [ [[package]] name = "keyring" -version = "2.3.1" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b479dcf9eae65481044dfda57af7fe2da6c1401180360f6898801fe9ed4db9" +checksum = "363387f0019d714aa60cc30ab4fe501a747f4c08fc58f069dd14be971bd495a0" dependencies = [ "byteorder", "lazy_static", @@ -2327,9 +2434,9 @@ dependencies = [ [[package]] name = "kstring" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" dependencies = [ "serde", "static_assertions", @@ -2337,37 +2444,39 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", - "diff", "ena", - "is-terminal", - "itertools 0.10.5", + "itertools 0.11.0", "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.7.5", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", "unicode-xid", + "walkdir", ] [[package]] name = "lalrpop-util" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.7", +] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" @@ -2377,15 +2486,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libgit2-sys" -version = "0.16.1+1.7.1" +version = "0.16.2+1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" +checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" dependencies = [ "cc", "libc", @@ -2403,13 +2512,12 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", - "redox_syscall", ] [[package]] @@ -2428,9 +2536,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.14" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -2444,7 +2552,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", ] @@ -2456,15 +2564,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "liquid" -version = "0.26.4" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f68ae1011499ae2ef879f631891f21c78e309755f4a5e483c4a8f12e10b609" +checksum = "4e9338405fdbc0bce9b01695b2a2ef6b20eca5363f385d47bce48ddf8323cc25" dependencies = [ "doc-comment", "liquid-core", @@ -2475,12 +2583,12 @@ dependencies = [ [[package]] name = "liquid-core" -version = "0.26.4" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e0724dfcaad5cfb7965ea0f178ca0870b8d7315178f4a7179f5696f7f04d5f" +checksum = "feb8fed70857010ed9016ed2ce5a7f34e7cc51d5d7255c9c9dc2e3243e490b42" dependencies = [ "anymap2", - "itertools 0.10.5", + "itertools 0.13.0", "kstring", "liquid-derive", "num-traits", @@ -2493,22 +2601,22 @@ dependencies = [ [[package]] name = "liquid-derive" -version = "0.26.4" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" +checksum = "77a5aa659a76b649f0d639ef0b9c067a9499c42a9d7f3e7832e279f791704966" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "liquid-lib" -version = "0.26.4" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a17e273a6fb1fb6268f7a5867ddfd0bd4683c7e19b51084f3d567fad4348c0" +checksum = "ee1794b5605e9f8864a8a4f41aa97976b42512cc81093f8c885d29fb94c6c556" dependencies = [ - "itertools 0.10.5", + "itertools 0.13.0", "liquid-core", "once_cell", "percent-encoding", @@ -2519,9 +2627,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2529,9 +2637,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "logos" @@ -2553,7 +2661,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -2565,11 +2673,33 @@ dependencies = [ "logos-codegen", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2591,27 +2721,28 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "miden-air" -version = "0.9.2" -source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=2da11ad0a975d2e5d6a2582871f0c89b820b3ffa#2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" +version = "0.10.0" +source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88#b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" dependencies = [ "miden-core", + "thiserror", "winter-air", "winter-prover", ] [[package]] name = "miden-assembly" -version = "0.9.2" -source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=2da11ad0a975d2e5d6a2582871f0c89b820b3ffa#2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" +version = "0.10.0" +source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88#b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" dependencies = [ "aho-corasick", "lalrpop", @@ -2627,11 +2758,18 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.9.1" -source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=2da11ad0a975d2e5d6a2582871f0c89b820b3ffa#2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" +version = "0.10.0" +source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88#b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" dependencies = [ + "lock_api", + "loom", + "memchr", "miden-crypto", "miden-formatting", + "miette 7.1.0", + "num-derive", + "num-traits", + "parking_lot", "thiserror", "winter-math", "winter-utils", @@ -2701,7 +2839,7 @@ dependencies = [ "cargo_metadata", "concat-idents", "derive_more", - "env_logger 0.11.2", + "env_logger 0.11.5", "expect-test", "filetime", "glob", @@ -2714,6 +2852,7 @@ dependencies = [ "miden-stdlib", "midenc-codegen-masm", "midenc-compile", + "midenc-driver", "midenc-frontend-wasm", "midenc-hir", "midenc-hir-transform", @@ -2741,8 +2880,8 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.9.2" -source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=2da11ad0a975d2e5d6a2582871f0c89b820b3ffa#2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" +version = "0.10.0" +source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88#b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" dependencies = [ "miden-air", "miden-core", @@ -2752,8 +2891,8 @@ dependencies = [ [[package]] name = "miden-stdlib" -version = "0.9.2" -source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=2da11ad0a975d2e5d6a2582871f0c89b820b3ffa#2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" +version = "0.10.0" +source = "git+https://github.com/0xPolygonMiden/miden-vm?rev=b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88#b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" dependencies = [ "miden-assembly", ] @@ -2762,8 +2901,7 @@ dependencies = [ name = "midenc" version = "0.0.0" dependencies = [ - "anyhow", - "env_logger 0.11.2", + "env_logger 0.11.5", "human-panic", "midenc-driver", ] @@ -2774,13 +2912,13 @@ version = "0.0.0" dependencies = [ "anyhow", "cranelift-entity", - "env_logger 0.11.2", + "env_logger 0.11.5", "intrusive-collections", "inventory", "log", "miden-assembly", "miden-core", - "miden-diagnostics", + "miden-processor", "miden-stdlib", "midenc-hir", "midenc-hir-analysis", @@ -2798,7 +2936,6 @@ dependencies = [ name = "midenc-compile" version = "0.0.0" dependencies = [ - "anyhow", "clap", "inventory", "log", @@ -2810,7 +2947,6 @@ dependencies = [ "midenc-hir-analysis", "midenc-hir-transform", "midenc-session", - "rustc-hash", "thiserror", "wat", ] @@ -2819,9 +2955,7 @@ dependencies = [ name = "midenc-driver" version = "0.0.0" dependencies = [ - "anyhow", "clap", - "miden-diagnostics", "midenc-compile", "midenc-hir", "midenc-session", @@ -2832,21 +2966,22 @@ dependencies = [ name = "midenc-frontend-wasm" version = "0.0.0" dependencies = [ + "addr2line 0.24.1", "anyhow", "derive_more", "expect-test", - "gimli", - "indexmap 2.1.0", + "gimli 0.31.0", + "indexmap 2.2.6", "log", "miden-core", - "miden-diagnostics", "miden-integration-tests", "midenc-hir", "midenc-hir-type", + "midenc-session", "rustc-hash", "smallvec", "thiserror", - "wasmparser 0.118.1", + "wasmparser 0.214.0", "wat", ] @@ -2858,14 +2993,14 @@ dependencies = [ "cranelift-entity", "derive_more", "either", - "indexmap 2.1.0", + "indexmap 2.2.6", "intrusive-collections", "inventory", "lalrpop", "lalrpop-util", + "log", "miden-assembly", "miden-core", - "miden-diagnostics", "miden-parsing", "midenc-hir-macros", "midenc-hir-symbol", @@ -2892,7 +3027,6 @@ dependencies = [ "cranelift-entity", "intrusive-collections", "inventory", - "miden-diagnostics", "midenc-hir", "midenc-session", "pretty_assertions", @@ -2908,7 +3042,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -2949,9 +3083,12 @@ dependencies = [ "atty", "clap", "inventory", + "miden-assembly", + "miden-core", "miden-diagnostics", + "miden-stdlib", + "midenc-hir-macros", "midenc-hir-symbol", - "rustc-hash", "thiserror", ] @@ -2989,7 +3126,7 @@ dependencies = [ "supports-color", "supports-hyperlinks", "supports-unicode", - "syn 2.0.48", + "syn 2.0.72", "terminal_size", "textwrap", "thiserror", @@ -3005,7 +3142,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -3015,7 +3152,7 @@ source = "git+https://github.com/bitwalker/miette?branch=no-std#e918fbde6c9853fe dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -3026,22 +3163,23 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3070,11 +3208,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -3088,9 +3225,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" @@ -3104,20 +3241,42 @@ dependencies = [ "memoffset 0.7.1", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "normpath" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", ] [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -3129,39 +3288,54 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -3170,11 +3344,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -3182,9 +3355,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -3196,15 +3369,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -3217,11 +3390,13 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ + "flate2", "memchr", + "ruzstd", ] [[package]] @@ -3230,19 +3405,13 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -3259,7 +3428,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -3270,9 +3439,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -3298,15 +3467,21 @@ dependencies = [ [[package]] name = "os_info" -version = "3.7.0" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "winapi", + "windows-sys 0.52.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.0.0" @@ -3333,9 +3508,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3343,15 +3518,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -3362,9 +3537,9 @@ checksum = "14248cc8eced350e20122a291613de29e4fa129ba2731818c4cdbb44fccd3e55" [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-absolutize" @@ -3444,9 +3619,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -3455,9 +3630,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -3465,22 +3640,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "pest_meta" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -3489,12 +3664,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.1.0", + "indexmap 2.2.6", ] [[package]] @@ -3508,9 +3683,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -3520,12 +3695,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-io", ] @@ -3541,9 +3716,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "polling" @@ -3563,23 +3738,24 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.2" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.32", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -3589,9 +3765,13 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +dependencies = [ + "zerocopy", + "zerocopy-derive", +] [[package]] name = "precomputed-hash" @@ -3650,9 +3830,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3665,19 +3845,19 @@ checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.2", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -3785,9 +3965,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -3840,11 +4020,20 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -3853,25 +4042,34 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", ] [[package]] @@ -3882,37 +4080,29 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "remove_dir_all" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" +checksum = "c914caef075f03e9d5c568e2e71b3d3cf17dc61a5481ff379bb744721be0a75a" dependencies = [ - "aligned", "cfg-if", "cvt", "fs_at", - "lazy_static", "libc", "normpath", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -3932,9 +4122,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -3965,7 +4157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6273372244d04a8a4b0bec080ea1e710403e88c5d9d83f9808b2bfa64f0982a" dependencies = [ "ahash 0.8.11", - "bitflags 2.4.2", + "bitflags 2.6.0", "instant", "num-traits", "once_cell", @@ -3977,13 +4169,13 @@ dependencies = [ [[package]] name = "rhai_codegen" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db7f8dc4c9d48183a17ce550574c42995252b82d267eaca3fcd1b979159856c" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -4009,9 +4201,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -4034,7 +4226,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.21", + "semver 1.0.23", ] [[package]] @@ -4053,22 +4245,31 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -4082,11 +4283,21 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ruzstd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5022b253619b1ba797f243056276bed8ed1a73b0f5a7ce7225d524067644bf8f" +dependencies = [ + "byteorder", + "twox-hash", +] + [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -4116,6 +4327,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -4147,12 +4364,12 @@ dependencies = [ [[package]] name = "secret-service" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" +checksum = "b5204d39df37f06d1944935232fd2dfe05008def7ca599bf28c0800366c8a8f9" dependencies = [ "aes", - "block-modes", + "cbc", "futures-util", "generic-array", "hkdf", @@ -4166,11 +4383,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4179,9 +4396,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -4208,9 +4425,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -4223,51 +4440,52 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -4286,16 +4504,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.5.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58c3a1b3e418f61c25b2aeb43fc6c95eaa252b8cecdda67f401943e9e08d33f" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -4303,14 +4522,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.5.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2068b437a31fc68f25dd7edc296b078f04b45145c199d8eed9866e45f1ff274" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -4326,9 +4545,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -4351,6 +4570,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -4365,9 +4593,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -4399,9 +4627,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smartstring" @@ -4432,19 +4660,19 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "spdx" -version = "0.10.3" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b" +checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" dependencies = [ "smallvec", ] @@ -4499,12 +4727,6 @@ dependencies = [ "vte", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -4513,9 +4735,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "supports-color" @@ -4551,15 +4773,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4588,8 +4816,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "rustix 0.38.32", + "fastrand 2.1.0", + "rustix 0.38.34", "windows-sys 0.52.0", ] @@ -4629,7 +4857,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.48.0", ] @@ -4665,18 +4893,29 @@ source = "git+https://github.com/bitwalker/thiserror?branch=no-std#444c920234c68 dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", ] [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -4692,10 +4931,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -4710,9 +4950,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -4725,32 +4965,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -4765,36 +5004,35 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.8" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.18", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4805,22 +5043,35 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "toml_datetime", - "winnow 0.5.34", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.5.34", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.16", ] [[package]] @@ -4848,7 +5099,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -4858,6 +5109,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -4868,13 +5149,12 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.90" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa6f84ec205ebf87fb7a0abdbcd1467fa5af0e86878eb6d888b78ecbb10b6d5" +checksum = "b55265878356bdd85c9baa15859c87de93b2bf1f33acf752040a561e4a228f62" dependencies = [ "dissimilar", "glob", - "once_cell", "serde", "serde_derive", "serde_json", @@ -4882,6 +5162,16 @@ dependencies = [ "toml", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typed-arena" version = "2.0.2" @@ -4906,7 +5196,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.0", + "memoffset 0.9.1", "tempfile", "winapi", ] @@ -4943,24 +5233,24 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -4970,9 +5260,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -4982,19 +5272,25 @@ dependencies = [ [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -5003,9 +5299,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vte" @@ -5019,9 +5315,9 @@ dependencies = [ [[package]] name = "vte_generate_state_changes" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", @@ -5038,9 +5334,9 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" @@ -5158,11 +5454,11 @@ dependencies = [ "anyhow", "base64 0.21.7", "hex", - "indexmap 2.1.0", + "indexmap 2.2.6", "pbjson-types", "prost", "prost-types", - "semver 1.0.21", + "semver 1.0.23", "serde", "serde_with", "thiserror", @@ -5193,9 +5489,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5203,24 +5499,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -5230,9 +5526,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5240,22 +5536,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" @@ -5275,27 +5571,45 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.214.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff694f02a8d7a50b6922b197ae03883fbf18cdb2ae9fbee7b6148456f5f44041" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-metadata" -version = "0.10.15" +version = "0.10.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7" +checksum = "18ebaa7bd0f9e7a5e5dd29b9a998acf21c4abed74265524dd7e85934597bfb10" dependencies = [ "anyhow", - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.39.0", - "wasmparser 0.119.0", + "wasm-encoder 0.41.2", + "wasmparser 0.121.2", ] [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -5310,18 +5624,18 @@ version = "0.108.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5" dependencies = [ - "indexmap 2.1.0", - "semver 1.0.21", + "indexmap 2.2.6", + "semver 1.0.23", ] [[package]] name = "wasmparser" -version = "0.118.1" +version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" dependencies = [ - "indexmap 2.1.0", - "semver 1.0.21", + "indexmap 2.2.6", + "semver 1.0.23", ] [[package]] @@ -5330,47 +5644,73 @@ version = "0.119.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d" dependencies = [ - "bitflags 2.4.2", - "indexmap 2.1.0", - "semver 1.0.21", + "bitflags 2.6.0", + "indexmap 2.2.6", + "semver 1.0.23", +] + +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.6.0", + "indexmap 2.2.6", + "semver 1.0.23", +] + +[[package]] +name = "wasmparser" +version = "0.214.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.6.0", + "hashbrown 0.14.5", + "indexmap 2.2.6", + "semver 1.0.23", + "serde", ] [[package]] name = "wasmprinter" -version = "0.2.76" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac2a7745372074e5573e365e17100f5a26058740576313784ef03fb900ea8d2" +checksum = "60e73986a6b7fdfedb7c5bf9e7eb71135486507c8fbc4c0c42cffcb6532988b7" dependencies = [ "anyhow", - "wasmparser 0.119.0", + "wasmparser 0.121.2", ] [[package]] name = "wast" -version = "70.0.0" +version = "214.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee4bc54bbe1c6924160b9f75e374a1d07532e7580eb632c0ee6cdd109bb217e" +checksum = "694bcdb24c49c8709bd8713768b71301a11e823923eee355d530f1d8d0a7f8e9" dependencies = [ + "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.39.0", + "wasm-encoder 0.214.0", ] [[package]] name = "wat" -version = "1.0.83" +version = "1.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0dce8cdc288c717cf01e461a1e451a7b8445d53451123536ba576e423a101a" +checksum = "347249eb56773fa728df2656cfe3a8c19437ded61a922a0b5e0839d9790e278e" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -5385,7 +5725,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.34", ] [[package]] @@ -5397,7 +5737,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.48.0", ] @@ -5419,11 +5759,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -5432,22 +5772,77 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-core" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-targets 0.42.2", + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -5465,22 +5860,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] @@ -5500,25 +5880,20 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5527,15 +5902,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5545,15 +5914,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5563,15 +5926,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5581,15 +5944,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5599,15 +5956,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5617,15 +5968,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5635,24 +5980,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] @@ -5720,7 +6065,7 @@ checksum = "7ce0f4161cdde50de809b3869c1cb083a09e92e949428ea28f04c0d64045875c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -5775,15 +6120,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" dependencies = [ "anyhow", - "bitflags 2.4.2", - "indexmap 2.1.0", + "bitflags 2.6.0", + "indexmap 2.2.6", "log", "serde", "serde_derive", "serde_json", "wasm-encoder 0.38.1", "wasm-metadata", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wit-parser", ] @@ -5794,8 +6139,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "429e3c06fba3a7566aab724ae3ffff3152ede5399d44789e7dd11f5421292859" dependencies = [ "anyhow", - "bitflags 2.4.2", - "indexmap 2.1.0", + "bitflags 2.6.0", + "indexmap 2.2.6", "log", "serde", "serde_derive", @@ -5808,15 +6153,15 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +checksum = "316b36a9f0005f5aa4b03c39bc3728d045df136f8c13a73b7db4510dec725e08" dependencies = [ "anyhow", "id-arena", - "indexmap 2.1.0", + "indexmap 2.2.6", "log", - "semver 1.0.21", + "semver 1.0.23", "serde", "serde_derive", "serde_json", @@ -5825,12 +6170,12 @@ dependencies = [ [[package]] name = "xdg-home" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" dependencies = [ - "nix", - "winapi", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -5841,9 +6186,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zbus" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ "async-broadcast", "async-executor", @@ -5863,7 +6208,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.26.4", "once_cell", "ordered-stream", "rand", @@ -5882,9 +6227,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5896,9 +6241,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", @@ -5907,35 +6252,36 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zvariant" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" dependencies = [ "byteorder", "enumflags2", @@ -5947,9 +6293,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 3cf4994bf..693b3d075 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,13 @@ members = [ "midenc-session", "tools/*", "frontend-wasm", - "tests/rust-apps/*", "tests/integration", ] -exclude = ["tests/rust-apps-wasm", "cargo-ext/tests/data"] +exclude = [ + "tests/rust-apps/fib", + "tests/rust-apps-wasm", + "cargo-ext/tests/data", +] [workspace.package] version = "0.0.0" @@ -64,13 +67,11 @@ smallstr = { version = "0.3", features = ["union"] } thiserror = { version = "1.0", git = "https://github.com/bitwalker/thiserror", branch = "no-std" } toml = { version = "0.8", features = ["preserve_order"] } derive_more = "0.99" -indexmap = "2.1" -# 2da11ad0a975d2e5d6a2582871f0c89b820b3ffa is the latest commit in 'next' that includes -# the absolute paths support -miden-assembly = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" } -miden-core = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" } -miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" } -miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "2da11ad0a975d2e5d6a2582871f0c89b820b3ffa" } +indexmap = "2.2" +miden-assembly = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" } +miden-core = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" } +miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" } +miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm", rev = "b4e1f3abf84c0c982b3a0ff83bb7568d2ba7ee88" } midenc-codegen-masm = { path = "codegen/masm" } miden-diagnostics = "0.1" midenc-hir = { path = "hir" } diff --git a/Makefile.toml b/Makefile.toml index c5fdaf7b7..ae7b8b856 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -183,10 +183,12 @@ category = "Install" description = "Builds midenc and installs it globally via the cargo bin directory" command = "cargo" args = [ - "cargo", "install", "--path", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/midenc", + "--debug", + "--force", + "--bin", "midenc", ] @@ -219,7 +221,8 @@ category = "Test" description = "Runs tests written in Rust" command = "cargo" args = [ - "test", + "nextest", + "run", "@@remove-empty(CARGO_MAKE_CARGO_VERBOSE_FLAGS)", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )", "${@}", @@ -285,11 +288,11 @@ args = ["serve", "--open", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/docs"] description = "Runs clippy on the workspace" category = "Development" command = "cargo" -args = ["clippy" , "--all", "--", "-D", "clippy::all", "-D", "warnings"] +args = ["clippy", "--all", "--", "-D", "clippy::all", "-D", "warnings"] dependencies = ["install-clippy"] [tasks.install-clippy] category = "Development" description = "Installs cargo clippy plugin." command = "rustup" -args = ["component", "add", "clippy"] \ No newline at end of file +args = ["component", "add", "clippy"] diff --git a/codegen/masm/Cargo.toml b/codegen/masm/Cargo.toml index 9f9e281b7..f410b9373 100644 --- a/codegen/masm/Cargo.toml +++ b/codegen/masm/Cargo.toml @@ -19,7 +19,7 @@ inventory.workspace = true log.workspace = true miden-assembly.workspace = true miden-core.workspace = true -miden-diagnostics.workspace = true +miden-processor.workspace = true miden-stdlib.workspace = true midenc-hir.workspace = true midenc-hir-analysis.workspace = true diff --git a/codegen/masm/src/codegen/emit/binary.rs b/codegen/masm/src/codegen/emit/binary.rs index 87c0a5da3..eea74de6c 100644 --- a/codegen/masm/src/codegen/emit/binary.rs +++ b/codegen/masm/src/codegen/emit/binary.rs @@ -1,20 +1,20 @@ -use midenc_hir::{assert_matches, Felt, Immediate, Overflow, Type}; +use midenc_hir::{assert_matches, Felt, Immediate, Overflow, SourceSpan, Type}; use super::OpEmitter; use crate::masm::Op; impl<'a> OpEmitter<'a> { - pub fn eq(&mut self) { + pub fn eq(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected eq operands to be the same type"); match &ty { Type::I128 | Type::U128 => { - self.eq_i128(); + self.eq_i128(span); } Type::I64 | Type::U64 => { - self.eq_int64(); + self.eq_int64(span); } Type::Felt | Type::Ptr(_) @@ -25,47 +25,47 @@ impl<'a> OpEmitter<'a> { | Type::I8 | Type::U8 | Type::I1 => { - self.emit(Op::Eq); + self.emit(Op::Eq, span); } ty => unimplemented!("eq is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn eq_imm(&mut self, imm: Immediate) { + pub fn eq_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected eq operands to be the same type"); match &ty { Type::I128 | Type::U128 => { - self.push_immediate(imm); - self.eq_i128(); + self.push_immediate(imm, span); + self.eq_i128(span); } Type::I64 | Type::U64 => { - self.push_immediate(imm); - self.eq_int64(); + self.push_immediate(imm, span); + self.eq_int64(span); } Type::Felt | Type::Ptr(_) | Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::EqImm(imm.as_felt().unwrap())); + self.emit(Op::EqImm(imm.as_felt().unwrap()), span); } Type::I32 | Type::I16 | Type::I8 => { - self.emit(Op::EqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64))); + self.emit(Op::EqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64)), span); } ty => unimplemented!("eq is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn neq(&mut self) { + pub fn neq(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected neq operands to be the same type"); match &ty { Type::I128 | Type::U128 => { - self.neq_i128(); + self.neq_i128(span); } - Type::I64 | Type::U64 => self.neq_int64(), + Type::I64 | Type::U64 => self.neq_int64(span), Type::Felt | Type::Ptr(_) | Type::U32 @@ -75,276 +75,288 @@ impl<'a> OpEmitter<'a> { | Type::I8 | Type::U8 | Type::I1 => { - self.emit(Op::Neq); + self.emit(Op::Neq, span); } ty => unimplemented!("neq is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn neq_imm(&mut self, imm: Immediate) { + pub fn neq_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected neq operands to be the same type"); match &ty { Type::I128 | Type::U128 => { - self.push_immediate(imm); - self.neq_i128(); + self.push_immediate(imm, span); + self.neq_i128(span); } Type::I64 | Type::U64 => { - self.push_immediate(imm); - self.neq_int64() + self.push_immediate(imm, span); + self.neq_int64(span) } Type::Felt | Type::Ptr(_) | Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::NeqImm(imm.as_felt().unwrap())); + self.emit(Op::NeqImm(imm.as_felt().unwrap()), span); } Type::I32 | Type::I16 | Type::I8 => { - self.emit(Op::NeqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64))); + self.emit(Op::NeqImm(Felt::new(imm.as_i32().unwrap() as u32 as u64)), span); } ty => unimplemented!("neq is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn gt(&mut self) { + pub fn gt(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected gt operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Gt); + self.emit(Op::Gt, span); } Type::U64 => { - self.gt_u64(); + self.gt_u64(span); } Type::I64 => { - self.gt_i64(); + self.gt_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Gt); + self.emit(Op::U32Gt, span); } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gt".parse().unwrap())), + Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gt".parse().unwrap()), span), ty => unimplemented!("gt is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn gt_imm(&mut self, imm: Immediate) { + pub fn gt_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected gt operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::GtImm(imm.as_felt().unwrap())); + self.emit(Op::GtImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_u64(imm.as_u64().unwrap()); - self.gt_u64(); + self.push_u64(imm.as_u64().unwrap(), span); + self.gt_u64(span); } Type::I64 => { - self.push_i64(imm.as_i64().unwrap()); - self.gt_i64(); + self.push_i64(imm.as_i64().unwrap(), span); + self.gt_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gt]); + self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gt], span); } Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_gt".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm.as_i32().unwrap() as u32), + Op::Exec("intrinsics::i32::is_gt".parse().unwrap()), + ], + span, + ); } ty => unimplemented!("gt is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn gte(&mut self) { + pub fn gte(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected gte operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Gte); + self.emit(Op::Gte, span); } Type::U64 => { - self.gte_u64(); + self.gte_u64(span); } Type::I64 => { - self.gte_i64(); + self.gte_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Gte); + self.emit(Op::U32Gte, span); } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gte".parse().unwrap())), + Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_gte".parse().unwrap()), span), ty => unimplemented!("gte is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn gte_imm(&mut self, imm: Immediate) { + pub fn gte_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected gte operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::GteImm(imm.as_felt().unwrap())); + self.emit(Op::GteImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_u64(imm.as_u64().unwrap()); - self.gte_u64(); + self.push_u64(imm.as_u64().unwrap(), span); + self.gte_u64(span); } Type::I64 => { - self.push_i64(imm.as_i64().unwrap()); - self.gte_i64(); + self.push_i64(imm.as_i64().unwrap(), span); + self.gte_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gte]); + self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Gte], span); } Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_gte".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm.as_i32().unwrap() as u32), + Op::Exec("intrinsics::i32::is_gte".parse().unwrap()), + ], + span, + ); } ty => unimplemented!("gte is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn lt(&mut self) { + pub fn lt(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected lt operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Lt); + self.emit(Op::Lt, span); } Type::U64 => { - self.lt_u64(); + self.lt_u64(span); } Type::I64 => { - self.lt_i64(); + self.lt_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Lt); + self.emit(Op::U32Lt, span); } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lt".parse().unwrap())), + Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lt".parse().unwrap()), span), ty => unimplemented!("lt is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn lt_imm(&mut self, imm: Immediate) { + pub fn lt_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected lt operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::LtImm(imm.as_felt().unwrap())); + self.emit(Op::LtImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_u64(imm.as_u64().unwrap()); - self.lt_u64(); + self.push_u64(imm.as_u64().unwrap(), span); + self.lt_u64(span); } Type::I64 => { - self.push_i64(imm.as_i64().unwrap()); - self.lt_i64(); + self.push_i64(imm.as_i64().unwrap(), span); + self.lt_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lt]); + self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lt], span); } Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_lt".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm.as_i32().unwrap() as u32), + Op::Exec("intrinsics::i32::is_lt".parse().unwrap()), + ], + span, + ); } ty => unimplemented!("lt is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn lte(&mut self) { + pub fn lte(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected lte operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Lte); + self.emit(Op::Lte, span); } Type::U64 => { - self.lte_u64(); + self.lte_u64(span); } Type::I64 => { - self.lte_i64(); + self.lte_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit(Op::U32Lte); + self.emit(Op::U32Lte, span); } - Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lte".parse().unwrap())), + Type::I32 => self.emit(Op::Exec("intrinsics::i32::is_lte".parse().unwrap()), span), ty => unimplemented!("lte is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn lte_imm(&mut self, imm: Immediate) { + pub fn lte_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected lte operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::LteImm(imm.as_felt().unwrap())); + self.emit(Op::LteImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_u64(imm.as_u64().unwrap()); - self.lte_u64(); + self.push_u64(imm.as_u64().unwrap(), span); + self.lte_u64(span); } Type::I64 => { - self.push_i64(imm.as_i64().unwrap()); - self.lte_i64(); + self.push_i64(imm.as_i64().unwrap(), span); + self.lte_i64(span); } Type::U32 | Type::U16 | Type::U8 | Type::I1 => { - self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lte]); + self.emit_all(&[Op::PushU32(imm.as_u32().unwrap()), Op::U32Lte], span); } Type::I32 => { - self.emit_all(&[ - Op::PushU32(imm.as_i32().unwrap() as u32), - Op::Exec("intrinsics::i32::is_lte".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm.as_i32().unwrap() as u32), + Op::Exec("intrinsics::i32::is_lte".parse().unwrap()), + ], + span, + ); } ty => unimplemented!("lte is not yet implemented for {ty}"), } self.push(Type::I1); } - pub fn add(&mut self, overflow: Overflow) { + pub fn add(&mut self, overflow: Overflow, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected add operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Add); + self.emit(Op::Add, span); } Type::U64 => { - self.add_u64(overflow); + self.add_u64(overflow, span); } Type::I64 => { - self.add_i64(overflow); + self.add_i64(overflow, span); } Type::U32 => { - self.add_u32(overflow); + self.add_u32(overflow, span); } Type::I32 => { - self.add_i32(overflow); + self.add_i32(overflow, span); } ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.add_uint(ty.size_in_bits() as u32, overflow); + self.add_uint(ty.size_in_bits() as u32, overflow, span); } ty => unimplemented!("add is not yet implemented for {ty}"), } @@ -354,30 +366,30 @@ impl<'a> OpEmitter<'a> { } } - pub fn add_imm(&mut self, imm: Immediate, overflow: Overflow) { + pub fn add_imm(&mut self, imm: Immediate, overflow: Overflow, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected add operands to be the same type"); match &ty { - Type::Felt if imm == 1 => self.emit(Op::Incr), + Type::Felt if imm == 1 => self.emit(Op::Incr, span), Type::Felt => { - self.emit(Op::AddImm(imm.as_felt().unwrap())); + self.emit(Op::AddImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_immediate(imm); - self.add_u64(overflow); + self.push_immediate(imm, span); + self.add_u64(overflow, span); } Type::I64 => { - self.add_imm_i64(imm.as_i64().unwrap(), overflow); + self.add_imm_i64(imm.as_i64().unwrap(), overflow, span); } Type::U32 => { - self.add_imm_u32(imm.as_u32().unwrap(), overflow); + self.add_imm_u32(imm.as_u32().unwrap(), overflow, span); } Type::I32 => { - self.add_imm_i32(imm.as_i32().unwrap(), overflow); + self.add_imm_i32(imm.as_i32().unwrap(), overflow, span); } ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.add_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); + self.add_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow, span); } ty => unimplemented!("add is not yet implemented for {ty}"), } @@ -387,29 +399,29 @@ impl<'a> OpEmitter<'a> { } } - pub fn sub(&mut self, overflow: Overflow) { + pub fn sub(&mut self, overflow: Overflow, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected sub operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Sub); + self.emit(Op::Sub, span); } Type::U64 => { - self.sub_u64(overflow); + self.sub_u64(overflow, span); } Type::I64 => { - self.sub_i64(overflow); + self.sub_i64(overflow, span); } Type::U32 => { - self.sub_u32(overflow); + self.sub_u32(overflow, span); } Type::I32 => { - self.sub_i32(overflow); + self.sub_i32(overflow, span); } ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.sub_uint(ty.size_in_bits() as u32, overflow); + self.sub_uint(ty.size_in_bits() as u32, overflow, span); } ty => unimplemented!("sub is not yet implemented for {ty}"), } @@ -419,29 +431,29 @@ impl<'a> OpEmitter<'a> { } } - pub fn sub_imm(&mut self, imm: Immediate, overflow: Overflow) { + pub fn sub_imm(&mut self, imm: Immediate, overflow: Overflow, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected sub operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::SubImm(imm.as_felt().unwrap())); + self.emit(Op::SubImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_immediate(imm); - self.sub_u64(overflow); + self.push_immediate(imm, span); + self.sub_u64(overflow, span); } Type::I64 => { - self.sub_imm_i64(imm.as_i64().unwrap(), overflow); + self.sub_imm_i64(imm.as_i64().unwrap(), overflow, span); } Type::U32 => { - self.sub_imm_u32(imm.as_u32().unwrap(), overflow); + self.sub_imm_u32(imm.as_u32().unwrap(), overflow, span); } Type::I32 => { - self.sub_imm_i32(imm.as_i32().unwrap(), overflow); + self.sub_imm_i32(imm.as_i32().unwrap(), overflow, span); } ty @ (Type::U16 | Type::U8 | Type::I1) => { - self.sub_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); + self.sub_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow, span); } ty => unimplemented!("sub is not yet implemented for {ty}"), } @@ -451,7 +463,7 @@ impl<'a> OpEmitter<'a> { } } - pub fn mul(&mut self, overflow: Overflow) { + pub fn mul(&mut self, overflow: Overflow, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -479,14 +491,14 @@ impl<'a> OpEmitter<'a> { Overflow::Unchecked | Overflow::Wrapping, "only unchecked or wrapping semantics are supported for felt" ); - self.emit(Op::Mul); + self.emit(Op::Mul, span); } - Type::U64 => self.mul_u64(overflow), - Type::I64 => self.mul_i64(overflow), - Type::U32 => self.mul_u32(overflow), - Type::I32 => self.mul_i32(overflow), + Type::U64 => self.mul_u64(overflow, span), + Type::I64 => self.mul_i64(overflow, span), + Type::U32 => self.mul_u32(overflow, span), + Type::I32 => self.mul_i32(overflow, span), ty @ (Type::U16 | Type::U8) => { - self.mul_uint(ty.size_in_bits() as u32, overflow); + self.mul_uint(ty.size_in_bits() as u32, overflow, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mul expects integer operands, got {ty}") @@ -499,7 +511,7 @@ impl<'a> OpEmitter<'a> { } } - pub fn mul_imm(&mut self, imm: Immediate, overflow: Overflow) { + pub fn mul_imm(&mut self, imm: Immediate, overflow: Overflow, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected mul operands to be the same type"); @@ -510,17 +522,17 @@ impl<'a> OpEmitter<'a> { Overflow::Unchecked | Overflow::Wrapping, "only unchecked or wrapping semantics are supported for felt" ); - self.emit(Op::MulImm(imm.as_felt().unwrap())); + self.emit(Op::MulImm(imm.as_felt().unwrap()), span); } Type::U64 => { - self.push_immediate(imm); - self.mul_u64(overflow); + self.push_immediate(imm, span); + self.mul_u64(overflow, span); } - Type::I64 => self.mul_imm_i64(imm.as_i64().unwrap(), overflow), - Type::U32 => self.mul_imm_u32(imm.as_u32().unwrap(), overflow), - Type::I32 => self.mul_imm_i32(imm.as_i32().unwrap(), overflow), + Type::I64 => self.mul_imm_i64(imm.as_i64().unwrap(), overflow, span), + Type::U32 => self.mul_imm_u32(imm.as_u32().unwrap(), overflow, span), + Type::I32 => self.mul_imm_i32(imm.as_i32().unwrap(), overflow, span), ty @ (Type::U16 | Type::U8) => { - self.mul_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow); + self.mul_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, overflow, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mul expects integer operands, got {ty}") @@ -533,21 +545,21 @@ impl<'a> OpEmitter<'a> { } } - pub fn checked_div(&mut self) { + pub fn checked_div(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected div operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Div); + self.emit(Op::Div, span); } - Type::U64 => self.checked_div_u64(), - Type::I64 => self.checked_div_i64(), - Type::U32 => self.checked_div_u32(), - Type::I32 => self.checked_div_i32(), + Type::U64 => self.checked_div_u64(span), + Type::I64 => self.checked_div_i64(span), + Type::U32 => self.checked_div_u32(span), + Type::I32 => self.checked_div_i32(span), ty @ (Type::U16 | Type::U8) => { - self.checked_div_uint(ty.size_in_bits() as u32); + self.checked_div_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: div expects integer operands, got {ty}") @@ -557,24 +569,24 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn checked_div_imm(&mut self, imm: Immediate) { + pub fn checked_div_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected div operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Div); + self.emit(Op::Div, span); } Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.checked_div_u64(); + self.push_immediate(imm, span); + self.checked_div_u64(span); } - Type::I64 => self.checked_div_imm_i64(imm.as_i64().unwrap()), - Type::U32 => self.checked_div_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap()), + Type::I64 => self.checked_div_imm_i64(imm.as_i64().unwrap(), span), + Type::U32 => self.checked_div_imm_u32(imm.as_u32().unwrap(), span), + Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.checked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.checked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: div expects integer operands, got {ty}") @@ -584,19 +596,19 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_div(&mut self) { + pub fn unchecked_div(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected div operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Div); + self.emit(Op::Div, span); } - Type::U64 => self.unchecked_div_u64(), - Type::I64 => self.checked_div_i64(), - Type::U32 | Type::U16 | Type::U8 => self.unchecked_div_u32(), - Type::I32 => self.checked_div_i32(), + Type::U64 => self.unchecked_div_u64(span), + Type::I64 => self.checked_div_i64(span), + Type::U32 | Type::U16 | Type::U8 => self.unchecked_div_u32(span), + Type::I32 => self.checked_div_i32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: div expects integer operands, got {ty}") } @@ -605,24 +617,24 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_div_imm(&mut self, imm: Immediate) { + pub fn unchecked_div_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected div operands to be the same type"); match &ty { Type::Felt => { - self.emit(Op::Div); + self.emit(Op::Div, span); } Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.unchecked_div_u64(); + self.push_immediate(imm, span); + self.unchecked_div_u64(span); } - Type::I64 => self.checked_div_imm_i64(imm.as_i64().unwrap()), - Type::U32 => self.unchecked_div_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap()), + Type::I64 => self.checked_div_imm_i64(imm.as_i64().unwrap(), span), + Type::U32 => self.unchecked_div_imm_u32(imm.as_u32().unwrap(), span), + Type::I32 => self.checked_div_imm_i32(imm.as_i32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.unchecked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.unchecked_div_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: div expects integer operands, got {ty}") @@ -632,16 +644,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn checked_mod(&mut self) { + pub fn checked_mod(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected mod operands to be the same type"); match &ty { - Type::U64 => self.checked_mod_u64(), - Type::U32 => self.checked_mod_u32(), + Type::U64 => self.checked_mod_u64(span), + Type::U32 => self.checked_mod_u32(span), ty @ (Type::U16 | Type::U8) => { - self.checked_mod_uint(ty.size_in_bits() as u32); + self.checked_mod_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mod expects integer operands, got {ty}") @@ -651,19 +663,19 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn checked_mod_imm(&mut self, imm: Immediate) { + pub fn checked_mod_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected mod operands to be the same type"); match &ty { Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.checked_mod_u64(); + self.push_immediate(imm, span); + self.checked_mod_u64(span); } - Type::U32 => self.checked_mod_imm_u32(imm.as_u32().unwrap()), + Type::U32 => self.checked_mod_imm_u32(imm.as_u32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.checked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.checked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mod expects integer operands, got {ty}") @@ -673,16 +685,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_mod(&mut self) { + pub fn unchecked_mod(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected mod operands to be the same type"); match &ty { - Type::U64 => self.unchecked_mod_u64(), - Type::U32 => self.unchecked_mod_u32(), + Type::U64 => self.unchecked_mod_u64(span), + Type::U32 => self.unchecked_mod_u32(span), ty @ (Type::U16 | Type::U8) => { - self.unchecked_mod_uint(ty.size_in_bits() as u32); + self.unchecked_mod_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mod expects integer operands, got {ty}") @@ -692,19 +704,19 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_mod_imm(&mut self, imm: Immediate) { + pub fn unchecked_mod_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected mod operands to be the same type"); match &ty { Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.unchecked_mod_u64(); + self.push_immediate(imm, span); + self.unchecked_mod_u64(span); } - Type::U32 => self.unchecked_mod_imm_u32(imm.as_u32().unwrap()), + Type::U32 => self.unchecked_mod_imm_u32(imm.as_u32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.unchecked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.unchecked_mod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: mod expects integer operands, got {ty}") @@ -714,16 +726,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn checked_divmod(&mut self) { + pub fn checked_divmod(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected divmod operands to be the same type"); match &ty { - Type::U64 => self.checked_divmod_u64(), - Type::U32 => self.checked_divmod_u32(), + Type::U64 => self.checked_divmod_u64(span), + Type::U32 => self.checked_divmod_u32(span), ty @ (Type::U16 | Type::U8) => { - self.checked_divmod_uint(ty.size_in_bits() as u32); + self.checked_divmod_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: divmod expects integer operands, got {ty}") @@ -734,19 +746,19 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn checked_divmod_imm(&mut self, imm: Immediate) { + pub fn checked_divmod_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected divmod operands to be the same type"); match &ty { Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.checked_divmod_u64(); + self.push_immediate(imm, span); + self.checked_divmod_u64(span); } - Type::U32 => self.checked_divmod_imm_u32(imm.as_u32().unwrap()), + Type::U32 => self.checked_divmod_imm_u32(imm.as_u32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.checked_divmod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.checked_divmod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: divmod expects integer operands, got {ty}") @@ -757,16 +769,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_divmod(&mut self) { + pub fn unchecked_divmod(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected divmod operands to be the same type"); match &ty { - Type::U64 => self.unchecked_divmod_u64(), - Type::U32 => self.unchecked_divmod_u32(), + Type::U64 => self.unchecked_divmod_u64(span), + Type::U32 => self.unchecked_divmod_u32(span), ty @ (Type::U16 | Type::U8) => { - self.unchecked_divmod_uint(ty.size_in_bits() as u32); + self.unchecked_divmod_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: divmod expects integer operands, got {ty}") @@ -777,19 +789,23 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn unchecked_divmod_imm(&mut self, imm: Immediate) { + pub fn unchecked_divmod_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected divmod operands to be the same type"); match &ty { Type::U64 => { assert_ne!(imm.as_u64().unwrap(), 0, "invalid division by zero"); - self.push_immediate(imm); - self.unchecked_divmod_u64(); + self.push_immediate(imm, span); + self.unchecked_divmod_u64(span); } - Type::U32 => self.unchecked_divmod_imm_u32(imm.as_u32().unwrap()), + Type::U32 => self.unchecked_divmod_imm_u32(imm.as_u32().unwrap(), span), ty @ (Type::U16 | Type::U8) => { - self.unchecked_divmod_imm_uint(imm.as_u32().unwrap(), ty.size_in_bits() as u32); + self.unchecked_divmod_imm_uint( + imm.as_u32().unwrap(), + ty.size_in_bits() as u32, + span, + ); } ty if !ty.is_integer() => { panic!("invalid binary operand: divmod expects integer operands, got {ty}") @@ -800,7 +816,7 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn exp(&mut self) { + pub fn exp(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -808,17 +824,17 @@ impl<'a> OpEmitter<'a> { match &ty { Type::U64 => todo!("exponentiation by squaring"), Type::Felt => { - self.emit(Op::Exp); + self.emit(Op::Exp, span); } Type::U32 => { - self.emit_all(&[Op::Exp, Op::U32Assert]); + self.emit_all(&[Op::Exp, Op::U32Assert], span); } Type::I32 => { - self.emit(Op::Exec("intrinsics::i32::ipow".parse().unwrap())); + self.emit(Op::Exec("intrinsics::i32::ipow".parse().unwrap()), span); } ty @ (Type::U16 | Type::U8) => { - self.emit_all(&[Op::Exp, Op::U32Assert]); - self.int32_to_uint(ty.size_in_bits() as u32); + self.emit_all(&[Op::Exp, Op::U32Assert], span); + self.int32_to_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: exp expects integer operands, got {ty}") @@ -828,7 +844,7 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn exp_imm(&mut self, imm: Immediate) { + pub fn exp_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected exp operands to be the same type"); @@ -837,20 +853,20 @@ impl<'a> OpEmitter<'a> { match &ty { Type::U64 => todo!("exponentiation by squaring"), Type::Felt => { - self.emit(Op::ExpImm(exp)); + self.emit(Op::ExpImm(exp), span); } Type::U32 => { - self.emit_all(&[Op::ExpImm(exp), Op::U32Assert]); + self.emit_all(&[Op::ExpImm(exp), Op::U32Assert], span); } Type::I32 => { - self.emit_all(&[ - Op::PushU8(exp), - Op::Exec("intrinsics::i32::ipow".parse().unwrap()), - ]); + self.emit_all( + &[Op::PushU8(exp), Op::Exec("intrinsics::i32::ipow".parse().unwrap())], + span, + ); } ty @ (Type::U16 | Type::U8) => { - self.emit_all(&[Op::ExpImm(exp), Op::U32Assert]); - self.int32_to_uint(ty.size_in_bits() as u32); + self.emit_all(&[Op::ExpImm(exp), Op::U32Assert], span); + self.int32_to_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: exp expects integer operands, got {ty}") @@ -860,64 +876,64 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn and(&mut self) { + pub fn and(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected and operands to be the same type"); assert_eq!(ty, Type::I1, "expected and operands to be of boolean type"); - self.emit(Op::And); + self.emit(Op::And, span); self.push(ty); } - pub fn and_imm(&mut self, imm: Immediate) { + pub fn and_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected and operands to be the same type"); assert_eq!(ty, Type::I1, "expected and operands to be of boolean type"); - self.emit(Op::AndImm(imm.as_bool().unwrap())); + self.emit(Op::AndImm(imm.as_bool().unwrap()), span); self.push(ty); } - pub fn or(&mut self) { + pub fn or(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected or operands to be the same type"); assert_eq!(ty, Type::I1, "expected or operands to be of boolean type"); - self.emit(Op::Or); + self.emit(Op::Or, span); self.push(ty); } - pub fn or_imm(&mut self, imm: Immediate) { + pub fn or_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected or operands to be the same type"); assert_eq!(ty, Type::I1, "expected or operands to be of boolean type"); - self.emit(Op::OrImm(imm.as_bool().unwrap())); + self.emit(Op::OrImm(imm.as_bool().unwrap()), span); self.push(ty); } - pub fn xor(&mut self) { + pub fn xor(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected xor operands to be the same type"); assert_eq!(ty, Type::I1, "expected xor operands to be of boolean type"); - self.emit(Op::Xor); + self.emit(Op::Xor, span); self.push(ty); } - pub fn xor_imm(&mut self, imm: Immediate) { + pub fn xor_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected xor operands to be the same type"); assert_eq!(ty, Type::I1, "expected xor operands to be of boolean type"); - self.emit(Op::XorImm(imm.as_bool().unwrap())); + self.emit(Op::XorImm(imm.as_bool().unwrap()), span); self.push(ty); } - pub fn band(&mut self) { + pub fn band(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -927,28 +943,39 @@ impl<'a> OpEmitter<'a> { // AND the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.band_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // AND the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.band_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); - } - Type::U64 | Type::I64 => self.band_int64(), - Type::U32 | Type::I32 | Type::U16 | Type::I16 | Type::U8 | Type::I8 => self.band_u32(), - Type::I1 => self.emit(Op::And), + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.band_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // AND the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.band_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); + } + Type::U64 | Type::I64 => self.band_int64(span), + Type::U32 | Type::I32 | Type::U16 | Type::I16 | Type::U8 | Type::I8 => { + self.band_u32(span) + } + Type::I1 => self.emit(Op::And, span), ty if !ty.is_integer() => { panic!("invalid binary operand: band expects integer operands, got {ty}") } @@ -957,44 +984,53 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn band_imm(&mut self, imm: Immediate) { + pub fn band_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected band operands to be the same type"); match &ty { Type::U128 | Type::I128 => { - self.push_immediate(imm); + self.push_immediate(imm, span); // AND the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.band_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // AND the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.band_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.band_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // AND the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.band_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); } Type::U64 | Type::I64 => { - self.push_immediate(imm); - self.band_int64(); + self.push_immediate(imm, span); + self.band_int64(span); } - Type::U32 | Type::U16 | Type::U8 => self.band_imm_u32(imm.as_u32().unwrap()), + Type::U32 | Type::U16 | Type::U8 => self.band_imm_u32(imm.as_u32().unwrap(), span), Type::I32 | Type::I16 | Type::I8 => { - self.band_imm_u32(imm.as_i64().unwrap() as u64 as u32) + self.band_imm_u32(imm.as_i64().unwrap() as u64 as u32, span) } - Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap())), + Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap()), span), ty if !ty.is_integer() => { panic!("invalid binary operand: band expects integer operands, got {ty}") } @@ -1003,7 +1039,7 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn bor(&mut self) { + pub fn bor(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -1013,28 +1049,39 @@ impl<'a> OpEmitter<'a> { // OR the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.bor_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // OR the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.bor_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); - } - Type::U64 | Type::I64 => self.bor_int64(), - Type::U32 | Type::I32 | Type::U16 | Type::I16 | Type::U8 | Type::I8 => self.bor_u32(), - Type::I1 => self.emit(Op::Or), + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.bor_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // OR the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.bor_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); + } + Type::U64 | Type::I64 => self.bor_int64(span), + Type::U32 | Type::I32 | Type::U16 | Type::I16 | Type::U8 | Type::I8 => { + self.bor_u32(span) + } + Type::I1 => self.emit(Op::Or, span), ty if !ty.is_integer() => { panic!("invalid binary operand: bor expects integer operands, got {ty}") } @@ -1043,44 +1090,53 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn bor_imm(&mut self, imm: Immediate) { + pub fn bor_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected bor operands to be the same type"); match &ty { Type::U128 | Type::I128 => { - self.push_immediate(imm); + self.push_immediate(imm, span); // OR the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.bor_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // OR the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.bor_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.bor_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // OR the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.bor_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); } Type::U64 | Type::I64 => { - self.push_immediate(imm); - self.bor_int64(); + self.push_immediate(imm, span); + self.bor_int64(span); } - Type::U32 | Type::U16 | Type::U8 => self.bor_imm_u32(imm.as_u32().unwrap()), + Type::U32 | Type::U16 | Type::U8 => self.bor_imm_u32(imm.as_u32().unwrap(), span), Type::I32 | Type::I16 | Type::I8 => { - self.bor_imm_u32(imm.as_i64().unwrap() as u64 as u32) + self.bor_imm_u32(imm.as_i64().unwrap() as u64 as u32, span) } - Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap())), + Type::I1 => self.emit(Op::AndImm(imm.as_bool().unwrap()), span), ty if !ty.is_integer() => { panic!("invalid binary operand: bor expects integer operands, got {ty}") } @@ -1089,7 +1145,7 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn bxor(&mut self) { + pub fn bxor(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -1099,32 +1155,41 @@ impl<'a> OpEmitter<'a> { // XOR the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.bxor_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // XOR the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.bxor_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); - } - Type::U64 | Type::I64 => self.bxor_int64(), - Type::U32 | Type::I32 => self.bxor_u32(), + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.bxor_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // XOR the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.bxor_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); + } + Type::U64 | Type::I64 => self.bxor_int64(span), + Type::U32 | Type::I32 => self.bxor_u32(span), ty @ (Type::U16 | Type::I16 | Type::U8 | Type::I8) => { - self.bxor_u32(); - self.trunc_int32(ty.size_in_bits() as u32); + self.bxor_u32(span); + self.trunc_int32(ty.size_in_bits() as u32, span); } - Type::I1 => self.emit(Op::Xor), + Type::I1 => self.emit(Op::Xor, span), ty if !ty.is_integer() => { panic!("invalid binary operand: bxor expects integer operands, got {ty}") } @@ -1133,50 +1198,59 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn bxor_imm(&mut self, imm: Immediate) { + pub fn bxor_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected bxor operands to be the same type"); match &ty { Type::U128 | Type::I128 => { - self.push_immediate(imm); + self.push_immediate(imm, span); // XOR the high bits // // [b_hi_hi, b_hi_lo, b_lo_hi, b_lo_lo, a_hi_hi, ..] - self.emit_all(&[ - // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] - Op::Movup(5), - Op::Movup(5), - ]); - self.bxor_int64(); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] - // XOR the low bits - self.emit_all(&[ - // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] - Op::Movdn(5), - Op::Movdn(5), - ]); - self.bxor_int64(); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] - self.emit_all(&[ - // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] - Op::Movup(3), - Op::Movup(3), - ]); + self.emit_all( + &[ + // [a_hi_hi, a_hi_lo, b_hi_hi, b_hi_lo, ..] + Op::Movup(5), + Op::Movup(5), + ], + span, + ); + self.bxor_int64(span); // [band_hi_hi, band_hi_lo, b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo] + // XOR the low bits + self.emit_all( + &[ + // [b_lo_hi, b_lo_lo, a_lo_hi, a_lo_lo, band_hi_hi, band_hi_lo] + Op::Movdn(5), + Op::Movdn(5), + ], + span, + ); + self.bxor_int64(span); // [band_lo_hi, band_lo_lo, band_hi_hi, band_hi_lo] + self.emit_all( + &[ + // [band_hi_hi, band_hi_lo, band_lo_hi, band_lo_lo] + Op::Movup(3), + Op::Movup(3), + ], + span, + ); } Type::U64 | Type::I64 => { - self.push_immediate(imm); - self.bxor_int64(); + self.push_immediate(imm, span); + self.bxor_int64(span); } - Type::U32 => self.bxor_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.bxor_imm_u32(imm.as_i64().unwrap() as u64 as u32), + Type::U32 => self.bxor_imm_u32(imm.as_u32().unwrap(), span), + Type::I32 => self.bxor_imm_u32(imm.as_i64().unwrap() as u64 as u32, span), ty @ (Type::U16 | Type::U8) => { - self.bxor_imm_u32(imm.as_u32().unwrap()); - self.trunc_int32(ty.size_in_bits() as u32); + self.bxor_imm_u32(imm.as_u32().unwrap(), span); + self.trunc_int32(ty.size_in_bits() as u32, span); } ty @ (Type::I16 | Type::I8) => { - self.bxor_imm_u32(imm.as_i64().unwrap() as u64 as u32); - self.trunc_int32(ty.size_in_bits() as u32); + self.bxor_imm_u32(imm.as_i64().unwrap() as u64 as u32, span); + self.trunc_int32(ty.size_in_bits() as u32, span); } - Type::I1 => self.emit(Op::XorImm(imm.as_bool().unwrap())), + Type::I1 => self.emit(Op::XorImm(imm.as_bool().unwrap()), span), ty if !ty.is_integer() => { panic!("invalid binary operand: bxor expects integer operands, got {ty}") } @@ -1185,17 +1259,17 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn shl(&mut self) { + pub fn shl(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(rhs.ty(), Type::U32, "expected shift operand to be u32"); match &ty { - Type::U64 | Type::I64 => self.shl_u64(), - Type::U32 | Type::I32 => self.shl_u32(), + Type::U64 | Type::I64 => self.shl_u64(span), + Type::U32 | Type::I32 => self.shl_u32(span), ty @ (Type::U16 | Type::I16 | Type::U8 | Type::I8) => { - self.shl_u32(); - self.trunc_int32(ty.size_in_bits() as u32); + self.shl_u32(span); + self.trunc_int32(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: shl expects integer operands, got {ty}") @@ -1205,21 +1279,21 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn shl_imm(&mut self, imm: Immediate) { + pub fn shl_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(imm.ty(), Type::U32, "expected shift operand to be u32"); match &ty { Type::U64 | Type::I64 => { assert!(imm.as_u32().unwrap() < 64, "invalid shift value: must be < 64"); - self.push_immediate(imm); - self.shl_u64(); + self.push_immediate(imm, span); + self.shl_u64(span); } - Type::U32 => self.shl_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.shl_imm_u32(imm.as_u32().unwrap()), + Type::U32 => self.shl_imm_u32(imm.as_u32().unwrap(), span), + Type::I32 => self.shl_imm_u32(imm.as_u32().unwrap(), span), ty @ (Type::U16 | Type::I16 | Type::U8 | Type::I8) => { - self.shl_imm_u32(imm.as_u32().unwrap()); - self.trunc_int32(ty.size_in_bits() as u32); + self.shl_imm_u32(imm.as_u32().unwrap(), span); + self.trunc_int32(ty.size_in_bits() as u32, span); } ty if !ty.is_integer() => { panic!("invalid binary operand: shl expects integer operands, got {ty}") @@ -1229,16 +1303,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn shr(&mut self) { + pub fn shr(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(rhs.ty(), Type::U32, "expected shift operand to be u32"); match &ty { - Type::U64 => self.shr_u64(), - Type::I64 => self.shr_i64(), - Type::U32 | Type::U16 | Type::U8 => self.shr_u32(), - Type::I32 => self.shr_i32(), + Type::U64 => self.shr_u64(span), + Type::I64 => self.shr_i64(span), + Type::U32 | Type::U16 | Type::U8 => self.shr_u32(span), + Type::I32 => self.shr_i32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: shr expects integer operands, got {ty}") } @@ -1247,7 +1321,7 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn shr_imm(&mut self, imm: Immediate) { + pub fn shr_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(imm.ty(), Type::U32, "expected shift operand to be u32"); @@ -1255,12 +1329,12 @@ impl<'a> OpEmitter<'a> { Type::U64 => { let shift = imm.as_u32().unwrap(); assert!(shift < 64, "invalid shift value: must be < 64, got {shift}"); - self.push_immediate(imm); - self.shr_u64(); + self.push_immediate(imm, span); + self.shr_u64(span); } - Type::I64 => self.shr_imm_i64(imm.as_u32().unwrap()), - Type::U32 | Type::U16 | Type::U8 => self.shr_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.shr_imm_i32(imm.as_u32().unwrap()), + Type::I64 => self.shr_imm_i64(imm.as_u32().unwrap(), span), + Type::U32 | Type::U16 | Type::U8 => self.shr_imm_u32(imm.as_u32().unwrap(), span), + Type::I32 => self.shr_imm_i32(imm.as_u32().unwrap(), span), ty if !ty.is_integer() => { panic!("invalid binary operand: shr expects integer operands, got {ty}") } @@ -1269,14 +1343,14 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn rotl(&mut self) { + pub fn rotl(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(rhs.ty(), Type::U32, "expected shift operand to be u32"); match &ty { - Type::U64 | Type::I64 => self.rotl_u64(), - Type::U32 | Type::I32 => self.rotl_u32(), + Type::U64 | Type::I64 => self.rotl_u64(span), + Type::U32 | Type::I32 => self.rotl_u32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: rotl expects integer operands, got {ty}") } @@ -1285,16 +1359,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn rotl_imm(&mut self, imm: Immediate) { + pub fn rotl_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(imm.ty(), Type::U32, "expected shift operand to be u32"); match &ty { Type::U64 | Type::I64 => { - self.push_immediate(imm); - self.rotl_u64(); + self.push_immediate(imm, span); + self.rotl_u64(span); } - Type::U32 | Type::I32 => self.rotl_imm_u32(imm.as_u32().unwrap()), + Type::U32 | Type::I32 => self.rotl_imm_u32(imm.as_u32().unwrap(), span), ty if !ty.is_integer() => { panic!("invalid binary operand: rotl expects integer operands, got {ty}") } @@ -1303,14 +1377,14 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn rotr(&mut self) { + pub fn rotr(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(rhs.ty(), Type::U32, "expected shift operand to be u32"); match &ty { - Type::U64 | Type::I64 => self.rotr_u64(), - Type::U32 | Type::I32 => self.rotr_u32(), + Type::U64 | Type::I64 => self.rotr_u64(span), + Type::U32 | Type::I32 => self.rotr_u32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: rotr expects integer operands, got {ty}") } @@ -1319,16 +1393,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn rotr_imm(&mut self, imm: Immediate) { + pub fn rotr_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(imm.ty(), Type::U32, "expected shift operand to be u32"); match &ty { Type::U64 | Type::I64 => { - self.push_immediate(imm); - self.rotr_u64(); + self.push_immediate(imm, span); + self.rotr_u64(span); } - Type::U32 | Type::I32 => self.rotr_imm_u32(imm.as_u32().unwrap()), + Type::U32 | Type::I32 => self.rotr_imm_u32(imm.as_u32().unwrap(), span), ty if !ty.is_integer() => { panic!("invalid binary operand: rotr expects integer operands, got {ty}") } @@ -1337,16 +1411,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn min(&mut self) { + pub fn min(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected min operands to be the same type"); match &ty { - Type::U64 => self.min_u64(), - Type::I64 => self.min_i64(), - Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.min_u32(), - Type::I32 => self.min_i32(), + Type::U64 => self.min_u64(span), + Type::I64 => self.min_i64(span), + Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.min_u32(span), + Type::I32 => self.min_i32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: min expects integer operands, got {ty}") } @@ -1355,18 +1429,20 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn min_imm(&mut self, imm: Immediate) { + pub fn min_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected min operands to be the same type"); match &ty { Type::U64 => { - self.push_immediate(imm); - self.min_u64(); + self.push_immediate(imm, span); + self.min_u64(span); } - Type::I64 => self.min_imm_i64(imm.as_i64().unwrap()), - Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.min_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.min_imm_i32(imm.as_i32().unwrap()), + Type::I64 => self.min_imm_i64(imm.as_i64().unwrap(), span), + Type::U32 | Type::U16 | Type::U8 | Type::I1 => { + self.min_imm_u32(imm.as_u32().unwrap(), span) + } + Type::I32 => self.min_imm_i32(imm.as_i32().unwrap(), span), ty if !ty.is_integer() => { panic!("invalid binary operand: min expects integer operands, got {ty}") } @@ -1375,16 +1451,16 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn max(&mut self) { + pub fn max(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, rhs.ty(), "expected max operands to be the same type"); match &ty { - Type::U64 => self.max_u64(), - Type::I64 => self.max_i64(), - Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.max_u32(), - Type::I32 => self.max_i32(), + Type::U64 => self.max_u64(span), + Type::I64 => self.max_i64(span), + Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.max_u32(span), + Type::I32 => self.max_i32(span), ty if !ty.is_integer() => { panic!("invalid binary operand: max expects integer operands, got {ty}") } @@ -1393,18 +1469,20 @@ impl<'a> OpEmitter<'a> { self.push(ty); } - pub fn max_imm(&mut self, imm: Immediate) { + pub fn max_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected max operands to be the same type"); match &ty { Type::U64 => { - self.push_immediate(imm); - self.max_u64(); + self.push_immediate(imm, span); + self.max_u64(span); + } + Type::I64 => self.max_imm_i64(imm.as_i64().unwrap(), span), + Type::U32 | Type::U16 | Type::U8 | Type::I1 => { + self.max_imm_u32(imm.as_u32().unwrap(), span) } - Type::I64 => self.max_imm_i64(imm.as_i64().unwrap()), - Type::U32 | Type::U16 | Type::U8 | Type::I1 => self.max_imm_u32(imm.as_u32().unwrap()), - Type::I32 => self.max_imm_i32(imm.as_i32().unwrap()), + Type::I32 => self.max_imm_i32(imm.as_i32().unwrap(), span), ty if !ty.is_integer() => { panic!("invalid binary operand: max expects integer operands, got {ty}") } diff --git a/codegen/masm/src/codegen/emit/felt.rs b/codegen/masm/src/codegen/emit/felt.rs index 3e25c15e2..886bc39e1 100644 --- a/codegen/masm/src/codegen/emit/felt.rs +++ b/codegen/masm/src/codegen/emit/felt.rs @@ -1,4 +1,4 @@ -use midenc_hir::{Felt, FieldElement}; +use midenc_hir::{Felt, FieldElement, SourceSpan}; use super::OpEmitter; use crate::masm::Op; @@ -19,8 +19,8 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a == 0, a, ..]` #[inline(always)] - pub fn felt_is_zero(&mut self) { - self.emit_all(&[Op::Dup(0), Op::EqImm(ZERO)]); + pub fn felt_is_zero(&mut self, span: SourceSpan) { + self.emit_all(&[Op::Dup(0), Op::EqImm(ZERO)], span); } /// This operation asserts the field element on top of the stack is zero. @@ -31,8 +31,8 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a, ..]` #[inline(always)] - pub fn assert_felt_is_zero(&mut self) { - self.emit_all(&[Op::Dup(0), Op::Assertz]); + pub fn assert_felt_is_zero(&mut self, span: SourceSpan) { + self.emit_all(&[Op::Dup(0), Op::Assertz], span); } /// Convert a field element to i128 by zero-extension. @@ -43,8 +43,8 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [0, 0, a_hi, a_lo]` #[inline] - pub fn felt_to_i128(&mut self) { - self.emit_all(&[Op::U32Split, Op::Push2([ZERO, ZERO])]); + pub fn felt_to_i128(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Split, Op::Push2([ZERO, ZERO])], span); } /// Convert a field element to u64 by zero-extension. @@ -55,8 +55,8 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a_hi, a_lo]` #[inline(always)] - pub fn felt_to_u64(&mut self) { - self.emit(Op::U32Split); + pub fn felt_to_u64(&mut self, span: SourceSpan) { + self.emit(Op::U32Split, span); } /// Convert a field element to i64 by zero-extension. @@ -69,58 +69,64 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a_hi, a_lo]` #[inline(always)] - pub fn felt_to_i64(&mut self) { - self.felt_to_u64(); + pub fn felt_to_i64(&mut self, span: SourceSpan) { + self.felt_to_u64(span); } /// Convert a field element value to an unsigned N-bit integer, where N <= 32 /// /// Conversion will trap if the input value is too large to fit in an unsigned N-bit integer. - pub fn felt_to_uint(&mut self, n: u32) { + pub fn felt_to_uint(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); - self.emit_all(&[ - // Split into u32 limbs - Op::U32Split, - // Assert most significant 32 bits are unused - Op::Assertz, - ]); + self.emit_all( + &[ + // Split into u32 limbs + Op::U32Split, + // Assert most significant 32 bits are unused + Op::Assertz, + ], + span, + ); if n < 32 { // Convert to N-bit integer - self.int32_to_uint(n); + self.int32_to_uint(n, span); } } /// Convert a field element value to a signed N-bit integer, where N <= 32 /// /// Conversion will trap if the input value is too large to fit in a signed N-bit integer. - pub fn felt_to_int(&mut self, n: u32) { + pub fn felt_to_int(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); - self.emit_all(&[ - // Split into u32 limbs - Op::U32Split, - // Assert most significant 32 bits are unused - Op::Assertz, - ]); + self.emit_all( + &[ + // Split into u32 limbs + Op::U32Split, + // Assert most significant 32 bits are unused + Op::Assertz, + ], + span, + ); // Assert the sign bit isn't set - self.assert_unsigned_int32(); + self.assert_unsigned_int32(span); if n < 32 { // Convert to signed N-bit integer - self.int32_to_int(n); + self.int32_to_int(n, span); } } /// Zero-extend a field element value to N-bits, where N >= 64 /// /// N must be a power of two, or this function will panic. - pub fn zext_felt(&mut self, n: u32) { + pub fn zext_felt(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 64, 256); match n { - 64 => self.felt_to_u64(), - 128 => self.felt_to_i128(), + 64 => self.felt_to_u64(span), + 128 => self.felt_to_i128(span), n => { // Convert to u64 and zero-extend - self.felt_to_u64(); - self.zext_int64(n); + self.felt_to_u64(span); + self.zext_int64(n, span); } } } @@ -131,15 +137,15 @@ impl<'a> OpEmitter<'a> { /// integer type is a signed type, so we have one less bit available to use. /// /// N must be a power of two, or this function will panic. - pub fn sext_felt(&mut self, n: u32) { + pub fn sext_felt(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 64, 256); match n { - 64 => self.felt_to_i64(), - 128 => self.felt_to_i128(), + 64 => self.felt_to_i64(span), + 128 => self.felt_to_i128(span), n => { // Convert to i64 and sign-extend - self.felt_to_i64(); - self.sext_int64(n); + self.felt_to_i64(span); + self.sext_int64(n, span); } } } @@ -154,17 +160,17 @@ impl<'a> OpEmitter<'a> { /// This should produce outputs which are identical to equivalent u64 values, i.e. the same /// value in both u64 and felt representation will be truncated to the same u32 value. #[inline] - pub fn trunc_felt(&mut self, n: u32) { + pub fn trunc_felt(&mut self, n: u32, span: SourceSpan) { // Apply a field modulus of 2^32, i.e. `a mod 2^32`, converting // the field element into the u32 range. Miden defines values in // this range as having a standard unsigned binary representation. - self.emit(Op::U32Cast); - self.trunc_int32(n); + self.emit(Op::U32Cast, span); + self.trunc_int32(n, span); } /// Make `n` copies of the element on top of the stack #[inline(always)] - pub fn dup_felt(&mut self, count: u8) { - self.emit_n(count as usize, Op::Dup(0)); + pub fn dup_felt(&mut self, count: u8, span: SourceSpan) { + self.emit_n(count as usize, Op::Dup(0), span); } } diff --git a/codegen/masm/src/codegen/emit/int128.rs b/codegen/masm/src/codegen/emit/int128.rs index a34278221..16f44940a 100644 --- a/codegen/masm/src/codegen/emit/int128.rs +++ b/codegen/masm/src/codegen/emit/int128.rs @@ -1,3 +1,5 @@ +use midenc_hir::SourceSpan; + use super::OpEmitter; use crate::masm::Op; @@ -5,21 +7,21 @@ use crate::masm::Op; impl<'a> OpEmitter<'a> { /// Checks if the i128 value on the stack has its sign bit set. #[inline(always)] - pub fn is_signed_int128(&mut self) { - self.is_signed_int32() + pub fn is_signed_int128(&mut self, span: SourceSpan) { + self.is_signed_int32(span) } /// Assert that the i128 value on the stack does not have its sign bit set. #[inline(always)] - pub fn assert_unsigned_int128(&mut self) { + pub fn assert_unsigned_int128(&mut self, span: SourceSpan) { // Assert that the sign bit is unset - self.assert_unsigned_int32() + self.assert_unsigned_int32(span) } /// Push a u128 value on the operand stack /// /// An u128 value consists of 4 32-bit limbs - pub fn push_u128(&mut self, value: u128) { + pub fn push_u128(&mut self, value: u128, span: SourceSpan) { let bytes = value.to_le_bytes(); let hi = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], @@ -27,14 +29,14 @@ impl<'a> OpEmitter<'a> { let lo = u64::from_le_bytes([ bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], ]); - self.push_u64(lo); - self.push_u64(hi); + self.push_u64(lo, span); + self.push_u64(hi, span); } /// Push an i128 value on the operand stack /// /// An i128 value consists of 4 32-bit limbs - pub fn push_i128(&mut self, value: i128) { + pub fn push_i128(&mut self, value: i128, span: SourceSpan) { let bytes = value.to_le_bytes(); let hi = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], @@ -42,8 +44,8 @@ impl<'a> OpEmitter<'a> { let lo = u64::from_le_bytes([ bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], ]); - self.push_u64(lo); - self.push_u64(hi); + self.push_u64(lo, span); + self.push_u64(hi, span); } /// Convert an i128 value to a field element value. @@ -56,11 +58,11 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, the caller is expected to /// have already validated that the top of the stack holds a valid i128. - pub fn int128_to_felt(&mut self) { + pub fn int128_to_felt(&mut self, span: SourceSpan) { // First, convert to u64 - self.int128_to_u64(); + self.int128_to_u64(span); // Then convert the u64 to felt - self.u64_to_felt(); + self.u64_to_felt(span); } /// Convert a 128-bit value to u64 @@ -72,12 +74,12 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, the caller is expected to /// have already validated that the top of the stack holds a valid i128. - pub fn int128_to_u64(&mut self) { + pub fn int128_to_u64(&mut self, span: SourceSpan) { // Assert the first two limbs are equal to 0 // // What remains on the stack at this point are the low 64-bits, // which is also our result. - self.emit_n(2, Op::Assertz); + self.emit_n(2, Op::Assertz, span); } /// Convert a 128-bit value to u32 @@ -89,12 +91,12 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, the caller is expected to /// have already validated that the top of the stack holds a valid i128. - pub fn int128_to_u32(&mut self) { + pub fn int128_to_u32(&mut self, span: SourceSpan) { // Assert the first three limbs are equal to 0 // // What remains on the stack at this point are the low 32-bits, // which is also our result. - self.emit_n(3, Op::Assertz); + self.emit_n(3, Op::Assertz, span); } /// Convert a unsigned 128-bit value to i64 @@ -106,11 +108,11 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, the caller is expected to /// have already validated that the top of the stack holds a valid i128. - pub fn u128_to_i64(&mut self) { + pub fn u128_to_i64(&mut self, span: SourceSpan) { // Truncate the first 64-bits, so long as those bits are zero - self.int128_to_u64(); + self.int128_to_u64(span); // Ensure that the remaining 64 bits are a valid non-negative i64 value - self.assert_unsigned_int64(); + self.assert_unsigned_int64(span); } /// Convert an i128 value to i64 @@ -122,39 +124,45 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, the caller is expected to /// have already validated that the top of the stack holds a valid i128. - pub fn i128_to_i64(&mut self) { + pub fn i128_to_i64(&mut self, span: SourceSpan) { // Determine if this value is signed or not - self.is_signed_int32(); + self.is_signed_int32(span); // Preserving the is_signed flag, select the expected hi bits value - self.emit(Op::Dup(0)); - self.select_int32(u32::MAX, 0); + self.emit(Op::Dup(0), span); + self.select_int32(u32::MAX, 0, span); // Move the most significant 64 bits to top of stack - self.move_int64_up(2); + self.move_int64_up(2, span); // Move expected value to top of stack - self.emit(Op::Movup(2)); + self.emit(Op::Movup(2), span); // Assert the most significant 32 bits match, without consuming them - self.assert_eq_u32(); - self.emit_all(&[ - // Assert that both 32-bit limbs of the most significant 64 bits match, - // consuming them in the process - Op::AssertEq, - // At this point, the stack is: [is_signed, x1, x0] - // - // Select an expected value for the sign bit based on the is_signed flag - Op::Swap(1), - ]); + self.assert_eq_u32(span); + self.emit_all( + &[ + // Assert that both 32-bit limbs of the most significant 64 bits match, + // consuming them in the process + Op::AssertEq, + // At this point, the stack is: [is_signed, x1, x0] + // + // Select an expected value for the sign bit based on the is_signed flag + Op::Swap(1), + ], + span, + ); // [is_sign_bit_set, x1, is_signed, x0] - self.is_const_flag_set_u32(1 << 31); - self.emit_all(&[ - // [is_signed, is_sign_bit_set, x1, x0] - Op::Movup(2), - // Assert that the flags are equal: either the input was signed and the - // sign bit was set, or the input was unsigned, and the sign bit was unset, - // any other combination will trap. - // - // [x1, x0] - Op::AssertEq, - ]); + self.is_const_flag_set_u32(1 << 31, span); + self.emit_all( + &[ + // [is_signed, is_sign_bit_set, x1, x0] + Op::Movup(2), + // Assert that the flags are equal: either the input was signed and the + // sign bit was set, or the input was unsigned, and the sign bit was unset, + // any other combination will trap. + // + // [x1, x0] + Op::AssertEq, + ], + span, + ); } /// Truncate this i128 value to a felt value @@ -163,9 +171,9 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128, that is left up to the caller. #[inline] - pub fn trunc_i128_to_felt(&mut self) { - self.emit_n(2, Op::Drop); - self.trunc_int64_to_felt(); + pub fn trunc_i128_to_felt(&mut self, span: SourceSpan) { + self.emit_n(2, Op::Drop, span); + self.trunc_int64_to_felt(span); } /// Truncate this i128 value to N bits, where N is <= 64 @@ -177,17 +185,17 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i128 value, that is left up to the caller. #[inline] - pub fn trunc_i128(&mut self, n: u32) { + pub fn trunc_i128(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 64); match n { 64 => { - self.emit_n(2, Op::Drop); + self.emit_n(2, Op::Drop, span); } 32 => { - self.emit_n(3, Op::Drop); + self.emit_n(3, Op::Drop, span); } n => { - self.trunc_int32(n); + self.trunc_int32(n, span); } } } @@ -195,22 +203,25 @@ impl<'a> OpEmitter<'a> { /// Pop two i128 values, `b` and `a`, off the operand stack, and place the result of `a == b` on /// the stack. #[inline] - pub fn eq_i128(&mut self) { - self.emit_all(&[ - Op::Eqw, - // Move the boolean below the elements we're going to drop - Op::Movdn(8), - // Drop both i128 values - Op::Dropw, - Op::Dropw, - ]); + pub fn eq_i128(&mut self, span: SourceSpan) { + self.emit_all( + &[ + Op::Eqw, + // Move the boolean below the elements we're going to drop + Op::Movdn(8), + // Drop both i128 values + Op::Dropw, + Op::Dropw, + ], + span, + ); } /// Pop two i128 values, `b` and `a`, off the operand stack, and place the result of `a == b` on /// the stack. #[inline] - pub fn neq_i128(&mut self) { - self.eq_i128(); - self.emit(Op::Not); + pub fn neq_i128(&mut self, span: SourceSpan) { + self.eq_i128(span); + self.emit(Op::Not, span); } } diff --git a/codegen/masm/src/codegen/emit/int32.rs b/codegen/masm/src/codegen/emit/int32.rs index 233fe0d33..3fcf06ff8 100644 --- a/codegen/masm/src/codegen/emit/int32.rs +++ b/codegen/masm/src/codegen/emit/int32.rs @@ -1,4 +1,4 @@ -use midenc_hir::{Felt, FieldElement, Overflow}; +use midenc_hir::{Felt, FieldElement, Overflow, SourceSpan}; use super::{felt, OpEmitter}; use crate::masm::Op; @@ -18,8 +18,8 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a & mask, ..]` #[inline] - pub fn const_mask_u32(&mut self, mask: u32) { - self.emit_all(&[Op::PushU32(mask), Op::U32And]); + pub fn const_mask_u32(&mut self, mask: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(mask), Op::U32And], span); } /// Emits code to apply a 32-bit mask, `mask`, to a u32 value, `input`. @@ -34,8 +34,8 @@ impl<'a> OpEmitter<'a> { /// /// `[mask, input, ..] => [input & mask, input]` #[inline] - pub fn mask_u32(&mut self) { - self.emit_all(&[Op::Dup(1), Op::U32And]); + pub fn mask_u32(&mut self, span: SourceSpan) { + self.emit_all(&[Op::Dup(1), Op::U32And], span); } /// Emits code to check if all bits of `flags` are set in the u32 value on top of the stack. @@ -49,10 +49,10 @@ impl<'a> OpEmitter<'a> { /// /// `[a, ..] => [a & flags == flags, a]` #[inline] - pub fn is_const_flag_set_u32(&mut self, flags: u32) { - self.emit(Op::Dup(0)); - self.const_mask_u32(flags); - self.emit(Op::EqImm(Felt::new(flags as u64))); + pub fn is_const_flag_set_u32(&mut self, flags: u32, span: SourceSpan) { + self.emit(Op::Dup(0), span); + self.const_mask_u32(flags, span); + self.emit(Op::EqImm(Felt::new(flags as u64)), span); } /// Emits code to check if all bits of `mask` are set in `input`. @@ -67,13 +67,16 @@ impl<'a> OpEmitter<'a> { /// /// `[mask, input, ..] => [input & mask == mask, input]` #[inline] - pub fn is_flag_set_u32(&mut self) { - self.emit_all(&[ - Op::Dup(1), // [input, mask, input] - Op::Dup(1), // [mask, input, mask, input] - Op::U32And, // [input & mask, mask, input] - Op::Eq, // [input & mask == mask, input] - ]); + pub fn is_flag_set_u32(&mut self, span: SourceSpan) { + self.emit_all( + &[ + Op::Dup(1), // [input, mask, input] + Op::Dup(1), // [mask, input, mask, input] + Op::U32And, // [input & mask, mask, input] + Op::Eq, // [input & mask == mask, input] + ], + span, + ); } /// Check if a 32-bit integer value on the operand stack has its sign bit set. @@ -82,17 +85,17 @@ impl<'a> OpEmitter<'a> { /// /// See `is_const_flag_set` for semantics and stack effects. #[inline] - pub fn is_signed_int32(&mut self) { - self.is_const_flag_set_u32(SIGN_BIT); + pub fn is_signed_int32(&mut self, span: SourceSpan) { + self.is_const_flag_set_u32(SIGN_BIT, span); } /// Check if a 32-bit integer value on the operand stack does not have its sign bit set. /// /// The value on top of the stack IS NOT consumed. #[inline(always)] - pub fn is_unsigned_int32(&mut self) { - self.is_signed_int32(); - self.emit(Op::Not); + pub fn is_unsigned_int32(&mut self, span: SourceSpan) { + self.is_signed_int32(span); + self.emit(Op::Not, span); } /// Emits code to assert that a 32-bit value on the operand stack has the i32 sign bit set. @@ -101,9 +104,9 @@ impl<'a> OpEmitter<'a> { /// /// See `is_signed` for semantics and stack effects of the signedness check. #[inline] - pub fn assert_signed_int32(&mut self) { - self.is_signed_int32(); - self.emit(Op::Assert); + pub fn assert_signed_int32(&mut self, span: SourceSpan) { + self.is_signed_int32(span); + self.emit(Op::Assert, span); } /// Emits code to assert that a 32-bit value on the operand stack does not have the i32 sign bit @@ -113,21 +116,21 @@ impl<'a> OpEmitter<'a> { /// /// See `is_signed` for semantics and stack effects of the signedness check. #[inline] - pub fn assert_unsigned_int32(&mut self) { - self.is_signed_int32(); - self.emit(Op::Assertz); + pub fn assert_unsigned_int32(&mut self, span: SourceSpan) { + self.is_signed_int32(span); + self.emit(Op::Assertz, span); } /// Assert that the 32-bit value on the stack is a valid i32 value - pub fn assert_i32(&mut self) { + pub fn assert_i32(&mut self, span: SourceSpan) { // Copy the value on top of the stack - self.emit(Op::Dup(0)); + self.emit(Op::Dup(0), span); // Assert the value does not overflow i32::MAX or underflow i32::MIN // This can be checked by validating that when interpreted as a u32, // the value is <= i32::MIN, which is 1 more than i32::MAX. - self.push_i32(i32::MIN); - self.emit(Op::U32Lte); - self.emit(Op::Assert); + self.push_i32(i32::MIN, span); + self.emit(Op::U32Lte, span); + self.emit(Op::Assert, span); } /// Emits code to assert that a 32-bit value on the operand stack is equal to the given constant @@ -139,8 +142,8 @@ impl<'a> OpEmitter<'a> { /// /// `[input, ..] => [input, ..]` #[inline] - pub fn assert_eq_imm_u32(&mut self, value: u32) { - self.emit_all(&[Op::Dup(0), Op::EqImm(Felt::new(value as u64)), Op::Assert]); + pub fn assert_eq_imm_u32(&mut self, value: u32, span: SourceSpan) { + self.emit_all(&[Op::Dup(0), Op::EqImm(Felt::new(value as u64)), Op::Assert], span); } /// Emits code to assert that two 32-bit values, `expected` and `value`, on top of the operand @@ -152,8 +155,8 @@ impl<'a> OpEmitter<'a> { /// /// `[expected, input, ..] => [input, ..]` #[inline] - pub fn assert_eq_u32(&mut self) { - self.emit_all(&[Op::Dup(1), Op::AssertEq]); + pub fn assert_eq_u32(&mut self, span: SourceSpan) { + self.emit_all(&[Op::Dup(1), Op::AssertEq], span); } /// Emits code to select a constant u32 value, using the `n`th value on the operand @@ -164,15 +167,15 @@ impl<'a> OpEmitter<'a> { /// all three operands, placing only a single value back on the operand stack; the /// selected value, either `a` or `b`. Use `dup_select` if you would rather copy /// the conditional rather than move it. - pub fn mov_select_int32(&mut self, a: u32, b: u32, n: u8) { + pub fn mov_select_int32(&mut self, a: u32, b: u32, n: u8, span: SourceSpan) { assert_valid_stack_index!(n); // If the value we need will get pushed off the end of the stack, // bring it closer first, and adjust our `n` accordingly if n > 13 { - self.emit(Op::Movup(n)); - self.select_int32(a, b); + self.emit(Op::Movup(n), span); + self.select_int32(a, b, span); } else { - self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Movup(n + 2), Op::Cdrop]); + self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Movup(n + 2), Op::Cdrop], span); } } @@ -183,15 +186,15 @@ impl<'a> OpEmitter<'a> { /// /// Moves `c` to the top of the stack, where `c` is the `n`th value on the operand stack, /// then applies `select`. - pub fn dup_select_int32(&mut self, a: u32, b: u32, n: u8) { + pub fn dup_select_int32(&mut self, a: u32, b: u32, n: u8, span: SourceSpan) { assert_valid_stack_index!(n); // If the value we need will get pushed off the end of the stack, // bring it closer first, and adjust our `n` accordingly if n > 13 { - self.emit(Op::Dup(n)); - self.select_int32(a, b); + self.emit(Op::Dup(n), span); + self.select_int32(a, b, span); } else { - self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Dup(n + 2), Op::Cdrop]); + self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Dup(n + 2), Op::Cdrop], span); } } @@ -200,98 +203,110 @@ impl<'a> OpEmitter<'a> { /// # Stack Effects /// /// `[c, a, b, ..] => [d, ..] where d is c == 1 ? a : b` - pub fn select_int32(&mut self, a: u32, b: u32) { - self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Movup(2), Op::Cdrop]); + pub fn select_int32(&mut self, a: u32, b: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(b), Op::PushU32(a), Op::Movup(2), Op::Cdrop], span); } /// Convert an i32/u32 value on the stack to a signed N-bit integer value /// /// Execution traps if the value cannot fit in the signed N-bit range. - pub fn int32_to_int(&mut self, n: u32) { + pub fn int32_to_int(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); // Push is_signed on the stack - self.is_signed_int32(); + self.is_signed_int32(span); // Pop the is_signed flag, and replace it with a selected mask // for the upper reserved bits of the N-bit range let reserved = 32 - n; // Add one bit to the reserved bits to represent the sign bit, // and subtract it from the shift to account for the loss let mask = (2u32.pow(reserved + 1) - 1) << (n - 1); - self.select_int32(mask, 0); - self.emit_all(&[ - // Copy the input to the top of the stack for the masking op - Op::Dup(1), - // Copy the mask value for the masking op - Op::Dup(1), - // Apply the mask - Op::U32And, - // Assert that the masked bits and the mask are equal - Op::AssertEq, - ]); + self.select_int32(mask, 0, span); + self.emit_all( + &[ + // Copy the input to the top of the stack for the masking op + Op::Dup(1), + // Copy the mask value for the masking op + Op::Dup(1), + // Apply the mask + Op::U32And, + // Assert that the masked bits and the mask are equal + Op::AssertEq, + ], + span, + ); } /// Convert an i32/u32 value on the stack to a signed N-bit integer value /// /// Places a boolean on top of the stack indicating if the conversion was successful - pub fn try_int32_to_int(&mut self, n: u32) { + pub fn try_int32_to_int(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); // Push is_signed on the stack - self.is_signed_int32(); + self.is_signed_int32(span); // Pop the is_signed flag, and replace it with a selected mask // for the upper reserved bits of the N-bit range let reserved = 32 - n; // Add one bit to the reserved bits to represent the sign bit, // and subtract it from the shift to account for the loss let mask = (2u32.pow(reserved + 1) - 1) << (n - 1); - self.select_int32(mask, 0); - self.emit_all(&[ - // Copy the input to the top of the stack for the masking op - Op::Dup(1), - // Copy the mask value for the masking op - Op::Dup(1), - // Apply the mask - Op::U32And, - // Assert that the masked bits and the mask are equal - Op::Eq, - ]); + self.select_int32(mask, 0, span); + self.emit_all( + &[ + // Copy the input to the top of the stack for the masking op + Op::Dup(1), + // Copy the mask value for the masking op + Op::Dup(1), + // Apply the mask + Op::U32And, + // Assert that the masked bits and the mask are equal + Op::Eq, + ], + span, + ); } /// Convert an i32/u32 value on the stack to an unsigned N-bit integer value /// /// Execution traps if the value cannot fit in the unsigned N-bit range. - pub fn int32_to_uint(&mut self, n: u32) { + pub fn int32_to_uint(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); // Mask the value and ensure that the unused bits above the N-bit range are 0 let reserved = 32 - n; let mask = (2u32.pow(reserved) - 1) << n; - self.emit_all(&[ - // Copy the input - Op::Dup(1), - // Apply the mask - Op::PushU32(mask), - Op::U32And, - // Assert the masked value is all 0s - Op::Assertz, - ]); + self.emit_all( + &[ + // Copy the input + Op::Dup(1), + // Apply the mask + Op::PushU32(mask), + Op::U32And, + // Assert the masked value is all 0s + Op::Assertz, + ], + span, + ); } /// Convert an i32/u32 value on the stack to an unsigned N-bit integer value /// /// Places a boolean on top of the stack indicating if the conversion was successful - pub fn try_int32_to_uint(&mut self, n: u32) { + pub fn try_int32_to_uint(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); // Mask the value and ensure that the unused bits above the N-bit range are 0 let reserved = 32 - n; let mask = (2u32.pow(reserved) - 1) << n; - self.emit_all(&[ - // Copy the input - Op::Dup(1), - // Apply the mask - Op::PushU32(mask), - Op::U32And, - // Assert the masked value is all 0s - Op::EqImm(Felt::ZERO), - ]); + self.emit_all( + &[ + // Copy the input + Op::Dup(1), + // Apply the mask + Op::PushU32(mask), + Op::U32And, + // Assert the masked value is all 0s + Op::EqImm(Felt::ZERO), + ], + span, + ); } /// Emit code to truncate a 32-bit value on top of the operand stack, to N bits, where N is <= @@ -302,12 +317,12 @@ impl<'a> OpEmitter<'a> { /// NOTE: This function does not validate the input as < 2^32, the caller is expected to /// validate this. #[inline] - pub fn trunc_int32(&mut self, n: u32) { + pub fn trunc_int32(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); // Mask out any bits between N and 32. let unused_bits = 32 - n; if unused_bits > 0 { - self.const_mask_u32(1 << ((32 - unused_bits) - 1)); + self.const_mask_u32(1 << ((32 - unused_bits) - 1), span); } } @@ -318,7 +333,7 @@ impl<'a> OpEmitter<'a> { /// NOTE: This operation does not check the sign bit, it is assumed the value is /// either an unsigned integer, or a non-negative signed integer. #[inline] - pub fn zext_int32(&mut self, n: u32) { + pub fn zext_int32(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 32); // Only values larger than 32 bits require padding if n <= 32 { @@ -327,7 +342,7 @@ impl<'a> OpEmitter<'a> { let num_bits = n % 32; let num_elements = (n / 32) + (num_bits > 0) as u32; let needed = num_elements - 1; - self.emit_n(needed as usize, Op::PushU32(0)); + self.emit_n(needed as usize, Op::PushU32(0), span); } /// Emit code to sign-extend a signed 32-bit value to N bits, where N <= 128 @@ -338,11 +353,11 @@ impl<'a> OpEmitter<'a> { /// assumed the value is an i32, it is up to the caller to ensure this is a valid /// operation to perform on the input. #[inline] - pub fn sext_int32(&mut self, n: u32) { + pub fn sext_int32(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 32); - self.is_signed_int32(); - self.select_int32(u32::MAX, 0); - self.pad_int32(n); + self.is_signed_int32(span); + self.select_int32(u32::MAX, 0, span); + self.pad_int32(n, span); } /// Emit code to pad a 32-bit value out to N bits, where N >= 32. @@ -356,26 +371,26 @@ impl<'a> OpEmitter<'a> { /// The padding value will be duplicated for each additional 32-bit limb needed to /// ensure that there are enough limbs on the stack to represent an N-bit integer. #[inline] - pub fn pad_int32(&mut self, n: u32) { + pub fn pad_int32(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 32); // We need one element for each 32-bit limb let num_elements = n / 32; // We already have the input u32, as well as the pad value, so deduct // those elements from the number needed. let needed = num_elements.saturating_sub(2); - self.emit_n(needed as usize, Op::Dup(0)); + self.emit_n(needed as usize, Op::Dup(0), span); } /// Push a u32 value on the stack #[inline(always)] - pub fn push_u32(&mut self, i: u32) { - self.emit(Op::PushU32(i)); + pub fn push_u32(&mut self, i: u32, span: SourceSpan) { + self.emit(Op::PushU32(i), span); } /// Push a i32 value on the stack #[inline(always)] - pub fn push_i32(&mut self, i: i32) { - self.emit(Op::PushU32(i as u32)); + pub fn push_i32(&mut self, i: i32, span: SourceSpan) { + self.emit(Op::PushU32(i as u32), span); } /// This is the inverse operation of the Miden VM `u32split` instruction. @@ -383,33 +398,41 @@ impl<'a> OpEmitter<'a> { /// This takes two 32-bit limbs, and produces a felt. /// /// NOTE: It is expected that the caller has validated that the limbs are valid u32 values. - pub fn u32unsplit(&mut self) { - self.emit_all(&[Op::MulImm(felt::U32_FIELD_MODULUS), Op::Add]); + pub fn u32unsplit(&mut self, span: SourceSpan) { + self.emit_all(&[Op::MulImm(felt::U32_FIELD_MODULUS), Op::Add], span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a + b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. #[inline(always)] - pub fn add_u32(&mut self, overflow: Overflow) { - self.emit(match overflow { - Overflow::Unchecked => Op::Add, - Overflow::Checked => return self.emit_all(&[Op::Add, Op::U32Assert]), - Overflow::Wrapping => Op::U32WrappingAdd, - Overflow::Overflowing => Op::U32OverflowingAdd, - }); + pub fn add_u32(&mut self, overflow: Overflow, span: SourceSpan) { + self.emit( + match overflow { + Overflow::Unchecked => Op::Add, + Overflow::Checked => return self.emit_all(&[Op::Add, Op::U32Assert], span), + Overflow::Wrapping => Op::U32WrappingAdd, + Overflow::Overflowing => Op::U32OverflowingAdd, + }, + span, + ); } /// Pops two i32 values off the stack, `b` and `a`, and performs `a + b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. #[inline(always)] - pub fn add_i32(&mut self, overflow: Overflow) { - self.emit(match overflow { - Overflow::Unchecked | Overflow::Wrapping => Op::U32WrappingAdd, - Overflow::Checked => Op::Exec("intrinsics::i32::checked_add".parse().unwrap()), - Overflow::Overflowing => Op::Exec("intrinsics::i32::overflowing_add".parse().unwrap()), - }) + pub fn add_i32(&mut self, overflow: Overflow, span: SourceSpan) { + self.emit( + match overflow { + Overflow::Unchecked | Overflow::Wrapping => Op::U32WrappingAdd, + Overflow::Checked => Op::Exec("intrinsics::i32::checked_add".parse().unwrap()), + Overflow::Overflowing => { + Op::Exec("intrinsics::i32::overflowing_add".parse().unwrap()) + } + }, + span, + ) } /// Pops a u32 value off the stack, `a`, and performs `a + `. @@ -418,19 +441,23 @@ impl<'a> OpEmitter<'a> { /// /// Adding zero is a no-op. #[inline] - pub fn add_imm_u32(&mut self, imm: u32, overflow: Overflow) { + pub fn add_imm_u32(&mut self, imm: u32, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } - self.emit(match overflow { - Overflow::Unchecked if imm == 1 => Op::Incr, - Overflow::Unchecked => Op::AddImm(Felt::new(imm as u64)), - Overflow::Checked => { - return self.emit_all(&[Op::AddImm(Felt::new(imm as u64)), Op::U32Assert]); - } - Overflow::Wrapping => Op::U32WrappingAddImm(imm), - Overflow::Overflowing => Op::U32OverflowingAddImm(imm), - }); + self.emit( + match overflow { + Overflow::Unchecked if imm == 1 => Op::Incr, + Overflow::Unchecked => Op::AddImm(Felt::new(imm as u64)), + Overflow::Checked => { + return self + .emit_all(&[Op::AddImm(Felt::new(imm as u64)), Op::U32Assert], span); + } + Overflow::Wrapping => Op::U32WrappingAddImm(imm), + Overflow::Overflowing => Op::U32OverflowingAddImm(imm), + }, + span, + ); } /// Pops a i32 value off the stack, `a`, and performs `a + `. @@ -439,50 +466,61 @@ impl<'a> OpEmitter<'a> { /// /// Adding zero is a no-op. #[inline] - pub fn add_imm_i32(&mut self, imm: i32, overflow: Overflow) { + pub fn add_imm_i32(&mut self, imm: i32, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.add_imm_u32(imm as u32, overflow), + Overflow::Unchecked | Overflow::Wrapping => { + self.add_imm_u32(imm as u32, overflow, span) + } Overflow::Checked => { - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_add".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::checked_add".parse().unwrap()), + ], + span, + ); } - Overflow::Overflowing => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::overflowing_add".parse().unwrap()), - ]), + Overflow::Overflowing => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::overflowing_add".parse().unwrap()), + ], + span, + ), } } /// Pops two u32 values off the stack, `b` and `a`, and performs `a - b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn sub_u32(&mut self, overflow: Overflow) { - self.emit(match overflow { - Overflow::Unchecked => Op::Sub, - Overflow::Checked => { - return self.emit_all(&[Op::Sub, Op::U32Assert]); - } - Overflow::Wrapping => Op::U32WrappingSub, - Overflow::Overflowing => Op::U32OverflowingSub, - }); + pub fn sub_u32(&mut self, overflow: Overflow, span: SourceSpan) { + self.emit( + match overflow { + Overflow::Unchecked => Op::Sub, + Overflow::Checked => { + return self.emit_all(&[Op::Sub, Op::U32Assert], span); + } + Overflow::Wrapping => Op::U32WrappingSub, + Overflow::Overflowing => Op::U32OverflowingSub, + }, + span, + ); } /// Pops two i32 values off the stack, `b` and `a`, and performs `a - b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn sub_i32(&mut self, overflow: Overflow) { + pub fn sub_i32(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.sub_u32(overflow), + Overflow::Unchecked | Overflow::Wrapping => self.sub_u32(overflow, span), Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i32::checked_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i32::checked_sub".parse().unwrap()), span) } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i32::overflowing_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i32::overflowing_sub".parse().unwrap()), span) } } } @@ -493,18 +531,21 @@ impl<'a> OpEmitter<'a> { /// /// Subtracting zero is a no-op. #[inline] - pub fn sub_imm_u32(&mut self, imm: u32, overflow: Overflow) { + pub fn sub_imm_u32(&mut self, imm: u32, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } - self.emit(match overflow { - Overflow::Unchecked => Op::SubImm(Felt::new(imm as u64)), - Overflow::Checked => { - return self.emit_all(&[Op::SubImm(Felt::new(imm as u64)), Op::U32Assert]) - } - Overflow::Wrapping => Op::U32WrappingSubImm(imm), - Overflow::Overflowing => Op::U32OverflowingSubImm(imm), - }); + self.emit( + match overflow { + Overflow::Unchecked => Op::SubImm(Felt::new(imm as u64)), + Overflow::Checked => { + return self.emit_all(&[Op::SubImm(Felt::new(imm as u64)), Op::U32Assert], span) + } + Overflow::Wrapping => Op::U32WrappingSubImm(imm), + Overflow::Overflowing => Op::U32OverflowingSubImm(imm), + }, + span, + ); } /// Pops a i32 value off the stack, `a`, and performs `a - `. @@ -513,48 +554,59 @@ impl<'a> OpEmitter<'a> { /// /// Subtracting zero is a no-op. #[inline] - pub fn sub_imm_i32(&mut self, imm: i32, overflow: Overflow) { + pub fn sub_imm_i32(&mut self, imm: i32, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.sub_imm_u32(imm as u32, overflow), - Overflow::Checked => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_sub".parse().unwrap()), - ]), - Overflow::Overflowing => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::overflowing_sub".parse().unwrap()), - ]), + Overflow::Unchecked | Overflow::Wrapping => { + self.sub_imm_u32(imm as u32, overflow, span) + } + Overflow::Checked => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::checked_sub".parse().unwrap()), + ], + span, + ), + Overflow::Overflowing => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::overflowing_sub".parse().unwrap()), + ], + span, + ), } } /// Pops two u32 values off the stack, `b` and `a`, and performs `a * b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn mul_u32(&mut self, overflow: Overflow) { - self.emit(match overflow { - Overflow::Unchecked => Op::Mul, - Overflow::Checked => return self.emit_all(&[Op::Mul, Op::U32Assert]), - Overflow::Wrapping => Op::U32WrappingMul, - Overflow::Overflowing => Op::U32OverflowingMul, - }); + pub fn mul_u32(&mut self, overflow: Overflow, span: SourceSpan) { + self.emit( + match overflow { + Overflow::Unchecked => Op::Mul, + Overflow::Checked => return self.emit_all(&[Op::Mul, Op::U32Assert], span), + Overflow::Wrapping => Op::U32WrappingMul, + Overflow::Overflowing => Op::U32OverflowingMul, + }, + span, + ); } /// Pops two i32 values off the stack, `b` and `a`, and performs `a * b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn mul_i32(&mut self, overflow: Overflow) { + pub fn mul_i32(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("intrinsics::i32::wrapping_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i32::wrapping_mul".parse().unwrap()), span) } Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i32::checked_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i32::checked_mul".parse().unwrap()), span) } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i32::overflowing_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i32::overflowing_mul".parse().unwrap()), span) } } } @@ -568,21 +620,27 @@ impl<'a> OpEmitter<'a> { /// /// Multiplying by one is a no-op. #[inline] - pub fn mul_imm_u32(&mut self, imm: u32, overflow: Overflow) { + pub fn mul_imm_u32(&mut self, imm: u32, overflow: Overflow, span: SourceSpan) { match imm { 0 => { - self.emit_all(&[Op::Drop, Op::PushU32(0)]); + self.emit_all(&[Op::Drop, Op::PushU32(0)], span); } 1 => (), imm => { - self.emit(match overflow { - Overflow::Unchecked => Op::MulImm(Felt::new(imm as u64)), - Overflow::Checked => { - return self.emit_all(&[Op::MulImm(Felt::new(imm as u64)), Op::U32Assert]) - } - Overflow::Wrapping => Op::U32WrappingMulImm(imm), - Overflow::Overflowing => Op::U32OverflowingMulImm(imm), - }); + self.emit( + match overflow { + Overflow::Unchecked => Op::MulImm(Felt::new(imm as u64)), + Overflow::Checked => { + return self.emit_all( + &[Op::MulImm(Felt::new(imm as u64)), Op::U32Assert], + span, + ) + } + Overflow::Wrapping => Op::U32WrappingMulImm(imm), + Overflow::Overflowing => Op::U32OverflowingMulImm(imm), + }, + span, + ); } } } @@ -596,25 +654,34 @@ impl<'a> OpEmitter<'a> { /// /// Multiplying by one is a no-op. #[inline] - pub fn mul_imm_i32(&mut self, imm: i32, overflow: Overflow) { + pub fn mul_imm_i32(&mut self, imm: i32, overflow: Overflow, span: SourceSpan) { match imm { 0 => { - self.emit_all(&[Op::Drop, Op::PushU32(0)]); + self.emit_all(&[Op::Drop, Op::PushU32(0)], span); } 1 => (), imm => match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::wrapping_mul".parse().unwrap()), - ]), - Overflow::Checked => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_mul".parse().unwrap()), - ]), - Overflow::Overflowing => self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::overflowing_mul".parse().unwrap()), - ]), + Overflow::Unchecked | Overflow::Wrapping => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::wrapping_mul".parse().unwrap()), + ], + span, + ), + Overflow::Checked => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::checked_mul".parse().unwrap()), + ], + span, + ), + Overflow::Overflowing => self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::overflowing_mul".parse().unwrap()), + ], + span, + ), }, } } @@ -622,15 +689,15 @@ impl<'a> OpEmitter<'a> { /// Pops two u32 values off the stack, `b` and `a`, and performs `a / b`. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. - pub fn checked_div_u32(&mut self) { - self.emit_all(&[Op::U32Div, Op::U32Assert]); + pub fn checked_div_u32(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Div, Op::U32Assert], span); } /// Pops two i32 values off the stack, `b` and `a`, and performs `a / b`. /// /// This operation is checked, so if the operands or result are not valid i32, execution traps. - pub fn checked_div_i32(&mut self) { - self.emit(Op::Exec("intrinsics::i32::checked_div".parse().unwrap())); + pub fn checked_div_i32(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i32::checked_div".parse().unwrap()), span); } /// Pops a u32 value off the stack, `a`, and performs `a / `. @@ -638,9 +705,9 @@ impl<'a> OpEmitter<'a> { /// This function will panic if the divisor is zero. /// /// This operation is checked, so if the operand or result are not valid u32, execution traps. - pub fn checked_div_imm_u32(&mut self, imm: u32) { + pub fn checked_div_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32DivImm(imm), Op::U32Assert]); + self.emit_all(&[Op::U32DivImm(imm), Op::U32Assert], span); } /// Pops a i32 value off the stack, `a`, and performs `a / `. @@ -648,34 +715,37 @@ impl<'a> OpEmitter<'a> { /// This function will panic if the divisor is zero. /// /// This operation is checked, so if the operand or result are not valid i32, execution traps. - pub fn checked_div_imm_i32(&mut self, imm: i32) { + pub fn checked_div_imm_i32(&mut self, imm: i32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::checked_div".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::PushU32(imm as u32), + Op::Exec("intrinsics::i32::checked_div".parse().unwrap()), + ], + span, + ); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a / b`. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 - pub fn unchecked_div_u32(&mut self) { - self.emit(Op::U32Div); + pub fn unchecked_div_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Div, span); } /// Pops a u32 value off the stack, `a`, and performs `a / `. /// /// This function will panic if the divisor is zero. - pub fn unchecked_div_imm_u32(&mut self, imm: u32) { + pub fn unchecked_div_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32DivImm(imm)); + self.emit(Op::U32DivImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a % b`. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. - pub fn checked_mod_u32(&mut self) { - self.emit_all(&[Op::U32Mod, Op::U32Assert]); + pub fn checked_mod_u32(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Mod, Op::U32Assert], span); } /// Pops a u32 value off the stack, `a`, and performs `a % `. @@ -683,105 +753,105 @@ impl<'a> OpEmitter<'a> { /// This function will panic if the divisor is zero. /// /// This operation is checked, so if the operand or result are not valid u32, execution traps. - pub fn checked_mod_imm_u32(&mut self, imm: u32) { + pub fn checked_mod_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32ModImm(imm), Op::U32Assert]); + self.emit_all(&[Op::U32ModImm(imm), Op::U32Assert], span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a % b`. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 - pub fn unchecked_mod_u32(&mut self) { - self.emit(Op::U32Mod); + pub fn unchecked_mod_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Mod, span); } /// Pops a u32 value off the stack, `a`, and performs `a % `. /// /// This function will panic if the divisor is zero. - pub fn unchecked_mod_imm_u32(&mut self, imm: u32) { + pub fn unchecked_mod_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32ModImm(imm)); + self.emit(Op::U32ModImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the /// stack. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. - pub fn checked_divmod_u32(&mut self) { - self.emit_all(&[Op::U32DivMod, Op::U32Assert]); + pub fn checked_divmod_u32(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32DivMod, Op::U32Assert], span); } /// Pops a u32 value off the stack, `a`, and pushes `a / `, then `a % ` on the stack. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. - pub fn checked_divmod_imm_u32(&mut self, imm: u32) { + pub fn checked_divmod_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit_all(&[Op::U32DivModImm(imm), Op::U32Assert]); + self.emit_all(&[Op::U32DivModImm(imm), Op::U32Assert], span); } /// Pops two u32 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the /// stack. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 - pub fn unchecked_divmod_u32(&mut self) { - self.emit(Op::U32DivMod); + pub fn unchecked_divmod_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32DivMod, span); } /// Pops a u32 value off the stack, `a`, and pushes `a / `, then `a % ` on the stack. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 - pub fn unchecked_divmod_imm_u32(&mut self, imm: u32) { + pub fn unchecked_divmod_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.emit(Op::U32DivModImm(imm)); + self.emit(Op::U32DivModImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a & b` /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn band_u32(&mut self) { - self.emit(Op::U32And); + pub fn band_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32And, span); } /// Pops a u32 value off the stack, `a`, and performs `a & ` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn band_imm_u32(&mut self, imm: u32) { - self.emit_all(&[Op::PushU32(imm), Op::U32And]); + pub fn band_imm_u32(&mut self, imm: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(imm), Op::U32And], span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a | b` /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn bor_u32(&mut self) { - self.emit(Op::U32Or); + pub fn bor_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Or, span); } /// Pops a u32 value off the stack, `a`, and performs `a | ` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn bor_imm_u32(&mut self, imm: u32) { - self.emit_all(&[Op::PushU32(imm), Op::U32Or]); + pub fn bor_imm_u32(&mut self, imm: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(imm), Op::U32Or], span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a ^ b` /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn bxor_u32(&mut self) { - self.emit(Op::U32Xor); + pub fn bxor_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Xor, span); } /// Pops a u32 value off the stack, `a`, and performs `a ^ ` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn bxor_imm_u32(&mut self, imm: u32) { - self.emit_all(&[Op::PushU32(imm), Op::U32Xor]); + pub fn bxor_imm_u32(&mut self, imm: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(imm), Op::U32Xor], span); } /// Pops a u32 value off the stack, `a`, and performs `!a` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn bnot_u32(&mut self) { - self.emit(Op::U32WrappingSubImm(-1i32 as u32)); + pub fn bnot_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32WrappingSubImm(-1i32 as u32), span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a << b` @@ -789,16 +859,16 @@ impl<'a> OpEmitter<'a> { /// Execution traps if `b` > 31. /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn shl_u32(&mut self) { - self.emit(Op::U32Shl); + pub fn shl_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Shl, span); } /// Pops a u32 value off the stack, `a`, and performs `a << ` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn shl_imm_u32(&mut self, imm: u32) { + pub fn shl_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit(Op::U32ShlImm(imm)); + self.emit(Op::U32ShlImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a >> b` @@ -806,8 +876,8 @@ impl<'a> OpEmitter<'a> { /// Execution traps if `b` > 31. /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn shr_u32(&mut self) { - self.emit(Op::U32Shr); + pub fn shr_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Shr, span); } /// Pops two i32 values off the stack, `b` and `a`, and performs `a >> b` @@ -815,27 +885,27 @@ impl<'a> OpEmitter<'a> { /// Execution traps if `b` > 31. /// /// This operation is checked, if the operands or result are not valid i32, execution traps. - pub fn shr_i32(&mut self) { - self.emit(Op::Exec("intrinsics::i32::checked_shr".parse().unwrap())); + pub fn shr_i32(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i32::checked_shr".parse().unwrap()), span); } /// Pops a u32 value off the stack, `a`, and performs `a >> ` /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn shr_imm_u32(&mut self, imm: u32) { + pub fn shr_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit(Op::U32ShrImm(imm)); + self.emit(Op::U32ShrImm(imm), span); } /// Pops a i32 value off the stack, `a`, and performs `a >> ` /// /// This operation is checked, if the operand or result are not valid i32, execution traps. - pub fn shr_imm_i32(&mut self, imm: u32) { + pub fn shr_imm_i32(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 32, "invalid shift value: must be < 32, got {imm}"); - self.emit_all(&[ - Op::PushU32(imm), - Op::Exec("intrinsics::i32::checked_shr".parse().unwrap()), - ]); + self.emit_all( + &[Op::PushU32(imm), Op::Exec("intrinsics::i32::checked_shr".parse().unwrap())], + span, + ); } /// Pops two u32 values off the stack, `b` and `a`, and rotates the bits of `a` left by `b` bits @@ -843,16 +913,16 @@ impl<'a> OpEmitter<'a> { /// Execution traps if `b` > 31. /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn rotl_u32(&mut self) { - self.emit(Op::U32Rotl); + pub fn rotl_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Rotl, span); } /// Pops a u32 value off the stack, `a`, and rotates the bits of `a` left by `imm` bits /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn rotl_imm_u32(&mut self, imm: u32) { + pub fn rotl_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 32, "invalid rotation value: must be < 32, got {imm}"); - self.emit(Op::U32RotlImm(imm)); + self.emit(Op::U32RotlImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and rotates the bits of `a` right by `b` @@ -861,81 +931,81 @@ impl<'a> OpEmitter<'a> { /// Execution traps if `b` > 31. /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn rotr_u32(&mut self) { - self.emit(Op::U32Rotr); + pub fn rotr_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Rotr, span); } /// Pops a u32 value off the stack, `a`, and rotates the bits of `a` right by `imm` bits /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn rotr_imm_u32(&mut self, imm: u32) { + pub fn rotr_imm_u32(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 32, "invalid rotation value: must be < 32, got {imm}"); - self.emit(Op::U32RotrImm(imm)); + self.emit(Op::U32RotrImm(imm), span); } /// Pops two u32 values off the stack, `b` and `a`, and puts the result of `min(a, b)` on the /// stack /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn min_u32(&mut self) { - self.emit(Op::U32Min); + pub fn min_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Min, span); } /// Pops two i32 values off the stack, `b` and `a`, and puts the result of `min(a, b)` on the /// stack /// /// This operation is checked, if the operands or result are not valid i32, execution traps. - pub fn min_i32(&mut self) { - self.emit(Op::Exec("intrinsics::i32::min".parse().unwrap())); + pub fn min_i32(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i32::min".parse().unwrap()), span); } /// Pops a u32 value off the stack, `a`, and puts the result of `min(a, imm)` on the stack /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn min_imm_u32(&mut self, imm: u32) { - self.emit_all(&[Op::PushU32(imm), Op::U32Min]); + pub fn min_imm_u32(&mut self, imm: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(imm), Op::U32Min], span); } /// Pops a i32 value off the stack, `a`, and puts the result of `min(a, imm)` on the stack /// /// This operation is checked, if the operand or result are not valid i32, execution traps. - pub fn min_imm_i32(&mut self, imm: i32) { - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::min".parse().unwrap()), - ]); + pub fn min_imm_i32(&mut self, imm: i32, span: SourceSpan) { + self.emit_all( + &[Op::PushU32(imm as u32), Op::Exec("intrinsics::i32::min".parse().unwrap())], + span, + ); } /// Pops two u32 values off the stack, `b` and `a`, and puts the result of `max(a, b)` on the /// stack /// /// This operation is checked, if the operands or result are not valid u32, execution traps. - pub fn max_u32(&mut self) { - self.emit(Op::U32Max); + pub fn max_u32(&mut self, span: SourceSpan) { + self.emit(Op::U32Max, span); } /// Pops two i32 values off the stack, `b` and `a`, and puts the result of `max(a, b)` on the /// stack /// /// This operation is checked, if the operands or result are not valid i32, execution traps. - pub fn max_i32(&mut self) { - self.emit(Op::Exec("intrinsics::i32::max".parse().unwrap())); + pub fn max_i32(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i32::max".parse().unwrap()), span); } /// Pops a u32 value off the stack, `a`, and puts the result of `max(a, imm)` on the stack /// /// This operation is checked, if the operand or result are not valid u32, execution traps. - pub fn max_imm_u32(&mut self, imm: u32) { - self.emit_all(&[Op::PushU32(imm), Op::U32Max]); + pub fn max_imm_u32(&mut self, imm: u32, span: SourceSpan) { + self.emit_all(&[Op::PushU32(imm), Op::U32Max], span); } /// Pops a i32 value off the stack, `a`, and puts the result of `max(a, imm)` on the stack /// /// This operation is checked, if the operand or result are not valid i32, execution traps. - pub fn max_imm_i32(&mut self, imm: i32) { - self.emit_all(&[ - Op::PushU32(imm as u32), - Op::Exec("intrinsics::i32::max".parse().unwrap()), - ]); + pub fn max_imm_i32(&mut self, imm: i32, span: SourceSpan) { + self.emit_all( + &[Op::PushU32(imm as u32), Op::Exec("intrinsics::i32::max".parse().unwrap())], + span, + ); } } diff --git a/codegen/masm/src/codegen/emit/int64.rs b/codegen/masm/src/codegen/emit/int64.rs index 2de91cca6..7bde1cc44 100644 --- a/codegen/masm/src/codegen/emit/int64.rs +++ b/codegen/masm/src/codegen/emit/int64.rs @@ -1,4 +1,4 @@ -use midenc_hir::{Felt, FieldElement, Overflow}; +use midenc_hir::{Felt, FieldElement, Overflow, SourceSpan}; use super::{OpEmitter, P}; use crate::masm::{self as masm, Op}; @@ -8,14 +8,14 @@ impl<'a> OpEmitter<'a> { /// Convert a u64 value to felt. /// /// This operation will assert at runtime if the value is larger than the felt field. - pub fn u64_to_felt(&mut self) { + pub fn u64_to_felt(&mut self, span: SourceSpan) { // Copy the input operand for the check - self.copy_int64(); + self.copy_int64(span); // Assert that value is <= P, then unsplit the limbs to get a felt - self.push_u64(P); - self.lt_u64(); - self.emit(Op::Assert); - self.u32unsplit(); + self.push_u64(P, span); + self.lt_u64(span); + self.emit(Op::Assert, span); + self.u32unsplit(span); } /// Convert a i64 value to felt. @@ -23,49 +23,58 @@ impl<'a> OpEmitter<'a> { /// This operation will assert at runtime if the value is negative, or larger than the felt /// field. #[inline] - pub fn i64_to_felt(&mut self) { - self.u64_to_felt(); + pub fn i64_to_felt(&mut self, span: SourceSpan) { + self.u64_to_felt(span); } /// Convert a u64 value to an unsigned N-bit integer, where N <= 32 /// /// Conversion will trap if the input value is too large to fit in an N-bit integer. - pub fn u64_to_uint(&mut self, n: u32) { - self.emit_all(&[ - // Assert hi bits are zero - Op::Assertz, - // Check that the remaining bits fit in range - Op::Dup(0), - Op::Push(Felt::new(2u64.pow(n) - 1)), - Op::U32Lte, - Op::Assert, - ]); + pub fn u64_to_uint(&mut self, n: u32, span: SourceSpan) { + self.emit_all( + &[ + // Assert hi bits are zero + Op::Assertz, + // Check that the remaining bits fit in range + Op::Dup(0), + Op::Push(Felt::new(2u64.pow(n) - 1)), + Op::U32Lte, + Op::Assert, + ], + span, + ); } /// Convert an i64 value to a signed N-bit integer, where N <= 32 /// /// Conversion will trap if the input value is too large to fit in an N-bit integer. - pub fn i64_to_int(&mut self, n: u32) { - self.emit_all(&[ - // Assert hi bits are all zero or all one - // [x_hi, x_hi, x_lo] - Op::Dup(0), - // [is_unsigned, x_hi, x_lo] - Op::EqImm(Felt::ZERO), - // [is_unsigned, is_unsigned, ..] - Op::Dup(0), - // [is_unsigned, x_hi, is_unsigned, x_lo] - Op::Movdn(2), - ]); + pub fn i64_to_int(&mut self, n: u32, span: SourceSpan) { + self.emit_all( + &[ + // Assert hi bits are all zero or all one + // [x_hi, x_hi, x_lo] + Op::Dup(0), + // [is_unsigned, x_hi, x_lo] + Op::EqImm(Felt::ZERO), + // [is_unsigned, is_unsigned, ..] + Op::Dup(0), + // [is_unsigned, x_hi, is_unsigned, x_lo] + Op::Movdn(2), + ], + span, + ); // Select all 0s if is_unsigned is true, else all 1s // [mask, x_hi, is_unsigned, x_lo] - self.select_int32(0, u32::MAX); - self.emit_all(&[ - // [is_unsigned, x_lo] - Op::AssertEq, - // [x_lo, is_unsigned, x_lo] - Op::Dup(1), - ]); + self.select_int32(0, u32::MAX, span); + self.emit_all( + &[ + // [is_unsigned, x_lo] + Op::AssertEq, + // [x_lo, is_unsigned, x_lo] + Op::Dup(1), + ], + span, + ); // Select mask for remaining sign bits // // The mask should cover the u64 bits which must be set to 1 if @@ -77,19 +86,22 @@ impl<'a> OpEmitter<'a> { // integer, there are N-1 such bits. let value_bits = (2u64.pow(n - 1) - 1) as u32; // [sign_bits, is_unsigned, x_lo] - self.const_mask_u32(!value_bits); - self.emit_all(&[ - // [sign_bits, sign_bits, ..] - Op::Dup(0), - // [0, sign_bits, sign_bits, is_unsigned, x_lo] - Op::PushU32(0), - // [is_unsigned, 0, sign_bits, sign_bits, x_lo] - Op::Movup(3), - // [expected_sign_bits, sign_bits, x_lo] - Op::Cdrop, - // [x_lo] - Op::AssertEq, - ]); + self.const_mask_u32(!value_bits, span); + self.emit_all( + &[ + // [sign_bits, sign_bits, ..] + Op::Dup(0), + // [0, sign_bits, sign_bits, is_unsigned, x_lo] + Op::PushU32(0), + // [is_unsigned, 0, sign_bits, sign_bits, x_lo] + Op::Movup(3), + // [expected_sign_bits, sign_bits, x_lo] + Op::Cdrop, + // [x_lo] + Op::AssertEq, + ], + span, + ); } /// Truncate a i64/u64 value to a felt value @@ -108,9 +120,9 @@ impl<'a> OpEmitter<'a> { /// /// NOTE: This function does not validate the i64/u64, the caller is expected to /// have already validated that the top of the stack holds a valid value of this type. - #[inline] - pub fn trunc_int64_to_felt(&mut self) { - self.u32unsplit() + #[inline(always)] + pub fn trunc_int64_to_felt(&mut self, span: SourceSpan) { + self.u32unsplit(span) } /// Truncate this 64-bit value to N bits, where N is <= 32 @@ -120,69 +132,70 @@ impl<'a> OpEmitter<'a> { /// NOTE: This function does not validate the i64/u64, the caller is expected to /// have already validated that the top of the stack holds a valid value of that type. #[inline] - pub fn trunc_int64(&mut self, n: u32) { + pub fn trunc_int64(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); - self.emit(Op::Drop); + self.emit(Op::Drop, span); match n { 32 => (), - n => self.trunc_int32(n), + n => self.trunc_int32(n, span), } } /// Sign-extend a 64-bit value to an signed N-bit integer, where N >= 128 - pub fn sext_int64(&mut self, n: u32) { + pub fn sext_int64(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 128, 256); - self.is_signed_int64(); + self.is_signed_int64(span); // Select the extension bits - self.select_int32(u32::MAX, 0); + self.select_int32(u32::MAX, 0, span); // Pad out the missing bits // // Deduct 32 bits to account for the difference between u32 and u64 - self.pad_int32(n - 32); + self.pad_int32(n - 32, span); } /// Zero-extend a 64-bit value to N-bits, where N >= 64 - pub fn zext_int64(&mut self, n: u32) { + pub fn zext_int64(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 128, 256); // Pad out the missing bits // // Deduct 32 bits to account for the difference between u32 and u64 - self.zext_int32(n - 32); + self.zext_int32(n - 32, span); } /// Assert that there is a valid 64-bit integer value on the operand stack - pub fn assert_int64(&mut self) { - self.emit(Op::U32Assert2); + pub fn assert_int64(&mut self, span: SourceSpan) { + self.emit(Op::U32Assert2, span); } /// Checks if the 64-bit value on the stack has its sign bit set. - #[inline] - pub fn is_signed_int64(&mut self) { - self.is_signed_int32() + #[inline(always)] + pub fn is_signed_int64(&mut self, span: SourceSpan) { + self.is_signed_int32(span) } /// Assert that the 64-bit value on the stack does not have its sign bit set. - pub fn assert_unsigned_int64(&mut self) { + #[inline(always)] + pub fn assert_unsigned_int64(&mut self, span: SourceSpan) { // Assert that the sign bit is unset - self.assert_unsigned_int32() + self.assert_unsigned_int32(span) } /// Assert that the 64-bit value on the stack is a valid i64 value - pub fn assert_i64(&mut self) { + pub fn assert_i64(&mut self, span: SourceSpan) { // Copy the value on top of the stack - self.copy_int64(); + self.copy_int64(span); // Assert the value does not overflow i64::MAX or underflow i64::MIN // This can be checked by validating that when interpreted as a u64, // the value is <= i64::MIN, which is 1 more than i64::MAX. - self.push_i64(i64::MIN); - self.lte_u64(); - self.emit(Op::Assert); + self.push_i64(i64::MIN, span); + self.lte_u64(span); + self.emit(Op::Assert, span); } /// Duplicate the i64/u64 value on top of the stack #[inline(always)] - pub fn copy_int64(&mut self) { - self.copy_int64_from(0) + pub fn copy_int64(&mut self, span: SourceSpan) { + self.copy_int64_from(0, span) } /// Duplicate a i64/u64 value to the top of the stack @@ -190,10 +203,10 @@ impl<'a> OpEmitter<'a> { /// The value `n` must be a valid stack index, and may not reference the last stack slot, /// or this function will panic. #[inline(always)] - pub fn copy_int64_from(&mut self, n: u8) { + pub fn copy_int64_from(&mut self, n: u8, span: SourceSpan) { assert_valid_stack_index!(n + 1); // copy limbs such that the order is preserved - self.emit_n(2, Op::Dup(n + 1)); + self.emit_n(2, Op::Dup(n + 1), span); } /// Move a 64-bit value to the top of the stack, i.e. `movup(N)` for 64-bit values @@ -203,164 +216,167 @@ impl<'a> OpEmitter<'a> { /// /// A value of `0` has no effect. #[inline] - pub fn move_int64_up(&mut self, n: u8) { + pub fn move_int64_up(&mut self, n: u8, span: SourceSpan) { assert_valid_stack_index!(n + 1); match n { 0 => (), 1 => { // Move the top of the stack past the 64 bit value - self.emit(Op::Movdn(2)); + self.emit(Op::Movdn(2), span); } n => { - self.emit_all(&[ - // Move the low 32 bits to the top - Op::Movup(n + 1), - // Move the high 32 bits to the top - Op::Movup(n + 1), - ]); + self.emit_all( + &[ + // Move the low 32 bits to the top + Op::Movup(n + 1), + // Move the high 32 bits to the top + Op::Movup(n + 1), + ], + span, + ); } } } /// Pushes a literal i64 value on the operand stack #[inline(always)] - pub fn push_i64(&mut self, value: i64) { - self.push_u64(value as u64); + pub fn push_i64(&mut self, value: i64, span: SourceSpan) { + self.push_u64(value as u64, span); } /// Pushes a literal u64 value on the operand stack #[inline] - pub fn push_u64(&mut self, value: u64) { + pub fn push_u64(&mut self, value: u64, span: SourceSpan) { let (hi, lo) = to_raw_parts(value); - from_raw_parts(lo, hi, self.current_block()); + from_raw_parts(lo, hi, self.current_block(), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a < b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn lt_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::lt".parse().unwrap())); + pub fn lt_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::lt".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `a < b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn lt_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::lt".parse().unwrap())); + pub fn lt_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::lt".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a <= b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn lte_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::lte".parse().unwrap())); + pub fn lte_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::lte".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `a <= b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn lte_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::lte".parse().unwrap())); + pub fn lte_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::lte".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a > b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn gt_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::gt".parse().unwrap())); + pub fn gt_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::gt".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `a > b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn gt_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::gt".parse().unwrap())); + pub fn gt_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::gt".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a >= b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn gte_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::gte".parse().unwrap())); + pub fn gte_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::gte".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `a >= b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn gte_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::gte".parse().unwrap())); + pub fn gte_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::gte".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a == b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn eq_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::eq".parse().unwrap())); + pub fn eq_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::eq".parse().unwrap()), span); } /// Pops a u64 value off the stack, `a`, and pushes `a == 0` on the stack. /// /// This operation is checked, so if the value is not a valid u64, execution will trap. #[inline] - pub fn is_zero_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::eqz".parse().unwrap())); + pub fn is_zero_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::eqz".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `min(a, b)` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn min_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::min".parse().unwrap())); + pub fn min_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::min".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `min(a, b)` on the stack. /// /// This operation is checked, so if the values are not valid i64, execution will trap. - pub fn min_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::min".parse().unwrap())); + pub fn min_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::min".parse().unwrap()), span); } - pub fn min_imm_i64(&mut self, imm: i64) { - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::min".parse().unwrap())); + pub fn min_imm_i64(&mut self, imm: i64, span: SourceSpan) { + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::min".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `max(a, b)` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn max_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::max".parse().unwrap())); + pub fn max_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::max".parse().unwrap()), span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes `max(a, b)` on the stack. /// /// This operation is checked, so if the values are not valid i64, execution will trap. - pub fn max_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::max".parse().unwrap())); + pub fn max_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::max".parse().unwrap()), span); } - pub fn max_imm_i64(&mut self, imm: i64) { - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::max".parse().unwrap())); + pub fn max_imm_i64(&mut self, imm: i64, span: SourceSpan) { + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::max".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a != b` on the stack. /// /// This operation is checked, so if the values are not valid u64, execution will trap. #[inline] - pub fn neq_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::neq".parse().unwrap())); + pub fn neq_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::neq".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and performs `a + b`. @@ -377,19 +393,19 @@ impl<'a> OpEmitter<'a> { /// The caller is assumed to know that different `overflow` settings can /// produce different results, and that those differences are handled. #[inline] - pub fn add_u64(&mut self, overflow: Overflow) { + pub fn add_u64(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Checked => { - self.emit_all(&[ - Op::Exec("std::math::u64::overflowing_add".parse().unwrap()), - Op::Assertz, - ]); + self.emit_all( + &[Op::Exec("std::math::u64::overflowing_add".parse().unwrap()), Op::Assertz], + span, + ); } Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_add".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::wrapping_add".parse().unwrap()), span); } Overflow::Overflowing => { - self.emit(Op::Exec("std::math::u64::overflowing_add".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::overflowing_add".parse().unwrap()), span); } } } @@ -398,14 +414,19 @@ impl<'a> OpEmitter<'a> { /// /// See the [Overflow] type for how overflow semantics can change the operation. #[inline(always)] - pub fn add_i64(&mut self, overflow: Overflow) { - self.emit(match overflow { - Overflow::Unchecked | Overflow::Wrapping => { - Op::Exec("std::math::u64::wrapping_add".parse().unwrap()) - } - Overflow::Checked => Op::Exec("intrinsics::i64::checked_add".parse().unwrap()), - Overflow::Overflowing => Op::Exec("intrinsics::i64::overflowing_add".parse().unwrap()), - }) + pub fn add_i64(&mut self, overflow: Overflow, span: SourceSpan) { + self.emit( + match overflow { + Overflow::Unchecked | Overflow::Wrapping => { + Op::Exec("std::math::u64::wrapping_add".parse().unwrap()) + } + Overflow::Checked => Op::Exec("intrinsics::i64::checked_add".parse().unwrap()), + Overflow::Overflowing => { + Op::Exec("intrinsics::i64::overflowing_add".parse().unwrap()) + } + }, + span, + ) } /// Pops a i64 value off the stack, `a`, and performs `a + `. @@ -414,18 +435,18 @@ impl<'a> OpEmitter<'a> { /// /// Adding zero is a no-op. #[inline] - pub fn add_imm_i64(&mut self, imm: i64, overflow: Overflow) { + pub fn add_imm_i64(&mut self, imm: i64, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } - self.push_i64(imm); + self.push_i64(imm, span); match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.add_u64(overflow), + Overflow::Unchecked | Overflow::Wrapping => self.add_u64(overflow, span), Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i64::checked_add".parse().unwrap())); + self.emit(Op::Exec("intrinsics::i64::checked_add".parse().unwrap()), span); } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i64::overflowing_add".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::overflowing_add".parse().unwrap()), span) } } } @@ -444,19 +465,19 @@ impl<'a> OpEmitter<'a> { /// The caller is assumed to know that different `overflow` settings can /// produce different results, and that those differences are handled. #[inline] - pub fn sub_u64(&mut self, overflow: Overflow) { + pub fn sub_u64(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Checked => { - self.emit_all(&[ - Op::Exec("std::math::u64::overflowing_sub".parse().unwrap()), - Op::Assertz, - ]); + self.emit_all( + &[Op::Exec("std::math::u64::overflowing_sub".parse().unwrap()), Op::Assertz], + span, + ); } Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_sub".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::wrapping_sub".parse().unwrap()), span); } Overflow::Overflowing => { - self.emit(Op::Exec("std::math::u64::overflowing_sub".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::overflowing_sub".parse().unwrap()), span); } } } @@ -464,14 +485,14 @@ impl<'a> OpEmitter<'a> { /// Pops two i64 values off the stack, `b` and `a`, and performs `a - b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn sub_i64(&mut self, overflow: Overflow) { + pub fn sub_i64(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.sub_u64(overflow), + Overflow::Unchecked | Overflow::Wrapping => self.sub_u64(overflow, span), Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i64::checked_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::checked_sub".parse().unwrap()), span) } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i64::overflowing_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::overflowing_sub".parse().unwrap()), span) } } } @@ -482,18 +503,18 @@ impl<'a> OpEmitter<'a> { /// /// Subtracting zero is a no-op. #[inline] - pub fn sub_imm_i64(&mut self, imm: i64, overflow: Overflow) { + pub fn sub_imm_i64(&mut self, imm: i64, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } - self.push_i64(imm); + self.push_i64(imm, span); match overflow { - Overflow::Unchecked | Overflow::Wrapping => self.sub_u64(overflow), + Overflow::Unchecked | Overflow::Wrapping => self.sub_u64(overflow, span), Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i64::checked_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::checked_sub".parse().unwrap()), span) } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i64::overflowing_sub".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::overflowing_sub".parse().unwrap()), span) } } } @@ -512,23 +533,29 @@ impl<'a> OpEmitter<'a> { /// The caller is assumed to know that different `overflow` settings can /// produce different results, and that those differences are handled. #[inline] - pub fn mul_u64(&mut self, overflow: Overflow) { + pub fn mul_u64(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Checked => { - self.emit_all(&[ - Op::Exec("std::math::u64::overflowing_mul".parse().unwrap()), - Op::Exec("std::math::u64::overflowing_eqz".parse().unwrap()), - Op::Assertz, - ]); + self.emit_all( + &[ + Op::Exec("std::math::u64::overflowing_mul".parse().unwrap()), + Op::Exec("std::math::u64::overflowing_eqz".parse().unwrap()), + Op::Assertz, + ], + span, + ); } Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("std::math::u64::wrapping_mul".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::wrapping_mul".parse().unwrap()), span); } Overflow::Overflowing => { - self.emit_all(&[ - Op::Exec("std::math::u64::overflowing_mul".parse().unwrap()), - Op::Exec("std::math::u64::overflowing_eqz".parse().unwrap()), - ]); + self.emit_all( + &[ + Op::Exec("std::math::u64::overflowing_mul".parse().unwrap()), + Op::Exec("std::math::u64::overflowing_eqz".parse().unwrap()), + ], + span, + ); } } } @@ -536,16 +563,16 @@ impl<'a> OpEmitter<'a> { /// Pops two i64 values off the stack, `b` and `a`, and performs `a * b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn mul_i64(&mut self, overflow: Overflow) { + pub fn mul_i64(&mut self, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Unchecked | Overflow::Wrapping => { - self.emit(Op::Exec("intrinsics::i64::wrapping_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::wrapping_mul".parse().unwrap()), span) } Overflow::Checked => { - self.emit(Op::Exec("intrinsics::i64::checked_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::checked_mul".parse().unwrap()), span) } Overflow::Overflowing => { - self.emit(Op::Exec("intrinsics::i64::overflowing_mul".parse().unwrap())) + self.emit(Op::Exec("intrinsics::i64::overflowing_mul".parse().unwrap()), span) } } } @@ -559,24 +586,24 @@ impl<'a> OpEmitter<'a> { /// /// Multiplying by one is a no-op. #[inline] - pub fn mul_imm_i64(&mut self, imm: i64, overflow: Overflow) { + pub fn mul_imm_i64(&mut self, imm: i64, overflow: Overflow, span: SourceSpan) { match imm { 0 => { - self.emit_all(&[Op::Drop, Op::Drop, Op::PushU32(0), Op::PushU32(0)]); + self.emit_all(&[Op::Drop, Op::Drop, Op::PushU32(0), Op::PushU32(0)], span); } 1 => (), imm => match overflow { Overflow::Unchecked | Overflow::Wrapping => { - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::wrapping_mul".parse().unwrap())); + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::wrapping_mul".parse().unwrap()), span); } Overflow::Checked => { - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::checked_mul".parse().unwrap())); + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::checked_mul".parse().unwrap()), span); } Overflow::Overflowing => { - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::overflowing_mul".parse().unwrap())); + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::overflowing_mul".parse().unwrap()), span); } }, } @@ -587,8 +614,8 @@ impl<'a> OpEmitter<'a> { /// /// Both the operands and result are validated to ensure they are valid u64 values. #[inline] - pub fn checked_div_u64(&mut self) { - self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::div".parse().unwrap())]); + pub fn checked_div_u64(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::div".parse().unwrap())], span); } /// Pops two i64 values off the stack, `b` and `a`, and pushes the result of `a / b` on the @@ -596,8 +623,8 @@ impl<'a> OpEmitter<'a> { /// /// Both the operands and result are validated to ensure they are valid u64 values. #[inline] - pub fn checked_div_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::checked_div".parse().unwrap())); + pub fn checked_div_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::checked_div".parse().unwrap()), span); } /// Pops a i64 value off the stack, `a`, and performs `a / `. @@ -605,10 +632,10 @@ impl<'a> OpEmitter<'a> { /// This function will panic if the divisor is zero. /// /// This operation is checked, so if the operand or result are not valid i32, execution traps. - pub fn checked_div_imm_i64(&mut self, imm: i64) { + pub fn checked_div_imm_i64(&mut self, imm: i64, span: SourceSpan) { assert_ne!(imm, 0, "division by zero is not allowed"); - self.push_i64(imm); - self.emit(Op::Exec("intrinsics::i64::checked_div".parse().unwrap())); + self.push_i64(imm, span); + self.emit(Op::Exec("intrinsics::i64::checked_div".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a / b` on the @@ -616,8 +643,8 @@ impl<'a> OpEmitter<'a> { /// /// This operation is unchecked, it is up to the caller to ensure validity of the operands. #[inline] - pub fn unchecked_div_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::div".parse().unwrap())); + pub fn unchecked_div_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::div".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a % b` on the @@ -625,8 +652,8 @@ impl<'a> OpEmitter<'a> { /// /// Both the operands and result are validated to ensure they are valid u64 values. #[inline] - pub fn checked_mod_u64(&mut self) { - self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::mod".parse().unwrap())]); + pub fn checked_mod_u64(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::mod".parse().unwrap())], span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes the result of `a % b` on the @@ -634,8 +661,8 @@ impl<'a> OpEmitter<'a> { /// /// This operation is unchecked, it is up to the caller to ensure validity of the operands. #[inline] - pub fn unchecked_mod_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::mod".parse().unwrap())); + pub fn unchecked_mod_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::mod".parse().unwrap()), span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the @@ -643,8 +670,8 @@ impl<'a> OpEmitter<'a> { /// /// Both the operands and result are validated to ensure they are valid u64 values. #[inline] - pub fn checked_divmod_u64(&mut self) { - self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::divmod".parse().unwrap())]); + pub fn checked_divmod_u64(&mut self, span: SourceSpan) { + self.emit_all(&[Op::U32Assertw, Op::Exec("std::math::u64::divmod".parse().unwrap())], span); } /// Pops two u64 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the @@ -652,32 +679,32 @@ impl<'a> OpEmitter<'a> { /// /// This operation is unchecked, it is up to the caller to ensure validity of the operands. #[inline] - pub fn unchecked_divmod_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::divmod".parse().unwrap())); + pub fn unchecked_divmod_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::divmod".parse().unwrap()), span); } /// Pops two 64-bit values off the stack, `b` and `a`, and pushes `a & b` on the stack. /// /// Both the operands and result are validated to ensure they are valid int64 values. #[inline] - pub fn band_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::and".parse().unwrap())); + pub fn band_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::and".parse().unwrap()), span); } /// Pops two 64-bit values off the stack, `b` and `a`, and pushes `a | b` on the stack. /// /// Both the operands and result are validated to ensure they are valid int64 values. #[inline] - pub fn bor_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::or".parse().unwrap())); + pub fn bor_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::or".parse().unwrap()), span); } /// Pops two 64-bit values off the stack, `b` and `a`, and pushes `a ^ b` on the stack. /// /// Both the operands and result are validated to ensure they are valid int64 values. #[inline] - pub fn bxor_int64(&mut self) { - self.emit(Op::Exec("std::math::u64::xor".parse().unwrap())); + pub fn bxor_int64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::xor".parse().unwrap()), span); } /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and pushes `a << b` on the stack. @@ -686,8 +713,8 @@ impl<'a> OpEmitter<'a> { /// /// The operation will trap if the shift value is > 63. #[inline] - pub fn shl_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::shl".parse().unwrap())); + pub fn shl_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::shl".parse().unwrap()), span); } /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and pushes `a >> b` on the stack. @@ -696,8 +723,8 @@ impl<'a> OpEmitter<'a> { /// /// The operation will trap if the shift value is > 63. #[inline] - pub fn shr_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::shr".parse().unwrap())); + pub fn shr_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::shr".parse().unwrap()), span); } /// Arithmetic shift right (i.e. signedness is preserved) @@ -708,19 +735,19 @@ impl<'a> OpEmitter<'a> { /// /// The operation will trap if the shift value is > 63. #[inline] - pub fn shr_i64(&mut self) { - self.emit(Op::Exec("intrinsics::i64::checked_shr".parse().unwrap())); + pub fn shr_i64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("intrinsics::i64::checked_shr".parse().unwrap()), span); } /// Pops a i64 value off the stack, `a`, and performs `a >> ` /// /// This operation is checked, if the operand or result are not valid i64, execution traps. - pub fn shr_imm_i64(&mut self, imm: u32) { + pub fn shr_imm_i64(&mut self, imm: u32, span: SourceSpan) { assert!(imm < 63, "invalid shift value: must be < 63, got {imm}"); - self.emit_all(&[ - Op::PushU32(imm), - Op::Exec("intrinsics::i64::checked_shr".parse().unwrap()), - ]); + self.emit_all( + &[Op::PushU32(imm), Op::Exec("intrinsics::i64::checked_shr".parse().unwrap())], + span, + ); } /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and rotates the bitwise @@ -729,8 +756,8 @@ impl<'a> OpEmitter<'a> { /// /// The operation will trap if the rotation value is > 63. #[inline] - pub fn rotl_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::rotl".parse().unwrap())); + pub fn rotl_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::rotl".parse().unwrap()), span); } /// Pops a u32 value, `b`, and a u64 value, `a`, off the stack and rotates the bitwise @@ -739,8 +766,8 @@ impl<'a> OpEmitter<'a> { /// /// The operation will trap if the rotation value is > 63. #[inline] - pub fn rotr_u64(&mut self) { - self.emit(Op::Exec("std::math::u64::rotr".parse().unwrap())); + pub fn rotr_u64(&mut self, span: SourceSpan) { + self.emit(Op::Exec("std::math::u64::rotr".parse().unwrap()), span); } } @@ -758,6 +785,6 @@ pub fn to_raw_parts(value: u64) -> (u32, u32) { /// Construct a u64/i64 constant from raw parts, i.e. two 32-bit little-endian limbs #[inline] -pub fn from_raw_parts(lo: u32, hi: u32, block: &mut masm::Block) { - block.push(Op::Push2([Felt::new(lo as u64), Felt::new(hi as u64)])); +pub fn from_raw_parts(lo: u32, hi: u32, block: &mut masm::Block, span: SourceSpan) { + block.push(Op::Push2([Felt::new(lo as u64), Felt::new(hi as u64)]), span); } diff --git a/codegen/masm/src/codegen/emit/mem.rs b/codegen/masm/src/codegen/emit/mem.rs index 178b0f497..d815d4e9c 100644 --- a/codegen/masm/src/codegen/emit/mem.rs +++ b/codegen/masm/src/codegen/emit/mem.rs @@ -1,4 +1,4 @@ -use midenc_hir::{self as hir, Felt, FieldElement, StructType, Type}; +use midenc_hir::{self as hir, Felt, FieldElement, SourceSpan, StructType, Type}; use super::OpEmitter; use crate::masm::{NativePtr, Op}; @@ -12,11 +12,11 @@ impl<'a> OpEmitter<'a> { /// amount of memory allocated. /// /// The address of that slot is placed on the operand stack. - pub fn alloca(&mut self, ptr: &Type) { + pub fn alloca(&mut self, ptr: &Type, span: SourceSpan) { match ptr { Type::Ptr(pointee) => { let local = self.function.alloc_local(pointee.as_ref().clone()); - self.emit(Op::LocAddr(local)); + self.emit(Op::LocAddr(local), span); self.stack.push(ptr.clone()); } ty => panic!("expected a pointer type, got {ty}"), @@ -24,16 +24,16 @@ impl<'a> OpEmitter<'a> { } /// TODO(pauls): For now, we simply return -1 as if the heap cannot be grown any further - pub fn mem_grow(&mut self) { + pub fn mem_grow(&mut self, span: SourceSpan) { let _size = self.stack.pop().expect("operand stack is empty"); - self.emit(Op::PushU32(-1i32 as u32)); + self.emit(Op::PushU32(-1i32 as u32), span); self.stack.push(Type::I32); } /// TODO(pauls): For now, we simply return u32::MAX as if the heap is already fully grown - pub fn mem_size(&mut self) { + pub fn mem_size(&mut self, span: SourceSpan) { const MAX_HEAP_PAGES: u32 = u32::MAX / PAGE_SIZE; - self.emit(Op::PushU32(MAX_HEAP_PAGES)); + self.emit(Op::PushU32(MAX_HEAP_PAGES), span); self.stack.push(Type::U32); } } @@ -45,11 +45,11 @@ impl<'a> OpEmitter<'a> { /// /// Internally, this pushes the address of the local on the stack, then delegates to /// [OpEmitter::load] - pub fn load_local(&mut self, local: hir::LocalId) { + pub fn load_local(&mut self, local: hir::LocalId, span: SourceSpan) { let ty = self.function.local(local).ty.clone(); - self.emit(Op::LocAddr(local)); + self.emit(Op::LocAddr(local), span); self.stack.push(Type::Ptr(Box::new(ty.clone()))); - self.load(ty) + self.load(ty, span) } /// Load a value corresponding to the pointee type of a pointer operand on the stack. @@ -57,20 +57,20 @@ impl<'a> OpEmitter<'a> { /// The type of the pointer determines what address space the pointer value represents; /// either the Miden-native address space (word-addressable), or the IR's byte-addressable /// address space. - pub fn load(&mut self, ty: Type) { + pub fn load(&mut self, ty: Type, span: SourceSpan) { let ptr = self.stack.pop().expect("operand stack is empty"); match ptr.ty() { Type::Ptr(_) => { // Convert the pointer to a native pointer representation - self.emit_native_ptr(); + self.emit_native_ptr(span); match &ty { - Type::I128 => self.load_quad_word(None), - Type::I64 | Type::U64 => self.load_double_word(None), - Type::Felt => self.load_felt(None), - Type::I32 | Type::U32 => self.load_word(None), + Type::I128 => self.load_quad_word(None, span), + Type::I64 | Type::U64 => self.load_double_word(None, span), + Type::Felt => self.load_felt(None, span), + Type::I32 | Type::U32 => self.load_word(None, span), ty @ (Type::I16 | Type::U16 | Type::U8 | Type::I8 | Type::I1) => { - self.load_word(None); - self.trunc_int32(ty.size_in_bits() as u32); + self.load_word(None, span); + self.trunc_int32(ty.size_in_bits() as u32, span); } ty => todo!("support for loading {ty} is not yet implemented"), } @@ -86,16 +86,16 @@ impl<'a> OpEmitter<'a> { /// Load a value of type `ty` from `addr`. /// /// NOTE: The address represented by `addr` is in the IR's byte-addressable address space. - pub fn load_imm(&mut self, addr: u32, ty: Type) { + pub fn load_imm(&mut self, addr: u32, ty: Type, span: SourceSpan) { let ptr = NativePtr::from_ptr(addr); match &ty { - Type::I128 => self.load_quad_word(Some(ptr)), - Type::I64 | Type::U64 => self.load_double_word(Some(ptr)), - Type::Felt => self.load_felt(Some(ptr)), - Type::I32 | Type::U32 => self.load_word(Some(ptr)), + Type::I128 => self.load_quad_word(Some(ptr), span), + Type::I64 | Type::U64 => self.load_double_word(Some(ptr), span), + Type::Felt => self.load_felt(Some(ptr), span), + Type::I32 | Type::U32 => self.load_word(Some(ptr), span), Type::I16 | Type::U16 | Type::U8 | Type::I8 | Type::I1 => { - self.load_word(Some(ptr)); - self.trunc_int32(ty.size_in_bits() as u32); + self.load_word(Some(ptr), span); + self.trunc_int32(ty.size_in_bits() as u32, span); } ty => todo!("support for loading {ty} is not yet implemented"), } @@ -109,81 +109,87 @@ impl<'a> OpEmitter<'a> { /// Instructions which must act on a pointer will expect the stack to have /// these values in that order so that they can perform any necessary /// re-alignment. - fn emit_native_ptr(&mut self) { - self.emit_all(&[ - // Copy the address - // - // [addr, addr] - Op::Dup(0), - // Obtain the absolute offset - // - // [abs_offset, addr] - Op::U32ModImm(16), - // Obtain the byte offset - // - // [abs_offset, abs_offset, addr] - Op::Dup(0), - // [offset, abs_offset, addr] - Op::U32ModImm(4), - // Obtain the element index - // - // [abs_offset, offset, addr] - Op::Swap(1), - // [index, byte_offset, addr] - Op::U32DivImm(4), - // Translate the address to Miden's address space - // - // [addr, index, offset] - Op::Movup(2), - // [waddr, index, offset] - Op::U32DivImm(16), - ]); + fn emit_native_ptr(&mut self, span: SourceSpan) { + self.emit_all( + &[ + // Copy the address + // + // [addr, addr] + Op::Dup(0), + // Obtain the absolute offset + // + // [abs_offset, addr] + Op::U32ModImm(16), + // Obtain the byte offset + // + // [abs_offset, abs_offset, addr] + Op::Dup(0), + // [offset, abs_offset, addr] + Op::U32ModImm(4), + // Obtain the element index + // + // [abs_offset, offset, addr] + Op::Swap(1), + // [index, byte_offset, addr] + Op::U32DivImm(4), + // Translate the address to Miden's address space + // + // [addr, index, offset] + Op::Movup(2), + // [waddr, index, offset] + Op::U32DivImm(16), + ], + span, + ); } /// Load a field element from a naturally aligned address, either immediate or dynamic /// /// A native pointer triplet is expected on the stack if an immediate is not given. - fn load_felt(&mut self, ptr: Option) { + fn load_felt(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.load_felt_imm(imm); + return self.load_felt_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::load_felt".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::load_felt".parse().unwrap()), span); } - fn load_felt_imm(&mut self, ptr: NativePtr) { + fn load_felt_imm(&mut self, ptr: NativePtr, span: SourceSpan) { assert!(ptr.is_element_aligned(), "felt values must be naturally aligned"); match ptr.index { - 0 => self.emit(Op::MemLoadImm(ptr.waddr)), + 0 => self.emit(Op::MemLoadImm(ptr.waddr), span), 1 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Movup(4), - Op::Movup(4), - Op::Drop, - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Movup(4), + Op::Movup(4), + Op::Drop, + Op::Drop, + Op::Drop, + ], + span, + ); } 2 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Swap(1), - Op::Drop, - ]); + self.emit_all( + &[ + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Drop, + Op::Drop, + Op::Swap(1), + Op::Drop, + ], + span, + ); } 3 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[Op::Padw, Op::MemLoadwImm(ptr.waddr), Op::Drop, Op::Drop, Op::Drop], + span, + ); } _ => unreachable!(), } @@ -193,404 +199,470 @@ impl<'a> OpEmitter<'a> { /// word /// /// Expects a native pointer triplet on the stack if an immediate address is not given. - fn load_word(&mut self, ptr: Option) { + fn load_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.load_word_imm(imm); + return self.load_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::load_sw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::load_sw".parse().unwrap()), span); } /// Loads a single 32-bit machine word from the given immediate address. - fn load_word_imm(&mut self, ptr: NativePtr) { + fn load_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { let is_aligned = ptr.is_element_aligned(); let rshift = 32 - ptr.offset as u32; match ptr.index { - 0 if is_aligned => self.emit(Op::MemLoadImm(ptr.waddr)), + 0 if is_aligned => self.emit(Op::MemLoadImm(ptr.waddr), span), 0 => { - self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Move the two elements across which the desired machine word spans - // to the bottom of the stack temporarily - Op::Movdn(4), - Op::Movdn(4), - // Drop the unused elements - Op::Drop, - Op::Drop, - // Shift the high bits left by the offset - Op::U32ShlImm(ptr.offset as u32), - // Move the low bits to the top and shift them right - Op::Swap(1), - Op::U32ShrImm(rshift), - // OR the high and low bits together - Op::U32Or, - ]); + self.emit_all( + &[ + // Load a quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Move the two elements across which the desired machine word spans + // to the bottom of the stack temporarily + Op::Movdn(4), + Op::Movdn(4), + // Drop the unused elements + Op::Drop, + Op::Drop, + // Shift the high bits left by the offset + Op::U32ShlImm(ptr.offset as u32), + // Move the low bits to the top and shift them right + Op::Swap(1), + Op::U32ShrImm(rshift), + // OR the high and low bits together + Op::U32Or, + ], + span, + ); } - 1 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the first unused element - Op::Drop, - // Move the desired element past the last two unused - Op::Movdn(3), - // Drop the remaining unused elements - Op::Drop, - Op::Drop, - ]), - 1 => { - self.emit_all(&[ + 1 if is_aligned => self.emit_all( + &[ // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), // Drop the first unused element Op::Drop, - // Move the two elements across which the desired machine word spans - // to the bottom of the stack temporarily - Op::Movdn(3), + // Move the desired element past the last two unused Op::Movdn(3), - // Drop the remaining unused element + // Drop the remaining unused elements Op::Drop, - // Shift the high bits left by the offset - Op::U32ShlImm(ptr.offset as u32), - // Move the low bits to the top and shift them right - Op::Swap(1), - Op::U32ShrImm(rshift), - // OR the high and low bits together - Op::U32Or, - ]); + Op::Drop, + ], + span, + ), + 1 => { + self.emit_all( + &[ + // Load a quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the first unused element + Op::Drop, + // Move the two elements across which the desired machine word spans + // to the bottom of the stack temporarily + Op::Movdn(3), + Op::Movdn(3), + // Drop the remaining unused element + Op::Drop, + // Shift the high bits left by the offset + Op::U32ShlImm(ptr.offset as u32), + // Move the low bits to the top and shift them right + Op::Swap(1), + Op::U32ShrImm(rshift), + // OR the high and low bits together + Op::U32Or, + ], + span, + ); } - 2 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the first two unused elements - Op::Drop, - Op::Drop, - // Swap the last remaining unused element to the top and drop it - Op::Swap(1), - Op::Drop, - ]), - 2 => { - self.emit_all(&[ + 2 if is_aligned => self.emit_all( + &[ // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), // Drop the first two unused elements Op::Drop, Op::Drop, - // Shift the high bits left by the offset - Op::U32ShlImm(ptr.offset as u32), - // Move the low bits to the top and shift them right + // Swap the last remaining unused element to the top and drop it Op::Swap(1), - Op::U32ShrImm(rshift), - // OR the high and low bits together - Op::U32Or, - ]); - } - 3 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the three unused elements - Op::Drop, - Op::Drop, - Op::Drop, - ]), - 3 => { - self.emit_all(&[ - // Load the quad-word containing the low bits - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Move the element we need to the bottom temporarily - Op::Movdn(4), - // Drop the unused elements - Op::Drop, - Op::Drop, Op::Drop, - // Shift the low bits right by the offset - Op::U32ShrImm(rshift), - // Load the quad-word containing the high bits + ], + span, + ), + 2 => { + self.emit_all( + &[ + // Load a quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the first two unused elements + Op::Drop, + Op::Drop, + // Shift the high bits left by the offset + Op::U32ShlImm(ptr.offset as u32), + // Move the low bits to the top and shift them right + Op::Swap(1), + Op::U32ShrImm(rshift), + // OR the high and low bits together + Op::U32Or, + ], + span, + ); + } + 3 if is_aligned => self.emit_all( + &[ + // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), - // Drop the unused elements + // Drop the three unused elements Op::Drop, Op::Drop, Op::Drop, - // Shift the high bits left by the offset - Op::U32ShlImm(ptr.offset as u32), - // OR the high and low bits together - Op::U32Or, - ]); + ], + span, + ), + 3 => { + self.emit_all( + &[ + // Load the quad-word containing the low bits + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Move the element we need to the bottom temporarily + Op::Movdn(4), + // Drop the unused elements + Op::Drop, + Op::Drop, + Op::Drop, + // Shift the low bits right by the offset + Op::U32ShrImm(rshift), + // Load the quad-word containing the high bits + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the unused elements + Op::Drop, + Op::Drop, + Op::Drop, + // Shift the high bits left by the offset + Op::U32ShlImm(ptr.offset as u32), + // OR the high and low bits together + Op::U32Or, + ], + span, + ); } _ => unreachable!(), } } /// Load a pair of machine words (32-bit elements) to the operand stack - fn load_double_word(&mut self, ptr: Option) { + fn load_double_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.load_double_word_imm(imm); + return self.load_double_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::load_dw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::load_dw".parse().unwrap()), span); } - fn load_double_word_imm(&mut self, ptr: NativePtr) { + fn load_double_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { let aligned = ptr.is_element_aligned(); match ptr.index { 0 if aligned => { - self.emit_all(&[ - // Load quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Move the two elements we need to the bottom temporarily - Op::Movdn(4), - Op::Movdn(4), - // Drop the unused elements - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + // Load quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Move the two elements we need to the bottom temporarily + Op::Movdn(4), + Op::Movdn(4), + // Drop the unused elements + Op::Drop, + Op::Drop, + ], + span, + ); } 0 => { // An unaligned double-word load spans three elements - self.emit_all(&[ - // Load quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Move the unused element to the top and drop it - Op::Movup(4), - Op::Drop, - ]); - self.realign_double_word(ptr); + self.emit_all( + &[ + // Load quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Move the unused element to the top and drop it + Op::Movup(4), + Op::Drop, + ], + span, + ); + self.realign_double_word(ptr, span); } 1 if aligned => { - self.emit_all(&[ - // Load quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the first word, its unused - Op::Drop, - // Move the last word up and drop it, also unused - Op::Movup(3), - Op::Drop, - ]); + self.emit_all( + &[ + // Load quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the first word, its unused + Op::Drop, + // Move the last word up and drop it, also unused + Op::Movup(3), + Op::Drop, + ], + span, + ); } 1 => { // An unaligned double-word load spans three elements - self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the unused element - Op::Drop, - ]); - self.realign_double_word(ptr); + self.emit_all( + &[ + // Load a quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the unused element + Op::Drop, + ], + span, + ); + self.realign_double_word(ptr, span); } 2 if aligned => { - self.emit_all(&[ - // Load quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop unused words - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + // Load quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop unused words + Op::Drop, + Op::Drop, + ], + span, + ); } 2 => { // An unaligned double-word load spans three elements, // and in this case, two quad-words, because the last // element is across a quad-word boundary - self.emit_all(&[ - // Load the second quad-word first - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Move the element we need to the bottom temporarily - Op::Movdn(4), - // Drop the three unused elements of this word - Op::Drop, - Op::Drop, - Op::Drop, - // Load the first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the two unused elements - Op::Drop, - Op::Drop, - ]); - self.realign_double_word(ptr); + self.emit_all( + &[ + // Load the second quad-word first + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Move the element we need to the bottom temporarily + Op::Movdn(4), + // Drop the three unused elements of this word + Op::Drop, + Op::Drop, + Op::Drop, + // Load the first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the two unused elements + Op::Drop, + Op::Drop, + ], + span, + ); + self.realign_double_word(ptr, span); } 3 if aligned => { - self.emit_all(&[ - // Load second word, drop unused elements - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - Op::Movup(4), - Op::Drop, - Op::Movup(3), - Op::Drop, - // Load first word, drop unused elements - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + // Load second word, drop unused elements + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + Op::Movup(4), + Op::Drop, + Op::Movup(3), + Op::Drop, + // Load first word, drop unused elements + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Drop, + Op::Drop, + Op::Drop, + ], + span, + ); } 3 => { - self.emit_all(&[ - // Load second word, drop unused element - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - Op::Movup(4), - Op::Drop, - // Load first word, drop unused elements - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Drop, - Op::Drop, - Op::Drop, - ]); - self.realign_double_word(ptr); + self.emit_all( + &[ + // Load second word, drop unused element + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + Op::Movup(4), + Op::Drop, + // Load first word, drop unused elements + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Drop, + Op::Drop, + Op::Drop, + ], + span, + ); + self.realign_double_word(ptr, span); } _ => unimplemented!("unaligned loads are not yet implemented: {ptr:#?}"), } } /// Load a quartet of machine words (32-bit elements) to the operand stack - fn load_quad_word(&mut self, ptr: Option) { + fn load_quad_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.load_quad_word_imm(imm); + return self.load_quad_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::load_qw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::load_qw".parse().unwrap()), span); } - fn load_quad_word_imm(&mut self, ptr: NativePtr) { + fn load_quad_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { // For all other cases, more complicated loads are required let aligned = ptr.is_element_aligned(); match ptr.index { // Naturally-aligned - 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)]), + 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)], span), 0 => { // An unaligned quad-word load spans five elements - self.emit_all(&[ - // Load second quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Drop all but the first element - Op::Movdn(4), - Op::Drop, - Op::Drop, - Op::Drop, - // Load first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - ]); - self.realign_quad_word(ptr); + self.emit_all( + &[ + // Load second quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Drop all but the first element + Op::Movdn(4), + Op::Drop, + Op::Drop, + Op::Drop, + // Load first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + ], + span, + ); + self.realign_quad_word(ptr, span); } 1 if aligned => { - self.emit_all(&[ - // Load second quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Drop last element - Op::Movup(4), - Op::Drop, - // Load first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop first element - Op::Drop, - ]); + self.emit_all( + &[ + // Load second quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Drop last element + Op::Movup(4), + Op::Drop, + // Load first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop first element + Op::Drop, + ], + span, + ); } 1 => { // An unaligned double-word load spans five elements - self.emit_all(&[ - // Load second quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Drop all but the first two elements - Op::Movdn(4), - Op::Movdn(4), - Op::Drop, - Op::Drop, - // Load first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the first word - Op::Drop, - ]); - self.realign_quad_word(ptr); + self.emit_all( + &[ + // Load second quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Drop all but the first two elements + Op::Movdn(4), + Op::Movdn(4), + Op::Drop, + Op::Drop, + // Load first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the first word + Op::Drop, + ], + span, + ); + self.realign_quad_word(ptr, span); } 2 if aligned => { - self.emit_all(&[ - // Load second quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop last two elements - Op::Movup(4), - Op::Movup(4), - Op::Drop, - Op::Drop, - // Load first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop first two elements - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + // Load second quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop last two elements + Op::Movup(4), + Op::Movup(4), + Op::Drop, + Op::Drop, + // Load first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop first two elements + Op::Drop, + Op::Drop, + ], + span, + ); } 2 => { // An unaligned double-word load spans five elements - self.emit_all(&[ - // Load the second quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Drop the last element - Op::Movup(4), - Op::Drop, - // Load the first quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop the two unused elements - Op::Drop, - Op::Drop, - ]); - self.realign_quad_word(ptr); + self.emit_all( + &[ + // Load the second quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Drop the last element + Op::Movup(4), + Op::Drop, + // Load the first quad-word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop the two unused elements + Op::Drop, + Op::Drop, + ], + span, + ); + self.realign_quad_word(ptr, span); } 3 if aligned => { - self.emit_all(&[ - // Load second word, drop last element - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - Op::Movup(4), - Op::Drop, - // Load first word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop first three elements - Op::Drop, - Op::Drop, - Op::Drop, - ]); + self.emit_all( + &[ + // Load second word, drop last element + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + Op::Movup(4), + Op::Drop, + // Load first word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop first three elements + Op::Drop, + Op::Drop, + Op::Drop, + ], + span, + ); } 3 => { // An unaligned quad-word load spans five elements, - self.emit_all(&[ - // Load second word - Op::Padw, - Op::MemLoadwImm(ptr.waddr + 1), - // Load first word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Drop unused elements - Op::Drop, - Op::Drop, - Op::Drop, - ]); - self.realign_quad_word(ptr); + self.emit_all( + &[ + // Load second word + Op::Padw, + Op::MemLoadwImm(ptr.waddr + 1), + // Load first word + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Drop unused elements + Op::Drop, + Op::Drop, + Op::Drop, + ], + span, + ); + self.realign_quad_word(ptr, span); } _ => unimplemented!("unaligned loads are not yet implemented: {ptr:#?}"), } @@ -635,69 +707,72 @@ impl<'a> OpEmitter<'a> { /// have to perform a sequence of shifts and masks to get the bits where they belong. This /// function performs those steps, with the assumption that the caller has three values on /// the operand stack representing any unaligned double-word value - fn realign_double_word(&mut self, ptr: NativePtr) { + fn realign_double_word(&mut self, ptr: NativePtr, span: SourceSpan) { // The stack starts as: [chunk_hi, chunk_mid, chunk_lo] // // We will refer to the parts of our desired double-word value // as two parts, `x_hi` and `x_lo`. - self.emit_all(&[ - // Re-align the high bits by shifting out the offset - // - // This gives us the first half of the first word. - // - // [x_hi_hi, chunk_mid, chunk__lo] - Op::U32ShlImm(ptr.offset as u32), - // Move the value below the other chunks temporarily - // - // [chunk_mid, chunk_lo, x_hi_hi] - Op::Movdn(3), - // We must split the middle chunk into two parts, - // one containing the bits to be combined with the - // first machine word; the other to be combined with - // the second machine word. - // - // First, we duplicate the chunk, since we need two - // copies of it: - // - // [chunk_mid, chunk_mid, chunk_lo, x_hi_hi] - Op::Dup(0), - // Then, we shift the chunk right by 32 - offset bits, - // re-aligning the low bits of the first word, and - // isolating them. - // - // [x_hi_lo, chunk_mid, chunk_lo, x_hi_hi] - Op::U32ShrImm(32 - ptr.offset as u32), - // Move the high bits back to the top - // - // [x_hi_hi, x_hi_lo, chunk_mid, chunk_lo] - Op::Movup(3), - // OR the two parts of the `x_hi` chunk together - // - // [x_hi, chunk_mid, chunk_lo] - Op::U32Or, - // Move `x_hi` to the bottom for later - Op::Movdn(2), - // Now, we need to re-align the high bits of the second word - // by shifting the remaining copy of the middle chunk, similar - // to what we did at the very beginning. - // - // This gives us the first half of the second word. - // - // [x_lo_hi, chunk_lo, x_hi] - Op::U32ShlImm(ptr.offset as u32), - // Next, swap the low bit chunk to the top temporarily - Op::Swap(1), - // Shift the value right, as done previously for the middle chunk - Op::U32ShrImm(32 - ptr.offset as u32), - // OR the two halves together, giving us our second word, `x_lo` - // - // [x_lo, x_hi] - Op::U32Or, - // Swap the words so they are in the correct order - // - // [x_hi, x_lo] - Op::Swap(1), - ]); + self.emit_all( + &[ + // Re-align the high bits by shifting out the offset + // + // This gives us the first half of the first word. + // + // [x_hi_hi, chunk_mid, chunk__lo] + Op::U32ShlImm(ptr.offset as u32), + // Move the value below the other chunks temporarily + // + // [chunk_mid, chunk_lo, x_hi_hi] + Op::Movdn(3), + // We must split the middle chunk into two parts, + // one containing the bits to be combined with the + // first machine word; the other to be combined with + // the second machine word. + // + // First, we duplicate the chunk, since we need two + // copies of it: + // + // [chunk_mid, chunk_mid, chunk_lo, x_hi_hi] + Op::Dup(0), + // Then, we shift the chunk right by 32 - offset bits, + // re-aligning the low bits of the first word, and + // isolating them. + // + // [x_hi_lo, chunk_mid, chunk_lo, x_hi_hi] + Op::U32ShrImm(32 - ptr.offset as u32), + // Move the high bits back to the top + // + // [x_hi_hi, x_hi_lo, chunk_mid, chunk_lo] + Op::Movup(3), + // OR the two parts of the `x_hi` chunk together + // + // [x_hi, chunk_mid, chunk_lo] + Op::U32Or, + // Move `x_hi` to the bottom for later + Op::Movdn(2), + // Now, we need to re-align the high bits of the second word + // by shifting the remaining copy of the middle chunk, similar + // to what we did at the very beginning. + // + // This gives us the first half of the second word. + // + // [x_lo_hi, chunk_lo, x_hi] + Op::U32ShlImm(ptr.offset as u32), + // Next, swap the low bit chunk to the top temporarily + Op::Swap(1), + // Shift the value right, as done previously for the middle chunk + Op::U32ShrImm(32 - ptr.offset as u32), + // OR the two halves together, giving us our second word, `x_lo` + // + // [x_lo, x_hi] + Op::U32Or, + // Swap the words so they are in the correct order + // + // [x_hi, x_lo] + Op::Swap(1), + ], + span, + ); } /// This handles emitting code that handles aligning an unaligned quad machine-word value @@ -714,131 +789,134 @@ impl<'a> OpEmitter<'a> { /// /// See the example in [OpEmitter::realign_quad_word] for more details on how bits are /// laid out in each word, and what is required to realign unaligned words. - fn realign_quad_word(&mut self, ptr: NativePtr) { + fn realign_quad_word(&mut self, ptr: NativePtr, span: SourceSpan) { // The stack starts as: [chunk_hi, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo] // // We will refer to the parts of our desired quad-word value // as four parts, `x_hi2`, `x_hi1`, `x_lo2`, and `x_lo1`, where // the integer suffix should appear in decreasing order on the // stack when we're done. - self.emit_all(&[ - // Re-align the high bits by shifting out the offset - // - // This gives us the first half of `x_hi2`. - // - // [x_hi2_hi, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk__lo] - Op::U32ShlImm(ptr.offset as u32), - // Move the value below the other chunks temporarily - // - // [chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk__lo, x_hi2_hi] - Op::Movdn(5), - // We must split the `chunk_mid_hi` chunk into two parts, - // one containing the bits to be combined with `x_hi2_hi`; - // the other to be combined with `x_hi1_hi`. - // - // First, we duplicate the chunk, since we need two - // copies of it: - // - // [chunk_mid_hi, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2_hi] - Op::Dup(0), - // Then, we shift the chunk right by 32 - offset bits, - // re-aligning the low bits of `x_hi2`, and isolating them. - // - // [x_hi2_lo, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2_hi] - Op::U32ShrImm(32 - ptr.offset as u32), - // Move the high bits of `x_hi2` back to the top - // - // [x_hi2_hi, x_hi2_lo, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo] - Op::Movup(3), - // OR the two parts of the `x_hi2` chunk together - // - // [x_hi2, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo] - Op::U32Or, - // Move `x_hi2` to the bottom for later - // - // [chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] - Op::Movdn(5), - // Now, we need to re-align the high bits of `x_hi1` by shifting - // the remaining copy of `chunk_mid_hi`, similar to what we did for `x_hi2` - // - // This gives us the first half of `x_hi1` - // - // [x_hi1_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] - Op::U32ShlImm(ptr.offset as u32), - // Next, move the chunk containing the low bits of `x_hi1` to the top temporarily - // - // [chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] - Op::Movdn(5), - // Duplicate it, as we need two copies - // - // [chunk_mid_mid, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] - Op::Dup(0), - // Shift the value right, as done previously for the low bits of `x_hi2` - // - // [x_hi1_lo, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] - Op::U32ShrImm(32 - ptr.offset as u32), - // Move the high bits of `x_hi1` to the top - Op::Movup(5), - // OR the two halves together, giving us our second word, `x_hi1` - // - // [x_hi1, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] - Op::U32Or, - // Move the word to the bottom of the stack - // - // [chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] - Op::Movdn(5), - // Now, we need to re-align the high bits of `x_lo2` by shifting - // the remaining copy of `chunk_mid_mid`, as done previously. - // - // [x_lo2_hi, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] - Op::U32ShlImm(ptr.offset as u32), - // Next, move the chunk containing the low bits of `x_lo2` to the top temporarily - // - // [chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] - Op::Movdn(5), - // Duplicate it, as done previously - // - // [chunk_mid_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] - Op::Dup(0), - // Shift the value right to get the low bits of `x_lo2` - // - // [x_lo2_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] - Op::U32ShrImm(32 - ptr.offset as u32), - // Move the high bits of `x_lo2` to the top - // - // [x_lo2_hi, x_lo2_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] - Op::Movup(6), - // OR the two halves together, giving us our third word, `x_lo2` - // - // [x_lo2, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] - Op::U32Or, - // Move to the bottom of the stack - // - // [chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2] - Op::Movdn(5), - // Re-align the high bits of `x_lo1` - // - // [x_lo1_hi, chunk_lo, x_hi2, x_hi1, x_lo2] - Op::U32ShlImm(ptr.offset as u32), - // Move the chunk containing the low bits to the top - // - // [chunk_lo, x_hi2, x_hi1, x_lo2, x_lo1_hi] - Op::Movdn(5), - // Shift the value right to get the low bits of `x_lo1` - Op::U32ShrImm(32 - ptr.offset as u32), - // Move the high bits of `x_lo1` to the top - // - // [x_lo1_hi, x_lo1_lo, x_hi2, x_hi1, x_lo2] - Op::Movup(5), - // OR the two halves together, giving us our fourth word, `x_lo1` - // - // [x_lo1, x_hi2, x_hi1, x_lo2] - Op::U32Or, - // Move to the bottom - // - // [x_hi2, x_hi1, x_lo2, x_lo1] - Op::Movdn(5), - ]); + self.emit_all( + &[ + // Re-align the high bits by shifting out the offset + // + // This gives us the first half of `x_hi2`. + // + // [x_hi2_hi, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk__lo] + Op::U32ShlImm(ptr.offset as u32), + // Move the value below the other chunks temporarily + // + // [chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk__lo, x_hi2_hi] + Op::Movdn(5), + // We must split the `chunk_mid_hi` chunk into two parts, + // one containing the bits to be combined with `x_hi2_hi`; + // the other to be combined with `x_hi1_hi`. + // + // First, we duplicate the chunk, since we need two + // copies of it: + // + // [chunk_mid_hi, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2_hi] + Op::Dup(0), + // Then, we shift the chunk right by 32 - offset bits, + // re-aligning the low bits of `x_hi2`, and isolating them. + // + // [x_hi2_lo, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2_hi] + Op::U32ShrImm(32 - ptr.offset as u32), + // Move the high bits of `x_hi2` back to the top + // + // [x_hi2_hi, x_hi2_lo, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo] + Op::Movup(3), + // OR the two parts of the `x_hi2` chunk together + // + // [x_hi2, chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo] + Op::U32Or, + // Move `x_hi2` to the bottom for later + // + // [chunk_mid_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] + Op::Movdn(5), + // Now, we need to re-align the high bits of `x_hi1` by shifting + // the remaining copy of `chunk_mid_hi`, similar to what we did for `x_hi2` + // + // This gives us the first half of `x_hi1` + // + // [x_hi1_hi, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] + Op::U32ShlImm(ptr.offset as u32), + // Next, move the chunk containing the low bits of `x_hi1` to the top temporarily + // + // [chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] + Op::Movdn(5), + // Duplicate it, as we need two copies + // + // [chunk_mid_mid, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] + Op::Dup(0), + // Shift the value right, as done previously for the low bits of `x_hi2` + // + // [x_hi1_lo, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1_hi] + Op::U32ShrImm(32 - ptr.offset as u32), + // Move the high bits of `x_hi1` to the top + Op::Movup(5), + // OR the two halves together, giving us our second word, `x_hi1` + // + // [x_hi1, chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2] + Op::U32Or, + // Move the word to the bottom of the stack + // + // [chunk_mid_mid, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] + Op::Movdn(5), + // Now, we need to re-align the high bits of `x_lo2` by shifting + // the remaining copy of `chunk_mid_mid`, as done previously. + // + // [x_lo2_hi, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] + Op::U32ShlImm(ptr.offset as u32), + // Next, move the chunk containing the low bits of `x_lo2` to the top temporarily + // + // [chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] + Op::Movdn(5), + // Duplicate it, as done previously + // + // [chunk_mid_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] + Op::Dup(0), + // Shift the value right to get the low bits of `x_lo2` + // + // [x_lo2_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2_hi] + Op::U32ShrImm(32 - ptr.offset as u32), + // Move the high bits of `x_lo2` to the top + // + // [x_lo2_hi, x_lo2_lo, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] + Op::Movup(6), + // OR the two halves together, giving us our third word, `x_lo2` + // + // [x_lo2, chunk_mid_lo, chunk_lo, x_hi2, x_hi1] + Op::U32Or, + // Move to the bottom of the stack + // + // [chunk_mid_lo, chunk_lo, x_hi2, x_hi1, x_lo2] + Op::Movdn(5), + // Re-align the high bits of `x_lo1` + // + // [x_lo1_hi, chunk_lo, x_hi2, x_hi1, x_lo2] + Op::U32ShlImm(ptr.offset as u32), + // Move the chunk containing the low bits to the top + // + // [chunk_lo, x_hi2, x_hi1, x_lo2, x_lo1_hi] + Op::Movdn(5), + // Shift the value right to get the low bits of `x_lo1` + Op::U32ShrImm(32 - ptr.offset as u32), + // Move the high bits of `x_lo1` to the top + // + // [x_lo1_hi, x_lo1_lo, x_hi2, x_hi1, x_lo2] + Op::Movup(5), + // OR the two halves together, giving us our fourth word, `x_lo1` + // + // [x_lo1, x_hi2, x_hi1, x_lo2] + Op::U32Or, + // Move to the bottom + // + // [x_hi2, x_hi1, x_lo2, x_lo1] + Op::Movdn(5), + ], + span, + ); } } @@ -849,11 +927,11 @@ impl<'a> OpEmitter<'a> { /// /// Internally, this pushes the address of the given local on the stack, and delegates to /// [OpEmitter::store] to perform the actual store. - pub fn store_local(&mut self, local: hir::LocalId) { + pub fn store_local(&mut self, local: hir::LocalId, span: SourceSpan) { let ty = self.function.local(local).ty.clone(); - self.emit(Op::LocAddr(local)); + self.emit(Op::LocAddr(local), span); self.stack.push(Type::Ptr(Box::new(ty))); - self.store() + self.store(span) } /// Store a value of type `value` to the address in the Miden address space @@ -861,7 +939,7 @@ impl<'a> OpEmitter<'a> { /// /// The type of the pointer is given as `ptr`, and can be used for both validation and /// determining alignment. - pub fn store(&mut self) { + pub fn store(&mut self, span: SourceSpan) { let ptr = self.stack.pop().expect("operand stack is empty"); let value = self.stack.pop().expect("operand stack is empty"); let ptr_ty = ptr.ty(); @@ -871,15 +949,15 @@ impl<'a> OpEmitter<'a> { match ptr_ty { Type::Ptr(_) => { // Convert the pointer to a native pointer representation - self.emit_native_ptr(); + self.emit_native_ptr(span); match value_ty { - Type::I128 => self.store_quad_word(None), - Type::I64 | Type::U64 => self.store_double_word(None), - Type::Felt => self.store_felt(None), - Type::I32 | Type::U32 => self.store_word(None), - ref ty if ty.size_in_bytes() <= 4 => self.store_small(ty, None), - Type::Array(ref elem_ty, _) => self.store_array(elem_ty, None), - Type::Struct(ref struct_ty) => self.store_struct(struct_ty, None), + Type::I128 => self.store_quad_word(None, span), + Type::I64 | Type::U64 => self.store_double_word(None, span), + Type::Felt => self.store_felt(None, span), + Type::I32 | Type::U32 => self.store_word(None, span), + ref ty if ty.size_in_bytes() <= 4 => self.store_small(ty, None, span), + Type::Array(ref elem_ty, _) => self.store_array(elem_ty, None, span), + Type::Struct(ref struct_ty) => self.store_struct(struct_ty, None, span), ty => unimplemented!( "invalid store: support for storing {ty} has not been implemented" ), @@ -895,26 +973,26 @@ impl<'a> OpEmitter<'a> { /// Store a value of type `ty` to `addr`. /// /// NOTE: The address represented by `addr` is in the IR's byte-addressable address space. - pub fn store_imm(&mut self, addr: u32) { + pub fn store_imm(&mut self, addr: u32, span: SourceSpan) { let value = self.stack.pop().expect("operand stack is empty"); let value_ty = value.ty(); assert!(!value_ty.is_zst(), "cannot store a zero-sized type in memory"); let ptr = NativePtr::from_ptr(addr); match value_ty { - Type::I128 => self.store_quad_word(Some(ptr)), - Type::I64 | Type::U64 => self.store_double_word(Some(ptr)), - Type::Felt => self.store_felt(Some(ptr)), - Type::I32 | Type::U32 => self.store_word(Some(ptr)), - ref ty if ty.size_in_bytes() <= 4 => self.store_small(ty, Some(ptr)), - Type::Array(ref elem_ty, _) => self.store_array(elem_ty, Some(ptr)), - Type::Struct(ref struct_ty) => self.store_struct(struct_ty, Some(ptr)), + Type::I128 => self.store_quad_word(Some(ptr), span), + Type::I64 | Type::U64 => self.store_double_word(Some(ptr), span), + Type::Felt => self.store_felt(Some(ptr), span), + Type::I32 | Type::U32 => self.store_word(Some(ptr), span), + ref ty if ty.size_in_bytes() <= 4 => self.store_small(ty, Some(ptr), span), + Type::Array(ref elem_ty, _) => self.store_array(elem_ty, Some(ptr), span), + Type::Struct(ref struct_ty) => self.store_struct(struct_ty, Some(ptr), span), ty => { unimplemented!("invalid store: support for storing {ty} has not been implemented") } } } - pub fn memset(&mut self) { + pub fn memset(&mut self, span: SourceSpan) { let dst = self.stack.pop().expect("operand stack is empty"); let count = self.stack.pop().expect("operand stack is empty"); let value = self.stack.pop().expect("operand stack is empty"); @@ -926,28 +1004,34 @@ impl<'a> OpEmitter<'a> { // Prepare to loop until `count` iterations have been performed let current_block = self.current_block; let body = self.function.create_block(); - self.emit_all(&[ - // [dst, count, value..] - Op::PushU32(0), // [i, dst, count, value..] - Op::Dup(2), // [count, i, dst, count, value..] - Op::GteImm(Felt::ZERO), // [count > 0, i, dst, count, value..] - Op::While(body), - ]); + self.emit_all( + &[ + // [dst, count, value..] + Op::PushU32(0), // [i, dst, count, value..] + Op::Dup(2), // [count, i, dst, count, value..] + Op::GteImm(Felt::ZERO), // [count > 0, i, dst, count, value..] + Op::While(body), + ], + span, + ); // Loop body - compute address for next value to be written let value_size = value.ty().size_in_bytes(); self.switch_to_block(body); - self.emit_all(&[ - // [i, dst, count, value..] - // Offset the pointer by the current iteration count * aligned size of value, and trap - // if it overflows - Op::Dup(1), // [dst, i, dst, count, value] - Op::Dup(1), // [i, dst, i, dst, count, value] - Op::PushU32(value_size.try_into().expect("invalid value size")), /* [value_size, i, - * dst, ..] */ - Op::U32OverflowingMadd, // [value_size * i + dst, i, dst, count, value] - Op::Assertz, // [aligned_dst, i, dst, count, value..] - ]); + self.emit_all( + &[ + // [i, dst, count, value..] + // Offset the pointer by the current iteration count * aligned size of value, and + // trap if it overflows + Op::Dup(1), // [dst, i, dst, count, value] + Op::Dup(1), // [i, dst, i, dst, count, value] + Op::PushU32(value_size.try_into().expect("invalid value size")), /* [value_size, i, + * dst, ..] */ + Op::U32OverflowingMadd, // [value_size * i + dst, i, dst, count, value] + Op::Assertz, // [aligned_dst, i, dst, count, value..] + ], + span, + ); // Loop body - move value to top of stack, swap with pointer self.stack.push(value); @@ -955,23 +1039,26 @@ impl<'a> OpEmitter<'a> { self.stack.push(dst.clone()); self.stack.push(dst.ty()); self.stack.push(dst.ty()); - self.dup(4); // [value, aligned_dst, i, dst, count, value] - self.swap(1); // [aligned_dst, value, i, dst, count, value] + self.dup(4, span); // [value, aligned_dst, i, dst, count, value] + self.swap(1, span); // [aligned_dst, value, i, dst, count, value] // Loop body - write value to destination - self.store(); // [i, dst, count, value] + self.store(span); // [i, dst, count, value] // Loop body - increment iteration count, determine whether to continue loop - self.emit_all(&[ - Op::U32WrappingAddImm(1), - Op::Dup(0), // [i++, i++, dst, count, value] - Op::Dup(3), // [count, i++, i++, dst, count, value] - Op::U32Gte, // [i++ >= count, i++, dst, count, value] - ]); + self.emit_all( + &[ + Op::U32WrappingAddImm(1), + Op::Dup(0), // [i++, i++, dst, count, value] + Op::Dup(3), // [count, i++, i++, dst, count, value] + Op::U32Gte, // [i++ >= count, i++, dst, count, value] + ], + span, + ); // Cleanup - at end of 'while' loop, drop the 4 operands remaining on the stack self.switch_to_block(current_block); - self.dropn(4); + self.dropn(4, span); } /// Copy `count * sizeof(*ty)` from a source address to a destination address. @@ -985,7 +1072,7 @@ impl<'a> OpEmitter<'a> { /// The semantics of this instruction are as follows: /// /// * The `` - pub fn memcpy(&mut self) { + pub fn memcpy(&mut self, span: SourceSpan) { let src = self.stack.pop().expect("operand stack is empty"); let dst = self.stack.pop().expect("operand stack is empty"); let count = self.stack.pop().expect("operand stack is empty"); @@ -1000,11 +1087,14 @@ impl<'a> OpEmitter<'a> { match value_size { // Word-sized values have an optimized intrinsic we can lean on 16 => { - self.emit_all(&[ - // [src, dst, count] - Op::Movup(2), // [count, src, dst] - Op::Exec("std::mem::memcopy".parse().unwrap()), - ]); + self.emit_all( + &[ + // [src, dst, count] + Op::Movup(2), // [count, src, dst] + Op::Exec("std::mem::memcopy".parse().unwrap()), + ], + span, + ); return; } // Values which can be broken up into word-sized chunks can piggy-back on the @@ -1012,13 +1102,16 @@ impl<'a> OpEmitter<'a> { // multiplying `count` by the number of words in each value size if size % 16 == 0 => { let factor = size / 16; - self.emit_all(&[ - // [src, dst, count] - Op::Movup(2), // [count, src, dst] - Op::U32OverflowingMulImm(factor), - Op::Assertz, // [count * (size / 16), src, dst] - Op::Exec("std::mem::memcopy".parse().unwrap()), - ]); + self.emit_all( + &[ + // [src, dst, count] + Op::Movup(2), // [count, src, dst] + Op::U32OverflowingMulImm(factor), + Op::Assertz, // [count * (size / 16), src, dst] + Op::Exec("std::mem::memcopy".parse().unwrap()), + ], + span, + ); return; } // For now, all other values fallback to the default implementation @@ -1028,31 +1121,37 @@ impl<'a> OpEmitter<'a> { // Prepare to loop until `count` iterations have been performed let current_block = self.current_block; let body = self.function.create_block(); - self.emit_all(&[ - // [src, dst, count] - Op::PushU32(0), // [i, src, dst, count] - Op::Dup(3), // [count, i, src, dst, count] - Op::GteImm(Felt::ZERO), // [count > 0, i, src, dst, count] - Op::While(body), - ]); + self.emit_all( + &[ + // [src, dst, count] + Op::PushU32(0), // [i, src, dst, count] + Op::Dup(3), // [count, i, src, dst, count] + Op::GteImm(Felt::ZERO), // [count > 0, i, src, dst, count] + Op::While(body), + ], + span, + ); // Loop body - compute address for next value to be written self.switch_to_block(body); // Compute the source and destination addresses - self.emit_all(&[ - // [i, src, dst, count] - Op::Dup(2), // [dst, i, src, dst, count] - Op::Dup(1), // [i, dst, i, src, dst, count] - Op::PushU32(value_size), // [offset, i, dst, i, src, dst, count] - Op::U32OverflowingMadd, - Op::Assertz, // [new_dst := i * offset + dst, i, src, dst, count] - Op::Dup(2), // [src, new_dst, i, src, dst, count] - Op::Dup(2), // [i, src, new_dst, i, src, dst, count] - Op::PushU32(value_size), // [offset, i, src, new_dst, i, src, dst, count] - Op::U32OverflowingMadd, - Op::Assertz, // [new_src := i * offset + src, new_dst, i, src, dst, count] - ]); + self.emit_all( + &[ + // [i, src, dst, count] + Op::Dup(2), // [dst, i, src, dst, count] + Op::Dup(1), // [i, dst, i, src, dst, count] + Op::PushU32(value_size), // [offset, i, dst, i, src, dst, count] + Op::U32OverflowingMadd, + Op::Assertz, // [new_dst := i * offset + dst, i, src, dst, count] + Op::Dup(2), // [src, new_dst, i, src, dst, count] + Op::Dup(2), // [i, src, new_dst, i, src, dst, count] + Op::PushU32(value_size), // [offset, i, src, new_dst, i, src, dst, count] + Op::U32OverflowingMadd, + Op::Assertz, // [new_src := i * offset + src, new_dst, i, src, dst, count] + ], + span, + ); // Load the source value self.stack.push(count.clone()); @@ -1061,39 +1160,42 @@ impl<'a> OpEmitter<'a> { self.stack.push(Type::U32); self.stack.push(dst.clone()); self.stack.push(src.clone()); - self.load(value_ty.clone()); // [value, new_dst, i, src, dst, count] + self.load(value_ty.clone(), span); // [value, new_dst, i, src, dst, count] // Write to the destination - self.swap(1); // [new_dst, value, i, src, dst, count] - self.store(); // [i, src, dst, count] + self.swap(1, span); // [new_dst, value, i, src, dst, count] + self.store(span); // [i, src, dst, count] // Increment iteration count, determine whether to continue loop - self.emit_all(&[ - Op::U32WrappingAddImm(1), - Op::Dup(0), // [i++, i++, src, dst, count] - Op::Dup(4), // [count, i++, i++, src, dst, count] - Op::U32Gte, // [i++ >= count, i++, src, dst, count] - ]); + self.emit_all( + &[ + Op::U32WrappingAddImm(1), + Op::Dup(0), // [i++, i++, src, dst, count] + Op::Dup(4), // [count, i++, i++, src, dst, count] + Op::U32Gte, // [i++ >= count, i++, src, dst, count] + ], + span, + ); // Cleanup - at end of 'while' loop, drop the 4 operands remaining on the stack self.switch_to_block(current_block); - self.dropn(4); + self.dropn(4, span); } /// Store a quartet of machine words (32-bit elements) to the operand stack - fn store_quad_word(&mut self, ptr: Option) { + fn store_quad_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.store_quad_word_imm(imm); + return self.store_quad_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::store_qw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::store_qw".parse().unwrap()), span); } - fn store_quad_word_imm(&mut self, ptr: NativePtr) { + fn store_quad_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { // For all other cases, more complicated loads are required let aligned = ptr.is_element_aligned(); match ptr.index { // Naturally-aligned - 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)]), + 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)], span), _ => { todo!() } @@ -1101,20 +1203,20 @@ impl<'a> OpEmitter<'a> { } /// Store a pair of machine words (32-bit elements) to the operand stack - fn store_double_word(&mut self, ptr: Option) { + fn store_double_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.store_double_word_imm(imm); + return self.store_double_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::store_dw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::store_dw".parse().unwrap()), span); } - fn store_double_word_imm(&mut self, ptr: NativePtr) { + fn store_double_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { // For all other cases, more complicated stores are required let aligned = ptr.is_element_aligned(); match ptr.index { // Naturally-aligned - 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)]), + 0 if aligned => self.emit_all(&[Op::Padw, Op::MemLoadwImm(ptr.waddr)], span), _ => { todo!() } @@ -1125,180 +1227,198 @@ impl<'a> OpEmitter<'a> { /// word /// /// Expects a native pointer triplet on the stack if an immediate address is not given. - fn store_word(&mut self, ptr: Option) { + fn store_word(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.store_word_imm(imm); + return self.store_word_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::store_sw".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::store_sw".parse().unwrap()), span); } /// Stores a single 32-bit machine word to the given immediate address. - fn store_word_imm(&mut self, ptr: NativePtr) { + fn store_word_imm(&mut self, ptr: NativePtr, span: SourceSpan) { let is_aligned = ptr.is_element_aligned(); let rshift = 32 - ptr.offset as u32; match ptr.index { - 0 if is_aligned => self.emit(Op::MemStoreImm(ptr.waddr)), + 0 if is_aligned => self.emit(Op::MemStoreImm(ptr.waddr), span), 0 => { let mask_hi = u32::MAX << rshift; let mask_lo = u32::MAX >> (ptr.offset as u32); - self.emit_all(&[ - // Load the full quad-word on to the operand stack + self.emit_all( + &[ + // Load the full quad-word on to the operand stack + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Manipulate the bits of the first two elements, such that the 32-bit + // word we're storing is placed at the correct offset from the start + // of the memory cell when viewing the cell as a set of 4 32-bit chunks + // + // First, mask out the bits we plan to overwrite with the store op from the + // first two elements + Op::Swap(1), + Op::PushU32(mask_lo), + Op::U32And, + Op::Swap(1), + Op::PushU32(mask_hi), + Op::U32And, + // Now, we need to shift/mask/split the 32-bit value into two elements, + // then combine them with the preserved bits of the + // original contents of the cell + // + // We start with the bits belonging to the first element in the cell + Op::Dup(4), + Op::U32ShrImm(ptr.offset as u32), + Op::U32Or, + // Then the bits belonging to the second element in the cell + Op::Movup(4), + Op::U32ShlImm(rshift), + Op::Movup(2), + Op::U32Or, + // Make sure the elements of the cell are in order + Op::Swap(1), + // Write the word back to the cell + Op::MemStorewImm(ptr.waddr), + // Clean up the operand stack + Op::Dropw, + ], + span, + ); + } + 1 if is_aligned => self.emit_all( + &[ + // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), - // Manipulate the bits of the first two elements, such that the 32-bit - // word we're storing is placed at the correct offset from the start - // of the memory cell when viewing the cell as a set of 4 32-bit chunks - // - // First, mask out the bits we plan to overwrite with the store op from the - // first two elements - Op::Swap(1), - Op::PushU32(mask_lo), - Op::U32And, - Op::Swap(1), - Op::PushU32(mask_hi), - Op::U32And, - // Now, we need to shift/mask/split the 32-bit value into two elements, then - // combine them with the preserved bits of the original - // contents of the cell - // - // We start with the bits belonging to the first element in the cell - Op::Dup(4), - Op::U32ShrImm(ptr.offset as u32), - Op::U32Or, - // Then the bits belonging to the second element in the cell + // Replace the stored element Op::Movup(4), - Op::U32ShlImm(rshift), - Op::Movup(2), - Op::U32Or, - // Make sure the elements of the cell are in order - Op::Swap(1), + Op::Swap(2), + Op::Drop, // Write the word back to the cell Op::MemStorewImm(ptr.waddr), // Clean up the operand stack Op::Dropw, - ]); - } - 1 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Replace the stored element - Op::Movup(4), - Op::Swap(2), - Op::Drop, - // Write the word back to the cell - Op::MemStorewImm(ptr.waddr), - // Clean up the operand stack - Op::Dropw, - ]), + ], + span, + ), 1 => { let mask_hi = u32::MAX << rshift; let mask_lo = u32::MAX >> (ptr.offset as u32); - self.emit_all(&[ - // Load the full quad-word on to the operand stack + self.emit_all( + &[ + // Load the full quad-word on to the operand stack + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Manipulate the bits of the middle two elements, such that the 32-bit + // word we're storing is placed at the correct offset from the start + // of the memory cell when viewing the cell as a set of 4 32-bit chunks + // + // First, mask out the bits we plan to overwrite with the store op from the + // first two elements + Op::Swap(2), // [elem3, elem2, elem1, elem4, value] + Op::PushU32(mask_lo), + Op::U32And, + Op::Swap(1), // [elem2, elem3, elem1, elem4, value] + Op::PushU32(mask_hi), + Op::U32And, + // Now, we need to shift/mask/split the 32-bit value into two elements, + // then combine them with the preserved bits of the + // original contents of the cell + // + // We start with the bits belonging to the second element in the cell + Op::Dup(4), // [value, elem2, elem3, elem1, elem4, value] + Op::U32ShrImm(ptr.offset as u32), + Op::U32Or, + // Then the bits belonging to the third element in the cell + Op::Movup(4), + Op::U32ShlImm(rshift), + Op::Movup(2), + Op::U32Or, // [elem3, elem2, elem1, elem4] + // Make sure the elements of the cell are in order + Op::Swap(1), + Op::Movup(2), // [elem1, elem2, elem3, elem4] + // Write the word back to the cell + Op::MemStorewImm(ptr.waddr), + // Clean up the operand stack + Op::Dropw, + ], + span, + ); + } + 2 if is_aligned => self.emit_all( + &[ + // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), - // Manipulate the bits of the middle two elements, such that the 32-bit - // word we're storing is placed at the correct offset from the start - // of the memory cell when viewing the cell as a set of 4 32-bit chunks - // - // First, mask out the bits we plan to overwrite with the store op from the - // first two elements - Op::Swap(2), // [elem3, elem2, elem1, elem4, value] - Op::PushU32(mask_lo), - Op::U32And, - Op::Swap(1), // [elem2, elem3, elem1, elem4, value] - Op::PushU32(mask_hi), - Op::U32And, - // Now, we need to shift/mask/split the 32-bit value into two elements, then - // combine them with the preserved bits of the original - // contents of the cell - // - // We start with the bits belonging to the second element in the cell - Op::Dup(4), // [value, elem2, elem3, elem1, elem4, value] - Op::U32ShrImm(ptr.offset as u32), - Op::U32Or, - // Then the bits belonging to the third element in the cell - Op::Movup(4), - Op::U32ShlImm(rshift), - Op::Movup(2), - Op::U32Or, // [elem3, elem2, elem1, elem4] - // Make sure the elements of the cell are in order - Op::Swap(1), - Op::Movup(2), // [elem1, elem2, elem3, elem4] + // Replace the stored element + Op::Movup(5), + Op::Swap(3), + Op::Drop, // Write the word back to the cell Op::MemStorewImm(ptr.waddr), // Clean up the operand stack Op::Dropw, - ]); - } - 2 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Replace the stored element - Op::Movup(5), - Op::Swap(3), - Op::Drop, - // Write the word back to the cell - Op::MemStorewImm(ptr.waddr), - // Clean up the operand stack - Op::Dropw, - ]), + ], + span, + ), 2 => { let mask_hi = u32::MAX << rshift; let mask_lo = u32::MAX >> (ptr.offset as u32); - self.emit_all(&[ - // Load the full quad-word on to the operand stack + self.emit_all( + &[ + // Load the full quad-word on to the operand stack + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Manipulate the bits of the last two elements, such that the 32-bit + // word we're storing is placed at the correct offset from the start + // of the memory cell when viewing the cell as a set of 4 32-bit chunks + // + // First, mask out the bits we plan to overwrite with the store op from the + // first two elements + Op::Swap(3), // [elem4, elem2, elem3, elem1, value] + Op::PushU32(mask_lo), + Op::U32And, + Op::Movup(2), // [elem3, elem4, elem2, elem1, value] + Op::PushU32(mask_hi), + Op::U32And, + // Now, we need to shift/mask/split the 32-bit value into two elements, + // then combine them with the preserved bits of the + // original contents of the cell + // + // We start with the bits belonging to the third element in the cell + Op::Dup(4), // [value, elem3, elem4, elem2, elem1, value] + Op::U32ShrImm(ptr.offset as u32), + Op::U32Or, + // Then the bits belonging to the fourth element in the cell + Op::Movup(4), + Op::U32ShlImm(rshift), + Op::Movup(2), + Op::U32Or, // [elem4, elem3, elem2, elem1] + // Make sure the elements of the cell are in order + Op::Swap(2), // [elem2, elem3, elem4, elem1] + Op::Movup(3), // [elem1, elem2, elem3, elem4] + // Write the word back to the cell + Op::MemStorewImm(ptr.waddr), + // Clean up the operand stack + Op::Dropw, + ], + span, + ); + } + 3 if is_aligned => self.emit_all( + &[ + // Load a quad-word Op::Padw, Op::MemLoadwImm(ptr.waddr), - // Manipulate the bits of the last two elements, such that the 32-bit - // word we're storing is placed at the correct offset from the start - // of the memory cell when viewing the cell as a set of 4 32-bit chunks - // - // First, mask out the bits we plan to overwrite with the store op from the - // first two elements - Op::Swap(3), // [elem4, elem2, elem3, elem1, value] - Op::PushU32(mask_lo), - Op::U32And, - Op::Movup(2), // [elem3, elem4, elem2, elem1, value] - Op::PushU32(mask_hi), - Op::U32And, - // Now, we need to shift/mask/split the 32-bit value into two elements, then - // combine them with the preserved bits of the original - // contents of the cell - // - // We start with the bits belonging to the third element in the cell - Op::Dup(4), // [value, elem3, elem4, elem2, elem1, value] - Op::U32ShrImm(ptr.offset as u32), - Op::U32Or, - // Then the bits belonging to the fourth element in the cell + // Replace the stored element Op::Movup(4), - Op::U32ShlImm(rshift), - Op::Movup(2), - Op::U32Or, // [elem4, elem3, elem2, elem1] - // Make sure the elements of the cell are in order - Op::Swap(2), // [elem2, elem3, elem4, elem1] - Op::Movup(3), // [elem1, elem2, elem3, elem4] + Op::Drop, // Write the word back to the cell Op::MemStorewImm(ptr.waddr), // Clean up the operand stack Op::Dropw, - ]); - } - 3 if is_aligned => self.emit_all(&[ - // Load a quad-word - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Replace the stored element - Op::Movup(4), - Op::Drop, - // Write the word back to the cell - Op::MemStorewImm(ptr.waddr), - // Clean up the operand stack - Op::Dropw, - ]), + ], + span, + ), 3 => { // This is a rather annoying edge case, as it requires us to store bits // across two different words. We start with the "hi" bits that go at @@ -1306,41 +1426,44 @@ impl<'a> OpEmitter<'a> { // fashion let mask_hi = u32::MAX << rshift; let mask_lo = u32::MAX >> (ptr.offset as u32); - self.emit_all(&[ - // Load the full quad-word on to the operand stack - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - // Manipulate the bits of the last element, such that the "high" bits - // of the 32-bit word we're storing is placed at the correct offset from the - // start of the memory cell when viewing the cell as a set - // of 4 32-bit chunks - // - // First, mask out the bits we plan to overwrite with the store op from the - // last element - Op::Swap(3), // [elem4, elem2, elem3, elem1, value] - Op::PushU32(mask_lo), - Op::U32And, - // Now, we need to shift/mask/split the 32-bit value into the bits that will be - // merged with this word - Op::Dup(4), // [value, elem4, elem2, elem3, elem1, value] - Op::U32ShrImm(ptr.offset as u32), - Op::U32Or, - // Move the fourth element back into place - Op::Swap(3), // [elem1, elem2, elem3, elem4, value] - // Write the first word and clear the operand stack - Op::MemStorewImm(ptr.waddr), - Op::Dropw, - // Compute the bits of the value that we'll merge into the second word - Op::U32ShlImm(rshift), - // Load the first element of the second word - Op::MemLoadImm(ptr.waddr + 1), - // Mask out the bits we plan to overwrite - Op::PushU32(mask_hi), - Op::U32And, - // Merge the bits and write back the second word - Op::U32Or, - Op::MemStoreImm(ptr.waddr + 1), - ]); + self.emit_all( + &[ + // Load the full quad-word on to the operand stack + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + // Manipulate the bits of the last element, such that the "high" bits + // of the 32-bit word we're storing is placed at the correct offset from + // the start of the memory cell when viewing the + // cell as a set of 4 32-bit chunks + // + // First, mask out the bits we plan to overwrite with the store op from the + // last element + Op::Swap(3), // [elem4, elem2, elem3, elem1, value] + Op::PushU32(mask_lo), + Op::U32And, + // Now, we need to shift/mask/split the 32-bit value into the bits that + // will be merged with this word + Op::Dup(4), // [value, elem4, elem2, elem3, elem1, value] + Op::U32ShrImm(ptr.offset as u32), + Op::U32Or, + // Move the fourth element back into place + Op::Swap(3), // [elem1, elem2, elem3, elem4, value] + // Write the first word and clear the operand stack + Op::MemStorewImm(ptr.waddr), + Op::Dropw, + // Compute the bits of the value that we'll merge into the second word + Op::U32ShlImm(rshift), + // Load the first element of the second word + Op::MemLoadImm(ptr.waddr + 1), + // Mask out the bits we plan to overwrite + Op::PushU32(mask_hi), + Op::U32And, + // Merge the bits and write back the second word + Op::U32Or, + Op::MemStoreImm(ptr.waddr + 1), + ], + span, + ); } _ => unreachable!(), } @@ -1349,113 +1472,122 @@ impl<'a> OpEmitter<'a> { /// Store a field element to a naturally aligned address, either immediate or dynamic /// /// A native pointer triplet is expected on the stack if an immediate is not given. - fn store_felt(&mut self, ptr: Option) { + fn store_felt(&mut self, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.store_felt_imm(imm); + return self.store_felt_imm(imm, span); } - self.emit(Op::Exec("intrinsics::mem::store_felt".parse().unwrap())); + self.emit(Op::Exec("intrinsics::mem::store_felt".parse().unwrap()), span); } - fn store_felt_imm(&mut self, ptr: NativePtr) { + fn store_felt_imm(&mut self, ptr: NativePtr, span: SourceSpan) { assert!(ptr.is_element_aligned(), "felt values must be naturally aligned"); match ptr.index { - 0 => self.emit(Op::MemStoreImm(ptr.waddr)), + 0 => self.emit(Op::MemStoreImm(ptr.waddr), span), 1 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Movup(4), - Op::Swap(2), - Op::Drop, - Op::MemStorewImm(ptr.waddr), - Op::Dropw, - ]); + self.emit_all( + &[ + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Movup(4), + Op::Swap(2), + Op::Drop, + Op::MemStorewImm(ptr.waddr), + Op::Dropw, + ], + span, + ); } 2 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Movup(4), - Op::Swap(3), - Op::Drop, - Op::MemStorewImm(ptr.waddr), - Op::Dropw, - ]); + self.emit_all( + &[ + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Movup(4), + Op::Swap(3), + Op::Drop, + Op::MemStorewImm(ptr.waddr), + Op::Dropw, + ], + span, + ); } 3 => { - self.emit_all(&[ - Op::Padw, - Op::MemLoadwImm(ptr.waddr), - Op::Movup(3), - Op::Drop, - Op::MemStorewImm(ptr.waddr), - Op::Dropw, - ]); + self.emit_all( + &[ + Op::Padw, + Op::MemLoadwImm(ptr.waddr), + Op::Movup(3), + Op::Drop, + Op::MemStorewImm(ptr.waddr), + Op::Dropw, + ], + span, + ); } _ => unreachable!(), } } - fn store_small(&mut self, ty: &Type, ptr: Option) { + fn store_small(&mut self, ty: &Type, ptr: Option, span: SourceSpan) { if let Some(imm) = ptr { - return self.store_small_imm(ty, imm); + return self.store_small_imm(ty, imm, span); } let type_size = ty.size_in_bits(); if type_size == 32 { - self.store_word(ptr); + self.store_word(ptr, span); return; } // Duplicate the address - self.emit_all(&[Op::Dup(2), Op::Dup(2), Op::Dup(2)]); + self.emit_all(&[Op::Dup(2), Op::Dup(2), Op::Dup(2)], span); // Load the current 32-bit value at `ptr` - self.load_word(ptr); + self.load_word(ptr, span); // Mask out the bits we're going to be writing from the loaded value let mask = u32::MAX << type_size; - self.const_mask_u32(mask); + self.const_mask_u32(mask, span); // Mix in the bits we want to write: [masked, addr1, addr2, addr3, value] - self.emit(Op::Movup(5)); - self.bor_u32(); + self.emit(Op::Movup(5), span); + self.bor_u32(span); // Store the combined bits: [value, addr1, addr2, addr3] - self.emit(Op::Movdn(4)); - self.store_word(ptr); + self.emit(Op::Movdn(4), span); + self.store_word(ptr, span); } - fn store_small_imm(&mut self, ty: &Type, ptr: NativePtr) { + fn store_small_imm(&mut self, ty: &Type, ptr: NativePtr, span: SourceSpan) { assert!(ptr.alignment() as usize >= ty.min_alignment()); let type_size = ty.size_in_bits(); if type_size == 32 { - self.store_word_imm(ptr); + self.store_word_imm(ptr, span); return; } // Load the current 32-bit value at `ptr` - self.load_word_imm(ptr); + self.load_word_imm(ptr, span); // Mask out the bits we're going to be writing from the loaded value let mask = u32::MAX << type_size; - self.const_mask_u32(mask); + self.const_mask_u32(mask, span); // Mix in the bits we want to write - self.emit(Op::Movup(4)); - self.bor_u32(); + self.emit(Op::Movup(4), span); + self.bor_u32(span); // Store the combined bits - self.store_word_imm(ptr); + self.store_word_imm(ptr, span); } - fn store_array(&mut self, _element_ty: &Type, _ptr: Option) { + fn store_array(&mut self, _element_ty: &Type, _ptr: Option, _span: SourceSpan) { todo!() } - fn store_struct(&mut self, _ty: &StructType, _ptr: Option) { + fn store_struct(&mut self, _ty: &StructType, _ptr: Option, _span: SourceSpan) { todo!() } } diff --git a/codegen/masm/src/codegen/emit/mod.rs b/codegen/masm/src/codegen/emit/mod.rs index 2c5d8c868..8d932786e 100644 --- a/codegen/masm/src/codegen/emit/mod.rs +++ b/codegen/masm/src/codegen/emit/mod.rs @@ -1,3 +1,5 @@ +use midenc_hir::diagnostics::Span; + /// The field modulus for Miden's prime field pub const P: u64 = (2u128.pow(64) - 2u128.pow(32) + 1) as u64; @@ -88,7 +90,7 @@ pub mod unary; use core::ops::{Deref, DerefMut}; use miden_assembly::ast::InvokeKind; -use midenc_hir::{self as hir, Immediate, Type}; +use midenc_hir::{self as hir, diagnostics::SourceSpan, Immediate, Type}; use super::{Operand, OperandStack}; use crate::masm::{self as masm, Op}; @@ -118,14 +120,14 @@ impl<'a> InstOpEmitter<'a> { } } - pub fn exec(&mut self, callee: hir::FunctionIdent) { + pub fn exec(&mut self, callee: hir::FunctionIdent, span: SourceSpan) { let import = self.dfg.get_import(&callee).unwrap(); - self.emitter.exec(import); + self.emitter.exec(import, span); } - pub fn syscall(&mut self, callee: hir::FunctionIdent) { + pub fn syscall(&mut self, callee: hir::FunctionIdent, span: SourceSpan) { let import = self.dfg.get_import(&callee).unwrap(); - self.emitter.syscall(import); + self.emitter.syscall(import, span); } #[inline(always)] @@ -218,30 +220,30 @@ impl<'a> OpEmitter<'a> { /// Emit `op` to the current block #[inline(always)] - pub fn emit(&mut self, op: masm::Op) { + pub fn emit(&mut self, op: masm::Op, span: SourceSpan) { self.maybe_register_invoke(&op); - self.current_block().push(op) + self.current_block().push(op, span) } /// Emit `n` copies of `op` to the current block #[inline(always)] - pub fn emit_n(&mut self, count: usize, op: masm::Op) { + pub fn emit_n(&mut self, count: usize, op: masm::Op, span: SourceSpan) { self.maybe_register_invoke(&op); - self.current_block().push_n(count, op); + self.current_block().push_n(count, op, span); } /// Emit `ops` to the current block #[inline(always)] - pub fn emit_all(&mut self, ops: &[masm::Op]) { + pub fn emit_all(&mut self, ops: &[masm::Op], span: SourceSpan) { for op in ops { self.maybe_register_invoke(op); } - self.current_block().extend_from_slice(ops); + self.current_block().extend(ops.iter().copied().map(|op| Span::new(span, op))); } /// Emit `n` copies of the sequence `ops` to the current block #[inline(always)] - pub fn emit_repeat(&mut self, count: usize, ops: &[masm::Op]) { + pub fn emit_repeat(&mut self, count: usize, ops: &[Span]) { for op in ops { self.maybe_register_invoke(op); } @@ -252,7 +254,7 @@ impl<'a> OpEmitter<'a> { #[inline] pub fn emit_template(&mut self, count: usize, template: F) where - F: Fn(usize) -> [Op; N], + F: Fn(usize) -> [Span; N], { for op in template(0) { self.maybe_register_invoke(&op); @@ -269,28 +271,28 @@ impl<'a> OpEmitter<'a> { /// /// This has no effect on the state of the emulated operand stack #[inline] - pub fn push_immediate(&mut self, imm: Immediate) { + pub fn push_immediate(&mut self, imm: Immediate, span: SourceSpan) { match imm { - Immediate::I1(i) => self.emit(Op::PushU8(i as u8)), - Immediate::I8(i) => self.emit(Op::PushU8(i as u8)), - Immediate::U8(i) => self.emit(Op::PushU8(i)), - Immediate::U16(i) => self.emit(Op::PushU32(i as u32)), - Immediate::I16(i) => self.emit(Op::PushU32(i as u16 as u32)), - Immediate::U32(i) => self.emit(Op::PushU32(i)), - Immediate::I32(i) => self.emit(Op::PushU32(i as u32)), - Immediate::U64(i) => self.push_u64(i), - Immediate::I64(i) => self.push_i64(i), - Immediate::U128(i) => self.push_u128(i), - Immediate::I128(i) => self.push_i128(i), - Immediate::Felt(i) => self.emit(Op::Push(i)), + Immediate::I1(i) => self.emit(Op::PushU8(i as u8), span), + Immediate::I8(i) => self.emit(Op::PushU8(i as u8), span), + Immediate::U8(i) => self.emit(Op::PushU8(i), span), + Immediate::U16(i) => self.emit(Op::PushU32(i as u32), span), + Immediate::I16(i) => self.emit(Op::PushU32(i as u16 as u32), span), + Immediate::U32(i) => self.emit(Op::PushU32(i), span), + Immediate::I32(i) => self.emit(Op::PushU32(i as u32), span), + Immediate::U64(i) => self.push_u64(i, span), + Immediate::I64(i) => self.push_i64(i, span), + Immediate::U128(i) => self.push_u128(i, span), + Immediate::I128(i) => self.push_i128(i, span), + Immediate::Felt(i) => self.emit(Op::Push(i), span), Immediate::F64(_) => unimplemented!("floating-point immediates are not supported"), } } /// Push a literal on the operand stack, and update the emulated stack accordingly - pub fn literal>(&mut self, imm: I) { + pub fn literal>(&mut self, imm: I, span: SourceSpan) { let imm = imm.into(); - self.push_immediate(imm); + self.push_immediate(imm, span); self.stack.push(imm); } @@ -308,7 +310,7 @@ impl<'a> OpEmitter<'a> { /// Duplicate an item on the stack to the top #[inline] #[track_caller] - pub fn dup(&mut self, i: u8) { + pub fn dup(&mut self, i: u8, span: SourceSpan) { assert_valid_stack_index!(i); let index = i as usize; let i = self.stack.effective_index(index) as u8; @@ -318,14 +320,14 @@ impl<'a> OpEmitter<'a> { let n = last.size(); let offset = (n - 1) as u8; for _ in 0..n { - self.emit(Op::Dup(i + offset)); + self.emit(Op::Dup(i + offset), span); } } /// Move an item on the stack to the top #[inline] #[track_caller] - pub fn movup(&mut self, i: u8) { + pub fn movup(&mut self, i: u8, span: SourceSpan) { assert_valid_stack_index!(i); let index = i as usize; let i = self.stack.effective_index(index) as u8; @@ -335,14 +337,14 @@ impl<'a> OpEmitter<'a> { let n = moved.size(); let offset = (n - 1) as u8; for _ in 0..n { - self.emit(Op::Movup(i + offset)); + self.emit(Op::Movup(i + offset), span); } } /// Move an item from the top of the stack to the `n`th position #[inline] #[track_caller] - pub fn movdn(&mut self, i: u8) { + pub fn movdn(&mut self, i: u8, span: SourceSpan) { assert_valid_stack_index!(i); let index = i as usize; let i = self.stack.effective_index_inclusive(index) as u8; @@ -351,14 +353,14 @@ impl<'a> OpEmitter<'a> { self.stack.movdn(index); // Emit low-level instructions corresponding to the operand we moved for _ in 0..top_size { - self.emit(Op::Movdn(i)); + self.emit(Op::Movdn(i), span); } } /// Swap an item with the top of the stack #[inline] #[track_caller] - pub fn swap(&mut self, i: u8) { + pub fn swap(&mut self, i: u8, span: SourceSpan) { assert!(i > 0, "swap requires a non-zero index"); assert_valid_stack_index!(i); let index = i as usize; @@ -368,37 +370,37 @@ impl<'a> OpEmitter<'a> { self.stack.swap(index); match (src, dst) { (1, 1) => { - self.emit(Op::Swap(i)); + self.emit(Op::Swap(i), span); } (1, n) if i == 1 => { // We can simply move the top element below the `dst` operand - self.emit(Op::Movdn(i + (n - 1))); + self.emit(Op::Movdn(i + (n - 1)), span); } (n, 1) if i == n => { // We can simply move the `dst` element to the top - self.emit(Op::Movup(i)); + self.emit(Op::Movup(i), span); } (n, m) if i == n => { // We can simply move `dst` down for _ in 0..n { - self.emit(Op::Movdn(i + (m - 1))); + self.emit(Op::Movdn(i + (m - 1)), span); } } (n, m) => { assert!(i >= n); let offset = m - 1; for _ in 0..n { - self.emit(Op::Movdn(i + offset)); + self.emit(Op::Movdn(i + offset), span); } let i = (i as i8 + (m as i8 - n as i8)) as u8; match i - 1 { 1 => { assert_eq!(m, 1); - self.emit(Op::Swap(1)); + self.emit(Op::Swap(1), span); } i => { for _ in 0..m { - self.emit(Op::Movup(i)); + self.emit(Op::Movup(i), span); } } } @@ -409,18 +411,18 @@ impl<'a> OpEmitter<'a> { /// Drop the top operand on the stack #[inline] #[track_caller] - pub fn drop(&mut self) { + pub fn drop(&mut self, span: SourceSpan) { let elem = self.stack.pop().expect("operand stack is empty"); match elem.size() { 1 => { - self.emit(Op::Drop); + self.emit(Op::Drop, span); } 4 => { - self.emit(Op::Dropw); + self.emit(Op::Dropw, span); } n => { for _ in 0..n { - self.emit(Op::Drop); + self.emit(Op::Drop, span); } } } @@ -429,27 +431,27 @@ impl<'a> OpEmitter<'a> { /// Drop the top `n` operands on the stack #[inline] #[track_caller] - pub fn dropn(&mut self, n: usize) { + pub fn dropn(&mut self, n: usize, span: SourceSpan) { assert!(self.stack.len() >= n); assert_ne!(n, 0); let raw_len: usize = self.stack.iter().rev().take(n).map(|o| o.size()).sum(); self.stack.dropn(n); match raw_len { 1 => { - self.emit(Op::Drop); + self.emit(Op::Drop, span); } 4 => { - self.emit(Op::Dropw); + self.emit(Op::Dropw, span); } n => { - self.emit_n(n / 4, Op::Dropw); - self.emit_n(n % 4, Op::Drop); + self.emit_n(n / 4, Op::Dropw, span); + self.emit_n(n % 4, Op::Drop, span); } } } /// Remove all but the top `n` values on the operand stack - pub fn truncate_stack(&mut self, n: usize) { + pub fn truncate_stack(&mut self, n: usize, span: SourceSpan) { let stack_size = self.stack.len(); let num_to_drop = stack_size - n; @@ -460,8 +462,8 @@ impl<'a> OpEmitter<'a> { if stack_size == num_to_drop { let raw_size = self.stack.raw_len(); self.stack.dropn(num_to_drop); - self.emit_n(raw_size / 4, Op::Dropw); - self.emit_n(raw_size % 4, Op::Dropw); + self.emit_n(raw_size / 4, Op::Dropw, span); + self.emit_n(raw_size % 4, Op::Dropw, span); return; } @@ -471,12 +473,12 @@ impl<'a> OpEmitter<'a> { if n == 1 { match stack_size { 2 => { - self.swap(1); - self.drop(); + self.swap(1, span); + self.drop(span); } n => { - self.movdn(n as u8 - 1); - self.dropn(n - 1); + self.movdn(n as u8 - 1, span); + self.dropn(n - 1, span); } } return; @@ -487,23 +489,23 @@ impl<'a> OpEmitter<'a> { // come up with a smarter/more efficient method for offset in 0..num_to_drop { let index = stack_size - 1 - offset; - self.drop_operand_at_position(index); + self.drop_operand_at_position(index, span); } } /// Remove the `n`th value from the top of the operand stack - pub fn drop_operand_at_position(&mut self, n: usize) { + pub fn drop_operand_at_position(&mut self, n: usize, span: SourceSpan) { match n { 0 => { - self.drop(); + self.drop(span); } 1 => { - self.swap(1); - self.drop(); + self.swap(1, span); + self.drop(span); } n => { - self.movup(n as u8); - self.drop(); + self.movup(n as u8, span); + self.drop(span); } } } @@ -520,28 +522,29 @@ impl<'a> OpEmitter<'a> { n: usize, m: usize, is_commutative_binary_operand: bool, + span: SourceSpan, ) { match (n, m) { (0, 0) => { - self.dup(0); + self.dup(0, span); } (actual, 0) => { - self.dup(actual as u8); + self.dup(actual as u8, span); } (actual, 1) => { // If the dependent is binary+commutative, we can // leave operands in either the 0th or 1st position, // as long as both operands are on top of the stack if !is_commutative_binary_operand { - self.dup(actual as u8); - self.swap(1); + self.dup(actual as u8, span); + self.swap(1, span); } else { - self.dup(actual as u8); + self.dup(actual as u8, span); } } (actual, expected) => { - self.dup(actual as u8); - self.movdn(expected as u8); + self.dup(actual as u8, span); + self.movdn(expected as u8, span); } } } @@ -558,6 +561,7 @@ impl<'a> OpEmitter<'a> { n: usize, m: usize, is_commutative_binary_operand: bool, + span: SourceSpan, ) { match (n, m) { (n, m) if n == m => (), @@ -566,19 +570,19 @@ impl<'a> OpEmitter<'a> { // leave operands in either the 0th or 1st position, // as long as both operands are on top of the stack if !is_commutative_binary_operand { - self.swap(1); + self.swap(1, span); } } (actual, 0) => { - self.movup(actual as u8); + self.movup(actual as u8, span); } (actual, 1) => { - self.movup(actual as u8); - self.swap(1); + self.movup(actual as u8, span); + self.swap(1, span); } (actual, expected) => { - self.movup(actual as u8); - self.movdn(expected as u8); + self.movup(actual as u8, span); + self.movdn(expected as u8, span); } } } @@ -624,21 +628,21 @@ mod tests { let four = Immediate::U64(2u64.pow(32)); let five = Immediate::U64(2u64.pow(32) | 2u64.pow(33) | u32::MAX as u64); - emitter.literal(one); - emitter.literal(two); - emitter.literal(three); - emitter.literal(four); - emitter.literal(five); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); + emitter.literal(three, SourceSpan::default()); + emitter.literal(four, SourceSpan::default()); + emitter.literal(five, SourceSpan::default()); { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 5); - assert_eq!(ops[0], Op::PushU32(1)); - assert_eq!(ops[1], Op::PushU32(2)); - assert_eq!(ops[2], Op::PushU8(3)); - assert_eq!(ops[3], Op::Push2([Felt::new(1), Felt::ZERO])); - assert_eq!(ops[4], Op::Push2([Felt::new(3), Felt::new(u32::MAX as u64)])); + assert_eq!(ops[0].into_inner(), Op::PushU32(1)); + assert_eq!(ops[1].into_inner(), Op::PushU32(2)); + assert_eq!(ops[2].into_inner(), Op::PushU8(3)); + assert_eq!(ops[3].into_inner(), Op::Push2([Felt::new(1), Felt::ZERO])); + assert_eq!(ops[4].into_inner(), Op::Push2([Felt::new(3), Felt::new(u32::MAX as u64)])); } assert_eq!(emitter.stack()[0], five); @@ -647,7 +651,7 @@ mod tests { assert_eq!(emitter.stack()[3], two); assert_eq!(emitter.stack()[4], one); - emitter.dup(0); + emitter.dup(0, SourceSpan::default()); assert_eq!(emitter.stack()[0], five); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], four); @@ -658,12 +662,12 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 7); - assert_eq!(ops[5], Op::Dup(1)); - assert_eq!(ops[6], Op::Dup(1)); + assert_eq!(ops[5].into_inner(), Op::Dup(1)); + assert_eq!(ops[6].into_inner(), Op::Dup(1)); } assert_eq!(emitter.stack().effective_index(3), 6); - emitter.dup(3); + emitter.dup(3, SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], five); @@ -675,12 +679,12 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 8); - assert_eq!(ops[6], Op::Dup(1)); - assert_eq!(ops[7], Op::Dup(6)); + assert_eq!(ops[6].into_inner(), Op::Dup(1)); + assert_eq!(ops[7].into_inner(), Op::Dup(6)); } assert_eq!(emitter.stack().effective_index(1), 1); - emitter.swap(1); + emitter.swap(1, SourceSpan::default()); assert_eq!(emitter.stack().effective_index(1), 2); assert_eq!(emitter.stack()[0], five); assert_eq!(emitter.stack()[1], three); @@ -693,12 +697,12 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 9); - assert_eq!(ops[7], Op::Dup(6)); - assert_eq!(ops[8], Op::Movdn(2)); + assert_eq!(ops[7].into_inner(), Op::Dup(6)); + assert_eq!(ops[8].into_inner(), Op::Movdn(2)); } assert_eq!(emitter.stack().effective_index(3), 5); - emitter.swap(3); + emitter.swap(3, SourceSpan::default()); assert_eq!(emitter.stack()[0], four); assert_eq!(emitter.stack()[1], three); assert_eq!(emitter.stack()[2], five); @@ -710,15 +714,17 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 13); - assert_eq!(ops[8], Op::Movdn(2)); // [five_a, five_b, three, five_c, five_d, four_a, four_b] - assert_eq!(ops[9], Op::Movdn(6)); // [five_b, three, five_c, five_d, four_a, four_b, five_a] - assert_eq!(ops[10], Op::Movdn(6)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] - assert_eq!(ops[11], Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[12], Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, - // five_b] + assert_eq!(ops[8].into_inner(), Op::Movdn(2)); // [five_a, five_b, three, five_c, five_d, four_a, four_b] + assert_eq!(ops[9].into_inner(), Op::Movdn(6)); // [five_b, three, five_c, five_d, four_a, four_b, five_a] + assert_eq!(ops[10].into_inner(), Op::Movdn(6)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] + assert_eq!(ops[11].into_inner(), Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[12].into_inner(), Op::Movup(4)); // [four_a, four_b, three, five_c, + // five_d, + // five_a, + // five_b] } - emitter.movdn(2); + emitter.movdn(2, SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], four); @@ -730,16 +736,18 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 15); - assert_eq!(ops[9], Op::Movdn(6)); // [five_b, three, five_c, five_d, four_a, four_b, five_a] - assert_eq!(ops[10], Op::Movdn(6)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] - assert_eq!(ops[11], Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[12], Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, five_b] - assert_eq!(ops[13], Op::Movdn(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[14], Op::Movdn(4)); // [three, five_c, five_d, four_a, four_b, five_a, - // five_b] + assert_eq!(ops[9].into_inner(), Op::Movdn(6)); // [five_b, three, five_c, five_d, four_a, four_b, five_a] + assert_eq!(ops[10].into_inner(), Op::Movdn(6)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] + assert_eq!(ops[11].into_inner(), Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[12].into_inner(), Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, five_b] + assert_eq!(ops[13].into_inner(), Op::Movdn(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[14].into_inner(), Op::Movdn(4)); // [three, five_c, five_d, four_a, + // four_b, + // five_a, + // five_b] } - emitter.movup(2); + emitter.movup(2, SourceSpan::default()); assert_eq!(emitter.stack()[0], four); assert_eq!(emitter.stack()[1], three); assert_eq!(emitter.stack()[2], five); @@ -751,14 +759,16 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 17); - assert_eq!(ops[13], Op::Movdn(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[14], Op::Movdn(4)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] - assert_eq!(ops[15], Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[16], Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, - // five_b] + assert_eq!(ops[13].into_inner(), Op::Movdn(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[14].into_inner(), Op::Movdn(4)); // [three, five_c, five_d, four_a, four_b, five_a, five_b] + assert_eq!(ops[15].into_inner(), Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[16].into_inner(), Op::Movup(4)); // [four_a, four_b, three, five_c, + // five_d, + // five_a, + // five_b] } - emitter.drop(); + emitter.drop(SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], five); @@ -770,13 +780,13 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 19); - assert_eq!(ops[15], Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] - assert_eq!(ops[16], Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, five_b] - assert_eq!(ops[17], Op::Drop); // [four_b, three, five_c, five_d, five_a, five_b] - assert_eq!(ops[18], Op::Drop); // [three, five_c, five_d, five_a, five_b] + assert_eq!(ops[15].into_inner(), Op::Movup(4)); // [four_b, three, five_c, five_d, four_a, five_a, five_b] + assert_eq!(ops[16].into_inner(), Op::Movup(4)); // [four_a, four_b, three, five_c, five_d, five_a, five_b] + assert_eq!(ops[17].into_inner(), Op::Drop); // [four_b, three, five_c, five_d, five_a, five_b] + assert_eq!(ops[18].into_inner(), Op::Drop); // [three, five_c, five_d, five_a, five_b] } - emitter.copy_operand_to_position(5, 3, false); + emitter.copy_operand_to_position(5, 3, false, SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], five); @@ -784,14 +794,14 @@ mod tests { assert_eq!(emitter.stack()[4], three); assert_eq!(emitter.stack()[5], two); - emitter.drop_operand_at_position(4); + emitter.drop_operand_at_position(4, SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], five); assert_eq!(emitter.stack()[3], one); assert_eq!(emitter.stack()[4], two); - emitter.move_operand_to_position(4, 2, false); + emitter.move_operand_to_position(4, 2, false, SourceSpan::default()); assert_eq!(emitter.stack()[0], three); assert_eq!(emitter.stack()[1], five); assert_eq!(emitter.stack()[2], two); @@ -847,7 +857,7 @@ mod tests { assert_eq!(emitter.stack()[4], v10); assert_eq!(emitter.stack()[2], v15); - emitter.copy_operand_to_position(4, 2, false); + emitter.copy_operand_to_position(4, 2, false, SourceSpan::default()); assert_eq!(emitter.stack()[5], v10); assert_eq!(emitter.stack()[2], v10); @@ -855,8 +865,8 @@ mod tests { let block = emitter.current_block(); let ops = block.ops.as_slice(); assert_eq!(ops.len(), 2); - assert_eq!(ops[0], Op::Dup(4)); - assert_eq!(ops[1], Op::Movdn(2)); + assert_eq!(ops[0].into_inner(), Op::Dup(4)); + assert_eq!(ops[1].into_inner(), Op::Movdn(2)); } } @@ -870,26 +880,26 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.add_imm(one, Overflow::Checked); + emitter.add_imm(one, Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.add(Overflow::Checked); + emitter.add(Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); - emitter.add_imm(one, Overflow::Overflowing); + emitter.add_imm(one, Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); - emitter.drop(); - emitter.dup(0); - emitter.add(Overflow::Overflowing); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.add(Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); @@ -905,26 +915,26 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.sub_imm(one, Overflow::Checked); + emitter.sub_imm(one, Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.sub(Overflow::Checked); + emitter.sub(Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); - emitter.sub_imm(one, Overflow::Overflowing); + emitter.sub_imm(one, Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); - emitter.drop(); - emitter.dup(0); - emitter.sub(Overflow::Overflowing); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.sub(Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); @@ -940,26 +950,26 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.mul_imm(one, Overflow::Checked); + emitter.mul_imm(one, Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.mul(Overflow::Checked); + emitter.mul(Overflow::Checked, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); - emitter.mul_imm(one, Overflow::Overflowing); + emitter.mul_imm(one, Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); - emitter.drop(); - emitter.dup(0); - emitter.mul(Overflow::Overflowing); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.mul(Overflow::Overflowing, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], Type::U32); @@ -975,20 +985,20 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.eq_imm(two); + emitter.eq_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.assert(None); + emitter.assert(None, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], one); - emitter.dup(0); - emitter.eq(); + emitter.dup(0, SourceSpan::default()); + emitter.eq(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1003,20 +1013,20 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.neq_imm(two); + emitter.neq_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.assertz(None); + emitter.assertz(None, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], one); - emitter.dup(0); - emitter.neq(); + emitter.dup(0, SourceSpan::default()); + emitter.neq(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1031,15 +1041,15 @@ mod tests { let t = Immediate::I1(true); let f = Immediate::I1(false); - emitter.literal(t); - emitter.literal(f); + emitter.literal(t, SourceSpan::default()); + emitter.literal(f, SourceSpan::default()); - emitter.and_imm(t); + emitter.and_imm(t, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], t); - emitter.and(); + emitter.and(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1054,15 +1064,15 @@ mod tests { let t = Immediate::I1(true); let f = Immediate::I1(false); - emitter.literal(t); - emitter.literal(f); + emitter.literal(t, SourceSpan::default()); + emitter.literal(f, SourceSpan::default()); - emitter.or_imm(t); + emitter.or_imm(t, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], t); - emitter.or(); + emitter.or(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1077,15 +1087,15 @@ mod tests { let t = Immediate::I1(true); let f = Immediate::I1(false); - emitter.literal(t); - emitter.literal(f); + emitter.literal(t, SourceSpan::default()); + emitter.literal(f, SourceSpan::default()); - emitter.xor_imm(t); + emitter.xor_imm(t, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], t); - emitter.xor(); + emitter.xor(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1099,9 +1109,9 @@ mod tests { let t = Immediate::I1(true); - emitter.literal(t); + emitter.literal(t, SourceSpan::default()); - emitter.not(); + emitter.not(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1116,17 +1126,17 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.gt_imm(two); + emitter.gt_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.drop(); - emitter.dup(0); - emitter.gt(); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.gt(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1141,17 +1151,17 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.gte_imm(two); + emitter.gte_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.drop(); - emitter.dup(0); - emitter.gte(); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.gte(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1166,17 +1176,17 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.lt_imm(two); + emitter.lt_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.drop(); - emitter.dup(0); - emitter.lt(); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.lt(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1191,17 +1201,17 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.lte_imm(two); + emitter.lte_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I1); assert_eq!(emitter.stack()[1], one); - emitter.drop(); - emitter.dup(0); - emitter.lte(); + emitter.drop(SourceSpan::default()); + emitter.dup(0, SourceSpan::default()); + emitter.lte(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1216,15 +1226,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.checked_div_imm(two); + emitter.checked_div_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.checked_div(); + emitter.checked_div(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1239,15 +1249,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.unchecked_div_imm(two); + emitter.unchecked_div_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.unchecked_div(); + emitter.unchecked_div(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1262,15 +1272,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.checked_mod_imm(two); + emitter.checked_mod_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.checked_mod(); + emitter.checked_mod(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1285,15 +1295,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.unchecked_mod_imm(two); + emitter.unchecked_mod_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.unchecked_mod(); + emitter.unchecked_mod(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1308,16 +1318,16 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.checked_divmod_imm(two); + emitter.checked_divmod_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], Type::U32); assert_eq!(emitter.stack()[2], one); - emitter.checked_divmod(); + emitter.checked_divmod(SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], Type::U32); @@ -1334,16 +1344,16 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.unchecked_divmod_imm(two); + emitter.unchecked_divmod_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], Type::U32); assert_eq!(emitter.stack()[2], one); - emitter.unchecked_divmod(); + emitter.unchecked_divmod(SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], Type::U32); @@ -1360,15 +1370,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.exp_imm(two); + emitter.exp_imm(two, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.exp(); + emitter.exp(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1383,15 +1393,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.band_imm(one); + emitter.band_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.band(); + emitter.band(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1406,15 +1416,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.bor_imm(one); + emitter.bor_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.bor(); + emitter.bor(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1429,15 +1439,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.bxor_imm(one); + emitter.bxor_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.bxor(); + emitter.bxor(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1452,15 +1462,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.shl_imm(one); + emitter.shl_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.shl(); + emitter.shl(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1475,15 +1485,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.shr_imm(one); + emitter.shr_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.shr(); + emitter.shr(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1498,15 +1508,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.rotl_imm(one); + emitter.rotl_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.rotl(); + emitter.rotl(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1521,15 +1531,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.rotr_imm(one); + emitter.rotr_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.rotr(); + emitter.rotr(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1544,15 +1554,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.min_imm(one); + emitter.min_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.min(); + emitter.min(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1567,15 +1577,15 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); - emitter.max_imm(one); + emitter.max_imm(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::U32); assert_eq!(emitter.stack()[1], one); - emitter.max(); + emitter.max(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1589,9 +1599,9 @@ mod tests { let max = Immediate::U32(u32::MAX); - emitter.literal(max); + emitter.literal(max, SourceSpan::default()); - emitter.trunc(&Type::U16); + emitter.trunc(&Type::U16, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U16); } @@ -1605,9 +1615,9 @@ mod tests { let one = Immediate::U16(1); - emitter.literal(one); + emitter.literal(one, SourceSpan::default()); - emitter.zext(&Type::U32); + emitter.zext(&Type::U32, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1621,9 +1631,9 @@ mod tests { let num = Immediate::I16(-128); - emitter.literal(num); + emitter.literal(num, SourceSpan::default()); - emitter.sext(&Type::I32); + emitter.sext(&Type::I32, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I32); } @@ -1637,9 +1647,9 @@ mod tests { let num = Immediate::U32(128); - emitter.literal(num); + emitter.literal(num, SourceSpan::default()); - emitter.cast(&Type::I32); + emitter.cast(&Type::I32, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I32); } @@ -1654,9 +1664,9 @@ mod tests { let addr = Immediate::U32(128); let ptr = Type::Ptr(Box::new(Type::Array(Box::new(Type::U64), 8))); - emitter.literal(addr); + emitter.literal(addr, SourceSpan::default()); - emitter.inttoptr(&ptr); + emitter.inttoptr(&ptr, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], ptr); } @@ -1670,9 +1680,9 @@ mod tests { let num = Immediate::U32(128); - emitter.literal(num); + emitter.literal(num, SourceSpan::default()); - emitter.is_odd(); + emitter.is_odd(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::I1); } @@ -1686,9 +1696,9 @@ mod tests { let num = Immediate::U32(128); - emitter.literal(num); + emitter.literal(num, SourceSpan::default()); - emitter.popcnt(); + emitter.popcnt(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1702,9 +1712,9 @@ mod tests { let num = Immediate::U32(128); - emitter.literal(num); + emitter.literal(num, SourceSpan::default()); - emitter.bnot(); + emitter.bnot(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1718,9 +1728,9 @@ mod tests { let ten = Immediate::U32(10); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); - emitter.pow2(); + emitter.pow2(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1734,9 +1744,9 @@ mod tests { let ten = Immediate::U32(10); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); - emitter.incr(); + emitter.incr(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1750,9 +1760,9 @@ mod tests { let ten = Immediate::Felt(Felt::new(10)); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); - emitter.inv(); + emitter.inv(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::Felt); } @@ -1766,9 +1776,9 @@ mod tests { let ten = Immediate::Felt(Felt::new(10)); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); - emitter.neg(); + emitter.neg(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::Felt); } @@ -1782,10 +1792,10 @@ mod tests { let ten = Immediate::U32(10); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); - emitter.assert(None); + emitter.assert(None, SourceSpan::default()); assert_eq!(emitter.stack_len(), 0); } @@ -1798,10 +1808,10 @@ mod tests { let ten = Immediate::U32(10); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); - emitter.assertz(None); + emitter.assertz(None, SourceSpan::default()); assert_eq!(emitter.stack_len(), 0); } @@ -1814,15 +1824,15 @@ mod tests { let ten = Immediate::U32(10); - emitter.literal(ten); - emitter.literal(ten); - emitter.literal(ten); + emitter.literal(ten, SourceSpan::default()); + emitter.literal(ten, SourceSpan::default()); + emitter.literal(ten, SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); - emitter.assert_eq_imm(ten); + emitter.assert_eq_imm(ten, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); - emitter.assert_eq(); + emitter.assert_eq(SourceSpan::default()); assert_eq!(emitter.stack_len(), 0); } @@ -1837,12 +1847,12 @@ mod tests { let one = Immediate::U32(1); let two = Immediate::U32(2); - emitter.literal(one); - emitter.literal(two); - emitter.literal(t); + emitter.literal(one, SourceSpan::default()); + emitter.literal(two, SourceSpan::default()); + emitter.literal(t, SourceSpan::default()); assert_eq!(emitter.stack_len(), 3); - emitter.select(); + emitter.select(SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); } @@ -1868,11 +1878,11 @@ mod tests { let t = Immediate::I1(true); let one = Immediate::U32(1); - emitter.literal(t); - emitter.literal(one); + emitter.literal(t, SourceSpan::default()); + emitter.literal(one, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); - emitter.exec(&callee); + emitter.exec(&callee, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], return_ty); } @@ -1889,11 +1899,11 @@ mod tests { emitter.push(addr); assert_eq!(emitter.stack_len(), 1); - emitter.load(Type::U32); + emitter.load(Type::U32, SourceSpan::default()); assert_eq!(emitter.stack_len(), 1); assert_eq!(emitter.stack()[0], Type::U32); - emitter.load_imm(128, Type::I32); + emitter.load_imm(128, Type::I32, SourceSpan::default()); assert_eq!(emitter.stack_len(), 2); assert_eq!(emitter.stack()[0], Type::I32); assert_eq!(emitter.stack()[1], Type::U32); diff --git a/codegen/masm/src/codegen/emit/primop.rs b/codegen/masm/src/codegen/emit/primop.rs index 73d5eb1bc..8012c6f0a 100644 --- a/codegen/masm/src/codegen/emit/primop.rs +++ b/codegen/masm/src/codegen/emit/primop.rs @@ -1,5 +1,6 @@ use midenc_hir::{ - self as hir, ArgumentExtension, ArgumentPurpose, Felt, FieldElement, Immediate, Type, + self as hir, diagnostics::SourceSpan, ArgumentExtension, ArgumentPurpose, Felt, FieldElement, + Immediate, Type, }; use super::{int64, OpEmitter}; @@ -9,7 +10,7 @@ impl<'a> OpEmitter<'a> { /// Assert that an integer value on the stack has the value 1 /// /// This operation consumes the input value. - pub fn assert(&mut self, code: Option) { + pub fn assert(&mut self, code: Option, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let code = code.unwrap_or_default(); match arg.ty() { @@ -21,16 +22,19 @@ impl<'a> OpEmitter<'a> { | Type::U8 | Type::I8 | Type::I1 => { - self.emit(Op::AssertWithError(code)); + self.emit(Op::AssertWithError(code), span); } Type::I128 | Type::U128 => { - self.emit_all(&[ - Op::Pushw([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ONE]), - Op::AssertEqwWithError(code), - ]); + self.emit_all( + &[ + Op::Pushw([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ONE]), + Op::AssertEqwWithError(code), + ], + span, + ); } Type::U64 | Type::I64 => { - self.emit_all(&[Op::AssertzWithError(code), Op::AssertWithError(code)]); + self.emit_all(&[Op::AssertzWithError(code), Op::AssertWithError(code)], span); } ty if !ty.is_integer() => { panic!("invalid argument to assert: expected integer, got {ty}") @@ -42,7 +46,7 @@ impl<'a> OpEmitter<'a> { /// Assert that an integer value on the stack has the value 0 /// /// This operation consumes the input value. - pub fn assertz(&mut self, code: Option) { + pub fn assertz(&mut self, code: Option, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let code = code.unwrap_or_default(); match arg.ty() { @@ -54,13 +58,13 @@ impl<'a> OpEmitter<'a> { | Type::U8 | Type::I8 | Type::I1 => { - self.emit(Op::AssertzWithError(code)); + self.emit(Op::AssertzWithError(code), span); } Type::U64 | Type::I64 => { - self.emit_all(&[Op::AssertzWithError(code), Op::AssertzWithError(code)]); + self.emit_all(&[Op::AssertzWithError(code), Op::AssertzWithError(code)], span); } Type::U128 | Type::I128 => { - self.emit_all(&[Op::Pushw([Felt::ZERO; 4]), Op::AssertEqwWithError(code)]); + self.emit_all(&[Op::Pushw([Felt::ZERO; 4]), Op::AssertEqwWithError(code)], span); } ty if !ty.is_integer() => { panic!("invalid argument to assertz: expected integer, got {ty}") @@ -72,7 +76,7 @@ impl<'a> OpEmitter<'a> { /// Assert that the top two integer values on the stack have the same value /// /// This operation consumes the input values. - pub fn assert_eq(&mut self) { + pub fn assert_eq(&mut self, span: SourceSpan) { let rhs = self.pop().expect("operand stack is empty"); let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); @@ -86,17 +90,20 @@ impl<'a> OpEmitter<'a> { | Type::U8 | Type::I8 | Type::I1 => { - self.emit(Op::AssertEq); + self.emit(Op::AssertEq, span); } - Type::U128 | Type::I128 => self.emit(Op::AssertEqw), + Type::U128 | Type::I128 => self.emit(Op::AssertEqw, span), Type::U64 | Type::I64 => { - self.emit_all(&[ - // compare the hi bits - Op::Movup(2), - Op::AssertEq, - // compare the low bits - Op::AssertEq, - ]); + self.emit_all( + &[ + // compare the hi bits + Op::Movup(2), + Op::AssertEq, + // compare the low bits + Op::AssertEq, + ], + span, + ); } ty if !ty.is_integer() => { panic!("invalid argument to assert_eq: expected integer, got {ty}") @@ -109,7 +116,7 @@ impl<'a> OpEmitter<'a> { /// as the provided immediate. /// /// This operation consumes the input value. - pub fn assert_eq_imm(&mut self, imm: Immediate) { + pub fn assert_eq_imm(&mut self, imm: Immediate, span: SourceSpan) { let lhs = self.pop().expect("operand stack is empty"); let ty = lhs.ty(); assert_eq!(ty, imm.ty(), "expected assert_eq_imm operands to have the same type"); @@ -122,11 +129,11 @@ impl<'a> OpEmitter<'a> { | Type::U8 | Type::I8 | Type::I1 => { - self.emit_all(&[Op::EqImm(imm.as_felt().unwrap()), Op::Assert]); + self.emit_all(&[Op::EqImm(imm.as_felt().unwrap()), Op::Assert], span); } Type::I128 | Type::U128 => { - self.push_immediate(imm); - self.emit(Op::AssertEqw) + self.push_immediate(imm, span); + self.emit(Op::AssertEqw, span) } Type::I64 | Type::U64 => { let imm = match imm { @@ -135,12 +142,15 @@ impl<'a> OpEmitter<'a> { _ => unreachable!(), }; let (hi, lo) = int64::to_raw_parts(imm); - self.emit_all(&[ - Op::EqImm(Felt::new(hi as u64)), - Op::Assert, - Op::EqImm(Felt::new(lo as u64)), - Op::Assert, - ]) + self.emit_all( + &[ + Op::EqImm(Felt::new(hi as u64)), + Op::Assert, + Op::EqImm(Felt::new(lo as u64)), + Op::Assert, + ], + span, + ) } ty if !ty.is_integer() => { panic!("invalid argument to assert_eq: expected integer, got {ty}") @@ -160,7 +170,7 @@ impl<'a> OpEmitter<'a> { /// * Pop `b` and `a` from the stack, and push back `b` if `c` is true, or `a` if `c` is false. /// /// This operation will assert that the selected value is a valid value for the given type. - pub fn select(&mut self) { + pub fn select(&mut self, span: SourceSpan) { let c = self.stack.pop().expect("operand stack is empty"); let b = self.stack.pop().expect("operand stack is empty"); let a = self.stack.pop().expect("operand stack is empty"); @@ -175,23 +185,26 @@ impl<'a> OpEmitter<'a> { | Type::I16 | Type::U8 | Type::I8 - | Type::I1 => self.emit(Op::Cdrop), - Type::I128 | Type::U128 => self.emit(Op::Cdropw), + | Type::I1 => self.emit(Op::Cdrop, span), + Type::I128 | Type::U128 => self.emit(Op::Cdropw, span), Type::I64 | Type::U64 => { // Perform two conditional drops, one for each 32-bit limb // corresponding to the value which is being selected - self.emit_all(&[ - // stack starts as [c, b_hi, b_lo, a_hi, a_lo] - Op::Dup(0), // [c, c, b_hi, b_lo, a_hi, a_lo] - Op::Movdn(6), // [c, b_hi, b_lo, a_hi, a_lo, c] - Op::Movup(3), // [a_hi, c, b_hi, b_lo, a_lo, c] - Op::Movup(2), // [b_hi, a_hi, c, b_lo, a_lo, c] - Op::Movup(6), // [c, b_hi, a_hi, c, b_lo, a_lo] - Op::Cdrop, // [d_hi, c, b_lo, a_lo] - Op::Movdn(4), // [c, b_lo, a_lo, d_hi] - Op::Cdrop, // [d_lo, d_hi] - Op::Swap(1), // [d_hi, d_lo] - ]); + self.emit_all( + &[ + // stack starts as [c, b_hi, b_lo, a_hi, a_lo] + Op::Dup(0), // [c, c, b_hi, b_lo, a_hi, a_lo] + Op::Movdn(6), // [c, b_hi, b_lo, a_hi, a_lo, c] + Op::Movup(3), // [a_hi, c, b_hi, b_lo, a_lo, c] + Op::Movup(2), // [b_hi, a_hi, c, b_lo, a_lo, c] + Op::Movup(6), // [c, b_hi, a_hi, c, b_lo, a_lo] + Op::Cdrop, // [d_hi, c, b_lo, a_lo] + Op::Movdn(4), // [c, b_lo, a_lo, d_hi] + Op::Cdrop, // [d_lo, d_hi] + Op::Swap(1), // [d_hi, d_lo] + ], + span, + ); } ty if !ty.is_integer() => { panic!("invalid argument to assert_eq: expected integer, got {ty}") @@ -204,7 +217,7 @@ impl<'a> OpEmitter<'a> { /// Execute the given procedure. /// /// A function called using this operation is invoked in the same memory context as the caller. - pub fn exec(&mut self, callee: &hir::ExternalFunction) { + pub fn exec(&mut self, callee: &hir::ExternalFunction, span: SourceSpan) { let import = callee; let callee = import.id; let signature = &import.signature; @@ -270,7 +283,7 @@ impl<'a> OpEmitter<'a> { ); // Zero-extend this argument self.stack.push(arg); - self.zext(¶m.ty); + self.zext(¶m.ty, span); self.stack.drop(); } // Caller can provide a smaller type which will be sign-extended to the expected @@ -307,7 +320,7 @@ impl<'a> OpEmitter<'a> { } // Push the operand back on the stack for `sext` self.stack.push(arg); - self.sext(¶m.ty); + self.sext(¶m.ty, span); self.stack.drop(); } ArgumentExtension::Zext | ArgumentExtension::Sext => (), @@ -318,13 +331,13 @@ impl<'a> OpEmitter<'a> { self.stack.push(result.ty.clone()); } - self.emit(Op::Exec(callee)); + self.emit(Op::Exec(callee), span); } /// Execute the given procedure as a syscall. /// /// A function called using this operation is invoked in the same memory context as the caller. - pub fn syscall(&mut self, _callee: &hir::ExternalFunction) { + pub fn syscall(&mut self, _callee: &hir::ExternalFunction, _span: SourceSpan) { todo!() } } diff --git a/codegen/masm/src/codegen/emit/smallint.rs b/codegen/masm/src/codegen/emit/smallint.rs index 3b776b672..b45b41631 100644 --- a/codegen/masm/src/codegen/emit/smallint.rs +++ b/codegen/masm/src/codegen/emit/smallint.rs @@ -8,7 +8,7 @@ //! For signed smallint operations, we implement them in terms of a two's complement representation, //! using a set of common primitives. The only thing that changes are which bits are considered by //! those primitives. -use midenc_hir::Overflow; +use midenc_hir::{diagnostics::SourceSpan, Overflow}; use super::OpEmitter; use crate::masm::Op; @@ -17,50 +17,50 @@ use crate::masm::Op; impl<'a> OpEmitter<'a> { /// Check that a u32 value on the stack can fit in the unsigned N-bit integer range #[inline(always)] - pub fn is_valid_uint(&mut self, n: u32) { + pub fn is_valid_uint(&mut self, n: u32, span: SourceSpan) { // Use fallible conversion from u32 - self.try_int32_to_uint(n); + self.try_int32_to_uint(n, span); } /// Check that the 32-bit value on the stack can fit in the signed N-bit integer range #[inline(always)] - pub fn is_valid_int(&mut self, n: u32) { - self.try_int32_to_int(n); + pub fn is_valid_int(&mut self, n: u32, span: SourceSpan) { + self.try_int32_to_int(n, span); } /// Check if the sign bit of an N-bit integer on the stack, is set. #[inline] - pub fn is_signed_smallint(&mut self, n: u32) { + pub fn is_signed_smallint(&mut self, n: u32, span: SourceSpan) { assert_valid_integer_size!(n, 1, 32); match n { // i1 is never signed - 1 => self.emit(Op::PushU32(0)), - n => self.is_const_flag_set_u32(1 << (n - 1)), + 1 => self.emit(Op::PushU32(0), span), + n => self.is_const_flag_set_u32(1 << (n - 1), span), } } /// Asserts the N-bit integer on the stack does not have its sign bit set. #[inline] - pub fn assert_unsigned_smallint(&mut self, n: u32) { + pub fn assert_unsigned_smallint(&mut self, n: u32, span: SourceSpan) { match n { // i1 is always unsigned 1 => (), n => { - self.is_signed_smallint(n); - self.emit(Op::Assert); + self.is_signed_smallint(n, span); + self.emit(Op::Assert, span); } } } /// Convert a signed N-bit integer to a field element #[inline(always)] - pub fn int_to_felt(&mut self, n: u32) { - self.assert_unsigned_smallint(n); + pub fn int_to_felt(&mut self, n: u32, span: SourceSpan) { + self.assert_unsigned_smallint(n, span); } /// Convert an unsigned N-bit integer to a field element #[inline(always)] - pub fn uint_to_felt(&mut self, n: u32) { + pub fn uint_to_felt(&mut self, n: u32, _span: SourceSpan) { // Conversion to felt is a no-op assert_valid_integer_size!(n, 1, 32); } @@ -69,35 +69,35 @@ impl<'a> OpEmitter<'a> { /// /// This operation will trap if the value has the sign bit set. #[inline] - pub fn int_to_u64(&mut self, n: u32) { - self.assert_unsigned_smallint(n); - self.emit(Op::PushU32(0)); + pub fn int_to_u64(&mut self, n: u32, span: SourceSpan) { + self.assert_unsigned_smallint(n, span); + self.emit(Op::PushU32(0), span); } /// Convert an unsigned N-bit integer to u64 #[inline(always)] - pub fn uint_to_u64(&mut self, _: u32) { - self.emit(Op::PushU32(0)); + pub fn uint_to_u64(&mut self, _: u32, span: SourceSpan) { + self.emit(Op::PushU32(0), span); } /// Convert a signed N-bit integer to i128 #[inline] - pub fn int_to_i128(&mut self, n: u32) { - self.sext_smallint(n, 128); + pub fn int_to_i128(&mut self, n: u32, span: SourceSpan) { + self.sext_smallint(n, 128, span); } /// Convert an unsigned N-bit integer to i128 #[inline(always)] - pub fn uint_to_i128(&mut self, _n: u32) { + pub fn uint_to_i128(&mut self, _n: u32, span: SourceSpan) { // zero-extend to i128 - self.emit_n(3, Op::PushU32(0)); + self.emit_n(3, Op::PushU32(0), span); } /// Sign-extend the N-bit value on the stack to M-bits, where M is >= N and <= 256. /// /// This assumes the value on the stack is a valid N-bit integer in two's complement /// representation, i.e. the most significant bit is the sign bit. - pub fn sext_smallint(&mut self, n: u32, m: u32) { + pub fn sext_smallint(&mut self, n: u32, m: u32, span: SourceSpan) { assert_valid_integer_size!(n, n, 256); // No-op if n == m { @@ -112,27 +112,30 @@ impl<'a> OpEmitter<'a> { // We optimize larger extensions by re-using the is_signed flag let is_large = m > 32; // Get the value of the sign bit - self.is_signed_smallint(n); + self.is_signed_smallint(n, span); if is_large { // Make a copy for selecting padding later - self.emit(Op::Dup(0)); - self.select_int32(sign_bits, 0); - self.emit_all(&[ - // Move the input value to the top of the stack - Op::Movup(2), - // Sign-extend to i32 - Op::U32Or, - // Move the is_signed flag back to the top - Op::Swap(1), - ]); + self.emit(Op::Dup(0), span); + self.select_int32(sign_bits, 0, span); + self.emit_all( + &[ + // Move the input value to the top of the stack + Op::Movup(2), + // Sign-extend to i32 + Op::U32Or, + // Move the is_signed flag back to the top + Op::Swap(1), + ], + span, + ); // Select the padding element value - self.select_int32(u32::MAX, 0); + self.select_int32(u32::MAX, 0, span); // Pad out to M bits - self.pad_int32(m); + self.pad_int32(m, span); } else { - self.select_int32(sign_bits, 0); + self.select_int32(sign_bits, 0, span); // Sign-extend to i32 - self.emit(Op::U32Or); + self.emit(Op::U32Or, span); } } @@ -140,31 +143,31 @@ impl<'a> OpEmitter<'a> { /// /// This assumes the value on the stack is a valid N-bit integer. #[inline] - pub fn zext_smallint(&mut self, n: u32, m: u32) { + pub fn zext_smallint(&mut self, n: u32, m: u32, span: SourceSpan) { assert_valid_integer_size!(n, n, 256); // No-op if n == m { return; } - self.zext_int32(m); + self.zext_int32(m, span); } - pub fn add_uint(&mut self, n: u32, overflow: Overflow) { + pub fn add_uint(&mut self, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked => self.add_u32(Overflow::Unchecked), + Overflow::Unchecked => self.add_u32(Overflow::Unchecked, span), overflow => { - self.add_u32(Overflow::Checked); - self.handle_uint_overflow(n, overflow) + self.add_u32(Overflow::Checked, span); + self.handle_uint_overflow(n, overflow, span) } } } - pub fn add_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { + pub fn add_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked => self.add_imm_u32(imm, Overflow::Unchecked), + Overflow::Unchecked => self.add_imm_u32(imm, Overflow::Unchecked, span), overflow => { - self.add_imm_u32(imm, Overflow::Checked); - self.handle_uint_overflow(n, overflow) + self.add_imm_u32(imm, Overflow::Checked, span); + self.handle_uint_overflow(n, overflow, span) } } } @@ -172,12 +175,12 @@ impl<'a> OpEmitter<'a> { /// Pops two u32 values off the stack, `b` and `a`, and performs `a - b`. /// /// See the [Overflow] type for how overflow semantics can change the operation. - pub fn sub_uint(&mut self, n: u32, overflow: Overflow) { + pub fn sub_uint(&mut self, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked => self.sub_u32(overflow), + Overflow::Unchecked => self.sub_u32(overflow, span), overflow => { - self.sub_u32(overflow); - self.handle_uint_overflow(n, overflow); + self.sub_u32(overflow, span); + self.handle_uint_overflow(n, overflow, span); } } } @@ -188,67 +191,67 @@ impl<'a> OpEmitter<'a> { /// /// Subtracting zero is a no-op. #[inline] - pub fn sub_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { + pub fn sub_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow, span: SourceSpan) { if imm == 0 { return; } match overflow { - Overflow::Unchecked => self.sub_imm_u32(imm, overflow), + Overflow::Unchecked => self.sub_imm_u32(imm, overflow, span), overflow => { - self.sub_imm_u32(imm, overflow); - self.handle_uint_overflow(n, overflow); + self.sub_imm_u32(imm, overflow, span); + self.handle_uint_overflow(n, overflow, span); } } } - pub fn mul_uint(&mut self, n: u32, overflow: Overflow) { + pub fn mul_uint(&mut self, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked => self.mul_u32(Overflow::Unchecked), + Overflow::Unchecked => self.mul_u32(Overflow::Unchecked, span), overflow => { - self.mul_u32(Overflow::Checked); - self.handle_uint_overflow(n, overflow) + self.mul_u32(Overflow::Checked, span); + self.handle_uint_overflow(n, overflow, span) } } } - pub fn mul_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow) { + pub fn mul_imm_uint(&mut self, imm: u32, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { - Overflow::Unchecked => self.mul_imm_u32(imm, Overflow::Unchecked), + Overflow::Unchecked => self.mul_imm_u32(imm, Overflow::Unchecked, span), overflow => { - self.mul_imm_u32(imm, Overflow::Checked); - self.handle_uint_overflow(n, overflow) + self.mul_imm_u32(imm, Overflow::Checked, span); + self.handle_uint_overflow(n, overflow, span) } } } #[inline] - pub fn checked_div_uint(&mut self, n: u32) { - self.checked_div_u32(); - self.int32_to_uint(n); + pub fn checked_div_uint(&mut self, n: u32, span: SourceSpan) { + self.checked_div_u32(span); + self.int32_to_uint(n, span); } #[inline] - pub fn checked_div_imm_uint(&mut self, imm: u32, n: u32) { - self.checked_div_imm_u32(imm); - self.int32_to_uint(n); + pub fn checked_div_imm_uint(&mut self, imm: u32, n: u32, span: SourceSpan) { + self.checked_div_imm_u32(imm, span); + self.int32_to_uint(n, span); } #[inline(always)] - pub fn unchecked_div_uint(&mut self, _n: u32) { - self.unchecked_div_u32(); + pub fn unchecked_div_uint(&mut self, _n: u32, span: SourceSpan) { + self.unchecked_div_u32(span); } #[inline(always)] - pub fn unchecked_div_imm_uint(&mut self, imm: u32, _n: u32) { - self.unchecked_div_imm_u32(imm); + pub fn unchecked_div_imm_uint(&mut self, imm: u32, _n: u32, span: SourceSpan) { + self.unchecked_div_imm_u32(imm, span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a % b`. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. #[inline(always)] - pub fn checked_mod_uint(&mut self, _n: u32) { - self.checked_mod_u32(); + pub fn checked_mod_uint(&mut self, _n: u32, span: SourceSpan) { + self.checked_mod_u32(span); } /// Pops a u32 value off the stack, `a`, and performs `a % `. @@ -257,24 +260,24 @@ impl<'a> OpEmitter<'a> { /// /// This operation is checked, so if the operand or result are not valid u32, execution traps. #[inline(always)] - pub fn checked_mod_imm_uint(&mut self, imm: u32, _n: u32) { - self.checked_mod_imm_u32(imm); + pub fn checked_mod_imm_uint(&mut self, imm: u32, _n: u32, span: SourceSpan) { + self.checked_mod_imm_u32(imm, span); } /// Pops two u32 values off the stack, `b` and `a`, and performs `a % b`. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 #[inline(always)] - pub fn unchecked_mod_uint(&mut self, _n: u32) { - self.unchecked_mod_u32(); + pub fn unchecked_mod_uint(&mut self, _n: u32, span: SourceSpan) { + self.unchecked_mod_u32(span); } /// Pops a u32 value off the stack, `a`, and performs `a % `. /// /// This function will panic if the divisor is zero. #[inline(always)] - pub fn unchecked_mod_imm_uint(&mut self, imm: u32, _n: u32) { - self.unchecked_mod_imm_u32(imm); + pub fn unchecked_mod_imm_uint(&mut self, imm: u32, _n: u32, span: SourceSpan) { + self.unchecked_mod_imm_u32(imm, span); } /// Pops two u32 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the @@ -282,16 +285,16 @@ impl<'a> OpEmitter<'a> { /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. #[inline(always)] - pub fn checked_divmod_uint(&mut self, _n: u32) { - self.checked_divmod_u32(); + pub fn checked_divmod_uint(&mut self, _n: u32, span: SourceSpan) { + self.checked_divmod_u32(span); } /// Pops a u32 value off the stack, `a`, and pushes `a / `, then `a % ` on the stack. /// /// This operation is checked, so if the operands or result are not valid u32, execution traps. #[inline(always)] - pub fn checked_divmod_imm_uint(&mut self, imm: u32, _n: u32) { - self.checked_divmod_imm_u32(imm); + pub fn checked_divmod_imm_uint(&mut self, imm: u32, _n: u32, span: SourceSpan) { + self.checked_divmod_imm_u32(imm, span); } /// Pops two u32 values off the stack, `b` and `a`, and pushes `a / b`, then `a % b` on the @@ -299,33 +302,36 @@ impl<'a> OpEmitter<'a> { /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 #[inline(always)] - pub fn unchecked_divmod_uint(&mut self, _n: u32) { - self.unchecked_divmod_u32(); + pub fn unchecked_divmod_uint(&mut self, _n: u32, span: SourceSpan) { + self.unchecked_divmod_u32(span); } /// Pops a u32 value off the stack, `a`, and pushes `a / `, then `a % ` on the stack. /// /// This operation is unchecked, so the result is not guaranteed to be a valid u32 #[inline(always)] - pub fn unchecked_divmod_imm_uint(&mut self, imm: u32, _n: u32) { - self.unchecked_divmod_imm_u32(imm) + pub fn unchecked_divmod_imm_uint(&mut self, imm: u32, _n: u32, span: SourceSpan) { + self.unchecked_divmod_imm_u32(imm, span) } - pub fn handle_uint_overflow(&mut self, n: u32, overflow: Overflow) { + pub fn handle_uint_overflow(&mut self, n: u32, overflow: Overflow, span: SourceSpan) { match overflow { Overflow::Unchecked => (), - Overflow::Checked => self.int32_to_uint(n), - Overflow::Wrapping => self.emit(Op::U32ModImm(2u32.pow(n))), + Overflow::Checked => self.int32_to_uint(n, span), + Overflow::Wrapping => self.emit(Op::U32ModImm(2u32.pow(n)), span), Overflow::Overflowing => { - self.try_int32_to_uint(n); - self.emit_all(&[ - // move result to top, and wrap it at 2^n - Op::Swap(1), - Op::U32ModImm(2u32.pow(n)), - // move is_valid flag to top, and invert it - Op::Swap(1), - Op::Not, - ]); + self.try_int32_to_uint(n, span); + self.emit_all( + &[ + // move result to top, and wrap it at 2^n + Op::Swap(1), + Op::U32ModImm(2u32.pow(n)), + // move is_valid flag to top, and invert it + Op::Swap(1), + Op::Not, + ], + span, + ); } } } diff --git a/codegen/masm/src/codegen/emit/unary.rs b/codegen/masm/src/codegen/emit/unary.rs index d7dfe3c2a..737c158bb 100644 --- a/codegen/masm/src/codegen/emit/unary.rs +++ b/codegen/masm/src/codegen/emit/unary.rs @@ -24,7 +24,7 @@ impl<'a> OpEmitter<'a> { /// This function assumes that an integer value of type `src` is on top of the operand stack, /// and will ensure a value of type `dst` is on the operand stack after truncation, or that /// execution traps. - pub fn trunc(&mut self, dst: &Type) { + pub fn trunc(&mut self, dst: &Type, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let src = arg.ty(); assert!( @@ -35,33 +35,33 @@ impl<'a> OpEmitter<'a> { match (&src, dst) { // If the types are equivalent, it's a no-op (src, dst) if src == dst => (), - (Type::Felt, _) if n <= 32 => self.trunc_felt(n), + (Type::Felt, _) if n <= 32 => self.trunc_felt(n, span), // Truncating i128 to u128, and vice versa is a bitcast (Type::I128 | Type::U128, Type::U128 | Type::I128) => (), // Truncating to felt - (Type::U128 | Type::I128, Type::Felt) => self.trunc_i128_to_felt(), + (Type::U128 | Type::I128, Type::Felt) => self.trunc_i128_to_felt(span), // Truncating a 128-bit integer to 64 bits or smaller - (Type::U128 | Type::I128, _) if n <= 64 => self.trunc_i128(n), + (Type::U128 | Type::I128, _) if n <= 64 => self.trunc_i128(n, span), // Truncating i64/u64 to felt - (Type::I64 | Type::U64, Type::Felt) => self.trunc_int64_to_felt(), + (Type::I64 | Type::U64, Type::Felt) => self.trunc_int64_to_felt(span), // Truncating i64 to u64, and vice versa is a bitcast (Type::I64 | Type::U64, Type::U64 | Type::I64) => (), // Truncating a u64/i64 to 32 bits or smaller - (Type::I64 | Type::U64, _) if n <= 32 => self.trunc_int64(n), + (Type::I64 | Type::U64, _) if n <= 32 => self.trunc_int64(n, span), // Truncating a felt to 32 bits or smaller - (Type::Felt, _) if n <= 32 => self.trunc_felt(n), + (Type::Felt, _) if n <= 32 => self.trunc_felt(n, span), // Truncating i32 to u32, and vice versa is a bitcast (Type::I32 | Type::U32, Type::U32 | Type::I32) => (), // Truncating an i32/u32 to smaller than 32 bits - (Type::I32 | Type::U32, _) if n <= 32 => self.trunc_int32(n), + (Type::I32 | Type::U32, _) if n <= 32 => self.trunc_int32(n, span), // Truncating i16 to u16, and vice versa is a bitcast (Type::I16 | Type::U16, Type::U16 | Type::I16) => (), // Truncating an i16/u16 to smaller than 16 bits - (Type::I16 | Type::U16, _) if n <= 16 => self.trunc_int32(n), + (Type::I16 | Type::U16, _) if n <= 16 => self.trunc_int32(n, span), // Truncating i8 to u8, and vice versa is a bitcast (Type::I8 | Type::U8, Type::U8 | Type::I8) => (), // Truncating an i8/u8 to smaller than 8 bits - (Type::I8 | Type::U8, _) if n <= 8 => self.trunc_int32(n), + (Type::I8 | Type::U8, _) if n <= 8 => self.trunc_int32(n, span), (src, dst) => unimplemented!("unsupported truncation of {src} to {dst}"), } self.stack.push(dst.clone()); @@ -93,7 +93,7 @@ impl<'a> OpEmitter<'a> { /// This function assumes that an integer value of type `src` is on top of the operand stack, /// and will ensure a value of type `dst` is on the operand stack after truncation, or that /// execution traps. - pub fn zext(&mut self, dst: &Type) { + pub fn zext(&mut self, dst: &Type, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let src = arg.ty(); let src_bits = src.size_in_bits() as u32; @@ -106,13 +106,13 @@ impl<'a> OpEmitter<'a> { // If the types are equivalent, it's a no-op, but only if they are integers (src, dst) if src == dst => (), // Zero-extending a u64 to i128 simply requires pushing a 0u64 on the stack - (Type::U64, Type::U128 | Type::I128) => self.push_u64(0), - (Type::Felt, Type::U64 | Type::U128 | Type::I128) => self.zext_felt(dst_bits), + (Type::U64, Type::U128 | Type::I128) => self.push_u64(0, span), + (Type::Felt, Type::U64 | Type::U128 | Type::I128) => self.zext_felt(dst_bits, span), (Type::U32, Type::U64 | Type::I64 | Type::U128 | Type::I128) => { - self.zext_int32(dst_bits) + self.zext_int32(dst_bits, span) } (Type::I1 | Type::U8 | Type::U16, Type::U64 | Type::I64 | Type::U128 | Type::I128) => { - self.zext_smallint(src_bits, dst_bits) + self.zext_smallint(src_bits, dst_bits, span) } // Zero-extending to u32/i32 from smaller integers is a no-op (Type::I1 | Type::U8 | Type::U16, Type::U32 | Type::I32) => (), @@ -153,7 +153,7 @@ impl<'a> OpEmitter<'a> { /// This function assumes that an integer value of type `src` is on top of the operand stack, /// and will ensure a value of type `dst` is on the operand stack after truncation, or that /// execution traps. - pub fn sext(&mut self, dst: &Type) { + pub fn sext(&mut self, dst: &Type, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let src = arg.ty(); assert!( @@ -170,19 +170,19 @@ impl<'a> OpEmitter<'a> { match (&src, dst) { // If the types are equivalent, it's a no-op (src, dst) if src == dst => (), - (Type::U64 | Type::I64, Type::I128) => self.sext_int64(128), - (Type::Felt, Type::I64 | Type::I128) => self.sext_felt(dst_bits), - (Type::I32 | Type::U32, Type::I64 | Type::I128) => self.sext_int32(dst_bits), + (Type::U64 | Type::I64, Type::I128) => self.sext_int64(128, span), + (Type::Felt, Type::I64 | Type::I128) => self.sext_felt(dst_bits, span), + (Type::I32 | Type::U32, Type::I64 | Type::I128) => self.sext_int32(dst_bits, span), ( Type::I1 | Type::I8 | Type::U8 | Type::I16 | Type::U16, Type::I32 | Type::I64 | Type::I128, - ) => self.sext_smallint(src_bits, dst_bits), + ) => self.sext_smallint(src_bits, dst_bits, span), (src, dst) => panic!("unsupported sign-extension from {src} to {dst}"), } self.stack.push(dst.clone()); } - pub fn bitcast(&mut self, dst: &Type) { + pub fn bitcast(&mut self, dst: &Type, _span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let src = arg.ty(); assert!( @@ -216,7 +216,7 @@ impl<'a> OpEmitter<'a> { /// This function assumes that an integer value of type `src` is on top of the operand stack, /// and will ensure a value of type `dst` is on the operand stack after truncation, or that /// execution traps. - pub fn cast(&mut self, dst: &Type) { + pub fn cast(&mut self, dst: &Type, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let src = arg.ty(); assert!( @@ -228,118 +228,121 @@ impl<'a> OpEmitter<'a> { let dst_bits = dst.size_in_bits() as u32; match (&src, dst) { // u128 - (Type::U128, Type::I128) => self.assert_unsigned_int128(), - (Type::U128, Type::I64) => self.u128_to_i64(), - (Type::U128 | Type::I128, Type::U64) => self.int128_to_u64(), - (Type::U128 | Type::I128, Type::Felt) => self.int128_to_felt(), - (Type::U128 | Type::I128, Type::U32) => self.int128_to_u32(), + (Type::U128, Type::I128) => self.assert_unsigned_int128(span), + (Type::U128, Type::I64) => self.u128_to_i64(span), + (Type::U128 | Type::I128, Type::U64) => self.int128_to_u64(span), + (Type::U128 | Type::I128, Type::Felt) => self.int128_to_felt(span), + (Type::U128 | Type::I128, Type::U32) => self.int128_to_u32(span), (Type::U128 | Type::I128, Type::U16 | Type::U8 | Type::I1) => { - self.int128_to_u32(); - self.int32_to_uint(dst_bits); + self.int128_to_u32(span); + self.int32_to_uint(dst_bits, span); } (Type::U128, Type::I32) => { - self.int128_to_u32(); - self.assert_unsigned_int32(); + self.int128_to_u32(span); + self.assert_unsigned_int32(span); } (Type::U128, Type::I16 | Type::I8) => { - self.int128_to_u32(); - self.int32_to_int(dst_bits); + self.int128_to_u32(span); + self.int32_to_int(dst_bits, span); } // i128 - (Type::I128, Type::I64) => self.i128_to_i64(), + (Type::I128, Type::I64) => self.i128_to_i64(span), (Type::I128, Type::I32 | Type::I16 | Type::I8) => { - self.i128_to_i64(); - self.i64_to_int(dst_bits); + self.i128_to_i64(span); + self.i64_to_int(dst_bits, span); } // i64 - (Type::I64, Type::I128) => self.sext_int64(128), - (Type::I64, Type::U128) => self.zext_int64(128), - (Type::I64, Type::U64) => self.assert_unsigned_int64(), - (Type::I64, Type::Felt) => self.i64_to_felt(), + (Type::I64, Type::I128) => self.sext_int64(128, span), + (Type::I64, Type::U128) => self.zext_int64(128, span), + (Type::I64, Type::U64) => self.assert_unsigned_int64(span), + (Type::I64, Type::Felt) => self.i64_to_felt(span), (Type::I64, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.assert_unsigned_int64(); - self.u64_to_uint(dst_bits); + self.assert_unsigned_int64(span); + self.u64_to_uint(dst_bits, span); } (Type::I64, Type::I32 | Type::I16 | Type::I8) => { - self.i64_to_int(dst_bits); + self.i64_to_int(dst_bits, span); } // u64 - (Type::U64, Type::I128 | Type::U128) => self.zext_int64(128), - (Type::U64, Type::I64) => self.assert_i64(), - (Type::U64, Type::Felt) => self.u64_to_felt(), + (Type::U64, Type::I128 | Type::U128) => self.zext_int64(128, span), + (Type::U64, Type::I64) => self.assert_i64(span), + (Type::U64, Type::Felt) => self.u64_to_felt(span), (Type::U64, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.u64_to_uint(dst_bits); + self.u64_to_uint(dst_bits, span); } (Type::U64, Type::I32 | Type::I16 | Type::I8) => { // Convert to N bits as unsigned - self.u64_to_uint(dst_bits); + self.u64_to_uint(dst_bits, span); // Verify that the input value is still unsigned - self.assert_unsigned_smallint(dst_bits); + self.assert_unsigned_smallint(dst_bits, span); } // felt - (Type::Felt, Type::I64 | Type::I128) => self.sext_felt(dst_bits), - (Type::Felt, Type::U128) => self.zext_felt(dst_bits), - (Type::Felt, Type::U64) => self.felt_to_u64(), + (Type::Felt, Type::I64 | Type::I128) => self.sext_felt(dst_bits, span), + (Type::Felt, Type::U128) => self.zext_felt(dst_bits, span), + (Type::Felt, Type::U64) => self.felt_to_u64(span), (Type::Felt, Type::U32 | Type::U16 | Type::U8 | Type::I1) => { - self.felt_to_uint(dst_bits); + self.felt_to_uint(dst_bits, span); } (Type::Felt, Type::I32 | Type::I16 | Type::I8) => { - self.felt_to_int(dst_bits); + self.felt_to_int(dst_bits, span); } // u32 - (Type::U32, Type::I64 | Type::U64 | Type::I128) => self.zext_int32(dst_bits), - (Type::U32, Type::I32) => self.assert_i32(), + (Type::U32, Type::I64 | Type::U64 | Type::I128) => self.zext_int32(dst_bits, span), + (Type::U32, Type::I32) => self.assert_i32(span), (Type::U32, Type::U16 | Type::U8 | Type::I1) => { - self.int32_to_uint(dst_bits); + self.int32_to_uint(dst_bits, span); } - (Type::U32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits), + (Type::U32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits, span), // i32 - (Type::I32, Type::I64 | Type::I128) => self.sext_int32(dst_bits), + (Type::I32, Type::I64 | Type::I128) => self.sext_int32(dst_bits, span), (Type::I32, Type::U64) => { - self.assert_i32(); - self.emit(Op::PushU32(0)); + self.assert_i32(span); + self.emit(Op::PushU32(0), span); } (Type::I32, Type::U32) => { - self.assert_i32(); + self.assert_i32(span); } (Type::I32, Type::U16 | Type::U8 | Type::I1) => { - self.int32_to_uint(dst_bits); + self.int32_to_uint(dst_bits, span); } - (Type::I32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits), + (Type::I32, Type::I16 | Type::I8) => self.int32_to_int(dst_bits, span), // i8/i16 (Type::I8 | Type::I16, Type::I32 | Type::I64 | Type::I128) => { - self.sext_smallint(src_bits, dst_bits); + self.sext_smallint(src_bits, dst_bits, span); } (Type::I8 | Type::I16, Type::U32 | Type::U64) => { - self.assert_unsigned_smallint(src_bits); - self.zext_smallint(src_bits, dst_bits); + self.assert_unsigned_smallint(src_bits, span); + self.zext_smallint(src_bits, dst_bits, span); } (Type::I16, Type::U16) | (Type::I8, Type::U8) => { - self.assert_unsigned_smallint(src_bits); + self.assert_unsigned_smallint(src_bits, span); } - (Type::I16, Type::U8 | Type::I1) => self.int32_to_int(dst_bits), - (Type::I16, Type::I8) => self.int32_to_int(dst_bits), + (Type::I16, Type::U8 | Type::I1) => self.int32_to_int(dst_bits, span), + (Type::I16, Type::I8) => self.int32_to_int(dst_bits, span), (Type::I8, Type::I1) => { - self.emit_all(&[ - // Assert that input is either 0 or 1 - // - // NOTE: The comparison here is unsigned, so the sign - // bit being set will make the i8 larger than 0 or 1 - Op::Dup(0), - Op::PushU32(2), - Op::Lt, - Op::Assert, - ]); + self.emit_all( + &[ + // Assert that input is either 0 or 1 + // + // NOTE: The comparison here is unsigned, so the sign + // bit being set will make the i8 larger than 0 or 1 + Op::Dup(0), + Op::PushU32(2), + Op::Lt, + Op::Assert, + ], + span, + ); } // i1 - (Type::I1, _) => self.zext_smallint(src_bits, dst_bits), + (Type::I1, _) => self.zext_smallint(src_bits, dst_bits, span), (src, dst) => unimplemented!("unsupported cast from {src} to {dst}"), } self.stack.push(dst.clone()); } /// Cast `arg` to a pointer value - pub fn inttoptr(&mut self, ty: &Type) { + pub fn inttoptr(&mut self, ty: &Type, span: SourceSpan) { assert!(ty.is_pointer(), "exected pointer typed argument"); // For now, we're strict about the types of values we'll allow casting from let arg = self.stack.pop().expect("operand stack is empty"); @@ -349,7 +352,7 @@ impl<'a> OpEmitter<'a> { self.stack.push(ty.clone()); } Type::Felt => { - self.emit(Op::U32Assert); + self.emit(Op::U32Assert, span); self.stack.push(ty.clone()); } int => panic!("invalid inttoptr cast: cannot cast value of type {int} to {ty}"), @@ -361,7 +364,7 @@ impl<'a> OpEmitter<'a> { /// The result is placed on the stack as a boolean value. /// /// This operation consumes the input operand. - pub fn is_odd(&mut self) { + pub fn is_odd(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { // For both signed and unsigned types, @@ -377,17 +380,17 @@ impl<'a> OpEmitter<'a> { | Type::U32 | Type::I32 | Type::Felt => { - self.emit(Op::IsOdd); + self.emit(Op::IsOdd, span); } // For i64/u64, we use the native instruction // on the lower limb to check for odd/even Type::I64 | Type::U64 => { - self.emit_all(&[Op::Drop, Op::IsOdd]); + self.emit_all(&[Op::Drop, Op::IsOdd], span); } // For i128, same as above, but more elements are dropped Type::I128 | Type::U128 => { - self.emit_n(3, Op::Drop); - self.emit(Op::IsOdd); + self.emit_n(3, Op::Drop, span); + self.emit(Op::IsOdd, span); } Type::F64 => { unimplemented!("is_odd support for floating-point values is not yet implemented") @@ -401,43 +404,49 @@ impl<'a> OpEmitter<'a> { /// place the result back on the operand stack as a u32 value. /// /// This operation consumes the input operand. - pub fn ilog2(&mut self) { + pub fn ilog2(&mut self, span: SourceSpan) { let ty = self.stack.peek().expect("operand stack is empty").ty(); match &ty { - Type::Felt => self.emit(Op::Ilog2), + Type::Felt => self.emit(Op::Ilog2, span), Type::I128 | Type::U128 | Type::I64 | Type::U64 => { // Compute the number of leading zeros // // NOTE: This function handles popping the input and pushing // a u32 result on the stack for us, so we can omit any stack // manipulation here. - self.clz(); + self.clz(span); let bits = ty.size_in_bits(); // ilog2 is bits - clz - 1 - self.emit_all(&[ - Op::PushU8(bits as u8), - Op::Swap(1), - Op::Sub, - Op::U32OverflowingSubImm(1), - Op::Assertz, - ]); + self.emit_all( + &[ + Op::PushU8(bits as u8), + Op::Swap(1), + Op::Sub, + Op::U32OverflowingSubImm(1), + Op::Assertz, + ], + span, + ); } Type::I32 | Type::U32 | Type::I16 | Type::U16 | Type::I8 | Type::U8 => { let _ = self.stack.pop(); - self.emit_all(&[ - // Compute ilog2 on the advice stack - Op::Ilog2, - // Drop the operand - Op::Drop, - // Move the result to the operand stack - Op::AdvPush(1), - ]); + self.emit_all( + &[ + // Compute ilog2 on the advice stack + Op::Ilog2, + // Drop the operand + Op::Drop, + // Move the result to the operand stack + Op::AdvPush(1), + ], + span, + ); self.stack.push(Type::U32); } Type::I1 => { // 2^0 == 1 let _ = self.stack.pop(); - self.emit_all(&[Op::Drop, Op::PushU8(0)]); + self.emit_all(&[Op::Drop, Op::PushU8(0)], span); self.stack.push(Type::U32); } ty if !ty.is_integer() => { @@ -451,46 +460,52 @@ impl<'a> OpEmitter<'a> { /// and place the count back on the stack as a u32 value. /// /// This operation consumes the input operand. - pub fn popcnt(&mut self) { + pub fn popcnt(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { Type::I128 | Type::U128 => { - self.emit_all(&[ - // [x3, x2, x1, x0] - Op::U32Popcnt, - // [popcnt3, x2, x1, x0] - Op::Swap(1), - // [x2, popcnt3, x1, x0] - Op::U32Popcnt, - // [popcnt2, popcnt3, x1, x0] - Op::Add, - // [popcnt_hi, x1, x0] - Op::Movdn(2), - // [x1, x0, popcnt] - Op::U32Popcnt, - // [popcnt1, x0, popcnt] - Op::Swap(1), - // [x0, popcnt1, popcnt] - Op::U32Popcnt, - // [popcnt0, popcnt1, popcnt] - // - // This last instruction adds all three values together mod 2^32 - Op::U32WrappingAdd3, - ]); + self.emit_all( + &[ + // [x3, x2, x1, x0] + Op::U32Popcnt, + // [popcnt3, x2, x1, x0] + Op::Swap(1), + // [x2, popcnt3, x1, x0] + Op::U32Popcnt, + // [popcnt2, popcnt3, x1, x0] + Op::Add, + // [popcnt_hi, x1, x0] + Op::Movdn(2), + // [x1, x0, popcnt] + Op::U32Popcnt, + // [popcnt1, x0, popcnt] + Op::Swap(1), + // [x0, popcnt1, popcnt] + Op::U32Popcnt, + // [popcnt0, popcnt1, popcnt] + // + // This last instruction adds all three values together mod 2^32 + Op::U32WrappingAdd3, + ], + span, + ); } Type::I64 | Type::U64 => { - self.emit_all(&[ - // Get popcnt of high bits - Op::U32Popcnt, - // Swap to low bits and repeat - Op::Swap(1), - Op::U32Popcnt, - // Add both counts to get the total count - Op::Add, - ]); + self.emit_all( + &[ + // Get popcnt of high bits + Op::U32Popcnt, + // Swap to low bits and repeat + Op::Swap(1), + Op::U32Popcnt, + // Add both counts to get the total count + Op::Add, + ], + span, + ); } Type::I32 | Type::U32 | Type::I16 | Type::U16 | Type::I8 | Type::U8 | Type::I1 => { - self.emit(Op::U32Popcnt); + self.emit(Op::U32Popcnt, span); } ty if !ty.is_integer() => { panic!("invalid popcnt on {ty}: only integral types are supported") @@ -504,7 +519,7 @@ impl<'a> OpEmitter<'a> { /// and place the count back on the stack as a u32 value. /// /// This operation is implemented so that it consumes the input operand. - pub fn clz(&mut self) { + pub fn clz(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { Type::I128 | Type::U128 => { @@ -513,51 +528,61 @@ impl<'a> OpEmitter<'a> { // library intrinsics to get the count for those limbs. We then add the count // for the low bits to that of the high bits, if the high bits are all zero, // otherwise we take just the high bit count. - self.emit_all(&[ - // Count leading zeros in the high bits - Op::Exec(u64_clz), // [hi_clz, lo_hi, lo_lo] - // Count leading zeros in the low bits - Op::Movup(2), // [lo_lo, hi_clz, lo_hi] - Op::Movup(2), // [lo_hi, lo_lo, hi_clz] - Op::Exec(u64_clz), // [lo_clz, hi_clz] - // Add the low bit leading zeros to those of the high bits, if the high bits - // are all zeros; otherwise return only the high bit count - Op::PushU32(0), // [0, lo_clz, hi_clz] - Op::Dup(2), // [hi_clz, 0, lo_clz, hi_clz] - Op::LtImm(Felt::new(32)), // [hi_clz < 32, 0, lo_clz, hi_clz] - Op::Cdrop, // [hi_clz < 32 ? 0 : lo_clz, hi_clz] - Op::Add, - ]); + self.emit_all( + &[ + // Count leading zeros in the high bits + Op::Exec(u64_clz), // [hi_clz, lo_hi, lo_lo] + // Count leading zeros in the low bits + Op::Movup(2), // [lo_lo, hi_clz, lo_hi] + Op::Movup(2), // [lo_hi, lo_lo, hi_clz] + Op::Exec(u64_clz), // [lo_clz, hi_clz] + // Add the low bit leading zeros to those of the high bits, if the high + // bits are all zeros; otherwise return only the + // high bit count + Op::PushU32(0), // [0, lo_clz, hi_clz] + Op::Dup(2), // [hi_clz, 0, lo_clz, hi_clz] + Op::LtImm(Felt::new(32)), // [hi_clz < 32, 0, lo_clz, hi_clz] + Op::Cdrop, // [hi_clz < 32 ? 0 : lo_clz, hi_clz] + Op::Add, + ], + span, + ); } Type::I64 | Type::U64 => { - self.emit(Op::Exec("std::math::u64::clz".parse().unwrap())); + self.emit(Op::Exec("std::math::u64::clz".parse().unwrap()), span); } Type::I32 | Type::U32 => { - self.emit(Op::U32Clz); + self.emit(Op::U32Clz, span); } Type::I16 | Type::U16 => { // There are always 16 leading zeroes from the perspective of the // MASM u32clz instruction for values of (i|u)16 type, so subtract // that from the count - self.emit_all(&[ - Op::U32Clz, - // Subtract the excess bits from the count - Op::U32WrappingSubImm(16), - ]); + self.emit_all( + &[ + Op::U32Clz, + // Subtract the excess bits from the count + Op::U32WrappingSubImm(16), + ], + span, + ); } Type::I8 | Type::U8 => { // There are always 24 leading zeroes from the perspective of the // MASM u32clz instruction for values of (i|u)8 type, so subtract // that from the count - self.emit_all(&[ - Op::U32Clz, - // Subtract the excess bits from the count - Op::U32WrappingSubImm(24), - ]); + self.emit_all( + &[ + Op::U32Clz, + // Subtract the excess bits from the count + Op::U32WrappingSubImm(24), + ], + span, + ); } Type::I1 => { // There is exactly one leading zero if false, or zero if true - self.emit(Op::EqImm(Felt::ZERO)); + self.emit(Op::EqImm(Felt::ZERO), span); } ty if !ty.is_integer() => { panic!("invalid clz on {ty}: only integral types are supported") @@ -571,7 +596,7 @@ impl<'a> OpEmitter<'a> { /// and place the count back on the stack as a u32 value. /// /// This operation is implemented so that it consumes the input operand. - pub fn clo(&mut self) { + pub fn clo(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { // The implementation here is effectively the same as `clz`, just with minor adjustments @@ -581,59 +606,70 @@ impl<'a> OpEmitter<'a> { // library intrinsics to get the count for those limbs. We then add the count // for the low bits to that of the high bits, if the high bits are all one, // otherwise we take just the high bit count. - self.emit_all(&[ - // Count leading ones in the high bits - Op::Exec(u64_clo), // [hi_clo, lo_hi, lo_lo] - // Count leading ones in the low bits - Op::Movup(2), // [lo_lo, hi_clo, lo_hi] - Op::Movup(2), // [lo_hi, lo_lo, hi_clo] - Op::Exec(u64_clo), // [lo_clo, hi_clo] - // Add the low bit leading ones to those of the high bits, if the high bits - // are all one; otherwise return only the high bit count - Op::PushU32(0), // [0, lo_clo, hi_clo] - Op::Dup(2), // [hi_clo, 0, lo_clo, hi_clo] - Op::LtImm(Felt::new(32)), // [hi_clo < 32, 0, lo_clo, hi_clo] - Op::Cdrop, // [hi_clo < 32 ? 0 : lo_clo, hi_clo] - Op::Add, - ]); - } - Type::I64 | Type::U64 => self.emit(Op::Exec("std::math::u64::clo".parse().unwrap())), + self.emit_all( + &[ + // Count leading ones in the high bits + Op::Exec(u64_clo), // [hi_clo, lo_hi, lo_lo] + // Count leading ones in the low bits + Op::Movup(2), // [lo_lo, hi_clo, lo_hi] + Op::Movup(2), // [lo_hi, lo_lo, hi_clo] + Op::Exec(u64_clo), // [lo_clo, hi_clo] + // Add the low bit leading ones to those of the high bits, if the high bits + // are all one; otherwise return only the high bit count + Op::PushU32(0), // [0, lo_clo, hi_clo] + Op::Dup(2), // [hi_clo, 0, lo_clo, hi_clo] + Op::LtImm(Felt::new(32)), // [hi_clo < 32, 0, lo_clo, hi_clo] + Op::Cdrop, // [hi_clo < 32 ? 0 : lo_clo, hi_clo] + Op::Add, + ], + span, + ); + } + Type::I64 | Type::U64 => { + self.emit(Op::Exec("std::math::u64::clo".parse().unwrap()), span) + } Type::I32 | Type::U32 => { - self.emit(Op::U32Clo); + self.emit(Op::U32Clo, span); } Type::I16 | Type::U16 => { // There are always 16 leading zeroes from the perspective of the // MASM u32clo instruction for values of (i|u)16 type, so to get // the correct count, we need to bitwise-OR in a 16 bits of leading // ones, then subtract that from the final count. - self.emit_all(&[ - // OR in the leading 16 ones - Op::PushU32(u32::MAX << 16), - Op::U32Or, - // Obtain the count - Op::U32Clo, - // Subtract the leading bits we added from the count - Op::U32WrappingSubImm(16), - ]); + self.emit_all( + &[ + // OR in the leading 16 ones + Op::PushU32(u32::MAX << 16), + Op::U32Or, + // Obtain the count + Op::U32Clo, + // Subtract the leading bits we added from the count + Op::U32WrappingSubImm(16), + ], + span, + ); } Type::I8 | Type::U8 => { // There are always 24 leading zeroes from the perspective of the // MASM u32clo instruction for values of (i|u)8 type, so as with the // 16-bit values, we need to bitwise-OR in 24 bits of leading ones, // then subtract them from the final count. - self.emit_all(&[ - // OR in the leading 24 ones - Op::PushU32(u32::MAX << 8), - Op::U32Or, - // Obtain the count - Op::U32Clo, - // Subtract the excess bits from the count - Op::U32WrappingSubImm(24), - ]); + self.emit_all( + &[ + // OR in the leading 24 ones + Op::PushU32(u32::MAX << 8), + Op::U32Or, + // Obtain the count + Op::U32Clo, + // Subtract the excess bits from the count + Op::U32WrappingSubImm(24), + ], + span, + ); } Type::I1 => { // There is exactly one leading one if true, or zero if false - self.emit(Op::EqImm(Felt::ONE)); + self.emit(Op::EqImm(Felt::ONE), span); } ty if !ty.is_integer() => { panic!("invalid clo on {ty}: only integral types are supported") @@ -647,7 +683,7 @@ impl<'a> OpEmitter<'a> { /// and place the count back on the stack as a u32 value. /// /// This operation is implemented so that it consumes the input operand. - pub fn ctz(&mut self) { + pub fn ctz(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { Type::I128 | Type::U128 => { @@ -656,60 +692,72 @@ impl<'a> OpEmitter<'a> { // library intrinsics to get the count for those limbs. We then add the count // for the low bits to that of the high bits, if the high bits are all one, // otherwise we take just the high bit count. - self.emit_all(&[ - // Count trailing zeros in the high bits - Op::Exec(u64_ctz), // [hi_ctz, lo_hi, lo_lo] - // Count trailing zeros in the low bits - Op::Movup(2), // [lo_lo, hi_ctz, lo_hi] - Op::Movup(2), // [lo_hi, lo_lo, hi_ctz] - Op::Exec(u64_ctz), // [lo_ctz, hi_ctz] - // Add the high bit trailing zeros to those of the low bits, if the low bits - // are all zero; otherwise return only the low bit count - Op::Swap(1), - Op::PushU32(0), // [0, hi_ctz, lo_ctz] - Op::Dup(2), // [lo_ctz, 0, hi_ctz, lo_ctz] - Op::LtImm(Felt::new(32)), // [lo_ctz < 32, 0, hi_ctz, lo_ctz] - Op::Cdrop, // [lo_ctz < 32 ? 0 : hi_ctz, lo_ctz] - Op::Add, - ]); - } - Type::I64 | Type::U64 => self.emit(Op::Exec("std::math::u64::ctz".parse().unwrap())), - Type::I32 | Type::U32 => self.emit(Op::U32Ctz), + self.emit_all( + &[ + // Count trailing zeros in the high bits + Op::Exec(u64_ctz), // [hi_ctz, lo_hi, lo_lo] + // Count trailing zeros in the low bits + Op::Movup(2), // [lo_lo, hi_ctz, lo_hi] + Op::Movup(2), // [lo_hi, lo_lo, hi_ctz] + Op::Exec(u64_ctz), // [lo_ctz, hi_ctz] + // Add the high bit trailing zeros to those of the low bits, if the low + // bits are all zero; otherwise return only the low + // bit count + Op::Swap(1), + Op::PushU32(0), // [0, hi_ctz, lo_ctz] + Op::Dup(2), // [lo_ctz, 0, hi_ctz, lo_ctz] + Op::LtImm(Felt::new(32)), // [lo_ctz < 32, 0, hi_ctz, lo_ctz] + Op::Cdrop, // [lo_ctz < 32 ? 0 : hi_ctz, lo_ctz] + Op::Add, + ], + span, + ); + } + Type::I64 | Type::U64 => { + self.emit(Op::Exec("std::math::u64::ctz".parse().unwrap()), span) + } + Type::I32 | Type::U32 => self.emit(Op::U32Ctz, span), Type::I16 | Type::U16 => { // Clamp the total number of trailing zeros to 16 - self.emit_all(&[ - // Obtain the count - Op::U32Ctz, - // Clamp to 16 - // operand_stack: [16, ctz] - Op::PushU8(16), - // operand_stack: [ctz, 16, ctz] - Op::Dup(1), - // operand_stack: [ctz >= 16, 16, ctz] - Op::GteImm(Felt::new(16)), - // operand_stack: [actual_ctz] - Op::Cdrop, - ]); + self.emit_all( + &[ + // Obtain the count + Op::U32Ctz, + // Clamp to 16 + // operand_stack: [16, ctz] + Op::PushU8(16), + // operand_stack: [ctz, 16, ctz] + Op::Dup(1), + // operand_stack: [ctz >= 16, 16, ctz] + Op::GteImm(Felt::new(16)), + // operand_stack: [actual_ctz] + Op::Cdrop, + ], + span, + ); } Type::I8 | Type::U8 => { // Clamp the total number of trailing zeros to 8 - self.emit_all(&[ - // Obtain the count - Op::U32Ctz, - // Clamp to 8 - // operand_stack: [8, ctz] - Op::PushU8(8), - // operand_stack: [ctz, 8, ctz] - Op::Dup(1), - // operand_stack: [ctz >= 8, 8, ctz] - Op::GteImm(Felt::new(8)), - // operand_stack: [actual_ctz] - Op::Cdrop, - ]); + self.emit_all( + &[ + // Obtain the count + Op::U32Ctz, + // Clamp to 8 + // operand_stack: [8, ctz] + Op::PushU8(8), + // operand_stack: [ctz, 8, ctz] + Op::Dup(1), + // operand_stack: [ctz >= 8, 8, ctz] + Op::GteImm(Felt::new(8)), + // operand_stack: [actual_ctz] + Op::Cdrop, + ], + span, + ); } Type::I1 => { // There is exactly one trailing zero if false, or zero if true - self.emit(Op::EqImm(Felt::ZERO)); + self.emit(Op::EqImm(Felt::ZERO), span); } ty if !ty.is_integer() => { panic!("invalid ctz on {ty}: only integral types are supported") @@ -723,7 +771,7 @@ impl<'a> OpEmitter<'a> { /// and place the count back on the stack as a u32 value. /// /// This operation is implemented so that it consumes the input operand. - pub fn cto(&mut self) { + pub fn cto(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); match arg.ty() { Type::I128 | Type::U128 => { @@ -732,32 +780,37 @@ impl<'a> OpEmitter<'a> { // library intrinsics to get the count for those limbs. We then add the count // for the low bits to that of the high bits, if the high bits are all one, // otherwise we take just the high bit count. - self.emit_all(&[ - // Count trailing ones in the high bits - Op::Exec(u64_cto), // [hi_cto, lo_hi, lo_lo] - // Count trailing ones in the low bits - Op::Movup(2), // [lo_lo, hi_cto, lo_hi] - Op::Movup(2), // [lo_hi, lo_lo, hi_cto] - Op::Exec(u64_cto), // [lo_cto, hi_cto] - // Add the high bit trailing ones to those of the low bits, if the low bits - // are all one; otherwise return only the low bit count - Op::Swap(1), - Op::PushU32(0), // [0, hi_cto, lo_cto] - Op::Dup(2), // [lo_cto, 0, hi_cto, lo_cto] - Op::LtImm(Felt::new(32)), // [lo_cto < 32, 0, hi_cto, lo_cto] - Op::Cdrop, // [lo_cto < 32 ? 0 : hi_cto, lo_cto] - Op::Add, - ]); - } - Type::I64 | Type::U64 => self.emit(Op::Exec("std::math::u64::cto".parse().unwrap())), + self.emit_all( + &[ + // Count trailing ones in the high bits + Op::Exec(u64_cto), // [hi_cto, lo_hi, lo_lo] + // Count trailing ones in the low bits + Op::Movup(2), // [lo_lo, hi_cto, lo_hi] + Op::Movup(2), // [lo_hi, lo_lo, hi_cto] + Op::Exec(u64_cto), // [lo_cto, hi_cto] + // Add the high bit trailing ones to those of the low bits, if the low bits + // are all one; otherwise return only the low bit count + Op::Swap(1), + Op::PushU32(0), // [0, hi_cto, lo_cto] + Op::Dup(2), // [lo_cto, 0, hi_cto, lo_cto] + Op::LtImm(Felt::new(32)), // [lo_cto < 32, 0, hi_cto, lo_cto] + Op::Cdrop, // [lo_cto < 32 ? 0 : hi_cto, lo_cto] + Op::Add, + ], + span, + ); + } + Type::I64 | Type::U64 => { + self.emit(Op::Exec("std::math::u64::cto".parse().unwrap()), span) + } Type::I32 | Type::U32 | Type::I16 | Type::U16 | Type::I8 | Type::U8 => { // The number of trailing ones is de-facto clamped by the bitwidth of // the value, since all of the padding bits are leading zeros. - self.emit(Op::U32Cto) + self.emit(Op::U32Cto, span) } Type::I1 => { // There is exactly one trailing one if true, or zero if false - self.emit(Op::EqImm(Felt::ONE)); + self.emit(Op::EqImm(Felt::ONE), span); } ty if !ty.is_integer() => { panic!("invalid cto on {ty}: only integral types are supported") @@ -772,11 +825,11 @@ impl<'a> OpEmitter<'a> { /// This has the effect of changing all 1 bits to 0s, and all 0 bits to 1s. /// /// This operation consumes the input operand. - pub fn bnot(&mut self) { + pub fn bnot(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let ty = arg.ty(); match &ty { - Type::I1 => self.emit(Op::Not), + Type::I1 => self.emit(Op::Not, span), Type::I8 | Type::U8 | Type::I16 @@ -790,13 +843,18 @@ impl<'a> OpEmitter<'a> { let num_elements = ty.size_in_bits() / 32; match num_elements { 0 | 1 => { - self.emit(Op::U32Not); + self.emit(Op::U32Not, span); } 2 => { - self.emit_repeat(2, &[Op::Swap(1), Op::U32Not]); + self.emit_repeat( + 2, + &[Span::new(span, Op::Swap(1)), Span::new(span, Op::U32Not)], + ); } n => { - self.emit_template(n, |n| [Op::Movup(n as u8), Op::U32Not]); + self.emit_template(n, |n| { + [Span::new(span, Op::Movup(n as u8)), Span::new(span, Op::U32Not)] + }); } } } @@ -811,10 +869,10 @@ impl<'a> OpEmitter<'a> { /// Invert the boolean value on top of the operand stack. /// /// This operation consumes the input operand. - pub fn not(&mut self) { + pub fn not(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); assert_eq!(arg.ty(), Type::I1, "logical NOT requires a boolean value"); - self.emit(Op::Not); + self.emit(Op::Not, span); self.stack.push(Type::I1); } @@ -824,36 +882,39 @@ impl<'a> OpEmitter<'a> { /// The input value must be < 64, or execution will trap. /// /// This operation consumes the input operand. - pub fn pow2(&mut self) { + pub fn pow2(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let ty = arg.ty(); match &ty { Type::U64 => { - self.emit_all(&[ - // Assert that the high bits are zero - Op::Assertz, - // This asserts if value > 63, thus result is guaranteed to fit in u64 - Op::Pow2, - // Obtain the u64 representation by splitting the felt result - Op::U32Split, - ]); + self.emit_all( + &[ + // Assert that the high bits are zero + Op::Assertz, + // This asserts if value > 63, thus result is guaranteed to fit in u64 + Op::Pow2, + // Obtain the u64 representation by splitting the felt result + Op::U32Split, + ], + span, + ); } Type::I64 => { - self.emit(Op::Exec("intrinsics::i64::pow2".parse().unwrap())); + self.emit(Op::Exec("intrinsics::i64::pow2".parse().unwrap()), span); } Type::Felt => { - self.emit(Op::Pow2); + self.emit(Op::Pow2, span); } Type::U32 => { - self.emit_all(&[Op::Pow2, Op::U32Assert]); + self.emit_all(&[Op::Pow2, Op::U32Assert], span); } Type::I32 => { - self.emit(Op::Exec("intrinsics::i32::pow2".parse().unwrap())); + self.emit(Op::Exec("intrinsics::i32::pow2".parse().unwrap()), span); } Type::U8 | Type::U16 => { - self.emit_all(&[Op::Pow2, Op::U32Assert]); + self.emit_all(&[Op::Pow2, Op::U32Assert], span); // Cast u32 to u8/u16 - self.int32_to_uint(ty.size_in_bits() as u32); + self.int32_to_uint(ty.size_in_bits() as u32, span); } ty if !ty.is_unsigned_integer() => { panic!( @@ -870,27 +931,27 @@ impl<'a> OpEmitter<'a> { /// The input value must be an integer, and overflow has wrapping semantics. /// /// This operation consumes the input operand. - pub fn incr(&mut self) { + pub fn incr(&mut self, span: SourceSpan) { let arg = self.stack.pop().expect("operand stack is empty"); let ty = arg.ty(); match &ty { // For this specific case, wrapping u64 arithmetic works for both i64/u64 Type::I64 | Type::U64 => { - self.push_u64(1); - self.add_u64(Overflow::Wrapping); + self.push_u64(1, span); + self.add_u64(Overflow::Wrapping, span); } Type::Felt => { - self.emit(Op::Incr); + self.emit(Op::Incr, span); } // For this specific case, wrapping u32 arithmetic works for both i32/u32 Type::I32 | Type::U32 => { - self.add_imm_u32(1, Overflow::Wrapping); + self.add_imm_u32(1, Overflow::Wrapping, span); } // We need to wrap the result for smallint types Type::I8 | Type::U8 | Type::I16 | Type::U16 => { let bits = ty.size_in_bits() as u32; - self.add_imm_u32(1, Overflow::Wrapping); - self.unchecked_mod_imm_u32(2u32.pow(bits)); + self.add_imm_u32(1, Overflow::Wrapping, span); + self.unchecked_mod_imm_u32(2u32.pow(bits), span); } ty if !ty.is_integer() => { panic!("invalid unary operand: incr requires an integer operand, got {ty}") @@ -904,12 +965,12 @@ impl<'a> OpEmitter<'a> { /// `n^-1 mod P`. /// /// This operation consumes the input operand. - pub fn inv(&mut self) { + pub fn inv(&mut self, span: SourceSpan) { let arg = self.pop().expect("operand stack is empty"); let ty = arg.ty(); match &ty { Type::Felt => { - self.emit(Op::Inv); + self.emit(Op::Inv, span); } ty if !ty.is_integer() => { panic!("invalid unary operand: inv requires an integer, got {ty}") @@ -922,12 +983,12 @@ impl<'a> OpEmitter<'a> { /// Compute the modular negation of the operand on top of the stack, `n`, i.e. `-n mod P`. /// /// This operation consumes the input operand. - pub fn neg(&mut self) { + pub fn neg(&mut self, span: SourceSpan) { let arg = self.pop().expect("operand stack is empty"); let ty = arg.ty(); match &ty { Type::Felt => { - self.emit(Op::Neg); + self.emit(Op::Neg, span); } ty if !ty.is_integer() => { panic!("invalid unary operand: neg requires an integer, got {ty}") diff --git a/codegen/masm/src/codegen/emitter.rs b/codegen/masm/src/codegen/emitter.rs index a12f79321..08463d5f7 100644 --- a/codegen/masm/src/codegen/emitter.rs +++ b/codegen/masm/src/codegen/emitter.rs @@ -1,8 +1,13 @@ use std::{collections::BTreeMap, rc::Rc}; use cranelift_entity::SecondaryMap; -use hir::Type; -use midenc_hir::{self as hir, adt::SparseMap, assert_matches}; +use midenc_hir::{ + self as hir, + adt::SparseMap, + assert_matches, + diagnostics::{SourceSpan, Span}, + Type, +}; use midenc_hir_analysis::{ DominatorTree, GlobalVariableLayout, LivenessAnalysis, Loop, LoopAnalysis, }; @@ -188,7 +193,6 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { // Continue normally, by emitting the contents of the block based on the given schedule for op in block_schedule.iter() { match op { - ScheduleOp::Init(_) | ScheduleOp::Enter(_) | ScheduleOp::Exit => continue, ScheduleOp::Inst(inst_info) => self.emit_inst(inst_info, tasks), ScheduleOp::Drop(value) => { let mut emitter = self.emitter(); @@ -196,7 +200,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { .stack() .find(value) .expect("could not find value on the operand stack"); - emitter.drop_operand_at_position(pos); + emitter.drop_operand_at_position(pos, SourceSpan::default()); } } } @@ -210,17 +214,19 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { // NOTE: This does not include block arguments for control flow instructions, those are // handled separately within the specific handlers for those instructions let args = self.function.f.dfg.inst_args(inst_info.inst); - self.schedule_operands(args, inst_info.plain_arguments()).unwrap_or_else(|err| { - panic!( - "failed to schedule operands: {:?} \n for inst: {} {:?}\n with error: {err:?}\n \ - constraints: {:?}\n stack: {:?}", - args, - inst_info.inst, - self.function.f.dfg.inst(inst_info.inst), - inst_info.plain_arguments(), - self.stack, - ) - }); + let span = self.function.f.dfg.inst_span(inst_info.inst); + self.schedule_operands(args, inst_info.plain_arguments(), span) + .unwrap_or_else(|err| { + panic!( + "failed to schedule operands: {:?} \n for inst: {} {:?}\n with error: \ + {err:?}\n constraints: {:?}\n stack: {:?}", + args, + inst_info.inst, + self.function.f.dfg.inst(inst_info.inst), + inst_info.plain_arguments(), + self.stack, + ) + }); match self.function.f.dfg.inst(inst_info.inst) { ix @ (Instruction::RetImm(_) | Instruction::Ret(_)) => self.emit_ret(inst_info, ix), @@ -241,16 +247,17 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { panic!("expected switch instructions to have been rewritten before stackification") } Instruction::LocalVar(ref op) => { + let span = self.function.f.dfg.inst_span(inst_info.inst); let local = self.function.locals[&op.local]; let args = op.args.as_slice(&self.function.f.dfg.value_lists); let mut emitter = self.inst_emitter(inst_info.inst); match op.op { hir::Opcode::Store => { assert_eq!(args.len(), 1); - emitter.store_local(local); + emitter.store_local(local, span); } hir::Opcode::Load => { - emitter.load_local(local); + emitter.load_local(local, span); } opcode => unimplemented!("unrecognized local variable op '{opcode}'"), } @@ -266,22 +273,23 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { self.function.f.dfg.inst_block(inst_info.inst).unwrap(), ); + let span = self.function.f.dfg.inst_span(inst_info.inst); let num_args = self.function.f.dfg.inst_args(inst_info.inst).len(); let level = self.controlling_loop_level().unwrap_or(0); let mut emitter = self.emitter(); // Upon return, the operand stack should only contain the function result(s), // so empty the stack before proceeding. - emitter.truncate_stack(num_args); + emitter.truncate_stack(num_args, span); // If this instruction is the immediate variant, we need to push the return // value on the stack at this point. if let Instruction::RetImm(hir::RetImm { arg, .. }) = ix { - emitter.literal(*arg); + emitter.literal(*arg, span); } // If we're in a loop, push N zeroes on the stack, where N is the current loop depth for _ in 0..level { - emitter.literal(false); + emitter.literal(false, span); } } @@ -300,14 +308,15 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { /// current block, up to the terminator, and then emit instructions to either continue the /// loop, or exit the current loop to the target loop. fn emit_br(&mut self, inst_info: &InstInfo, op: &hir::Br, tasks: &mut Tasks) { - let destination = op.destination; + let destination = op.successor.destination; let is_first_visit = !self.visited; let in_loop_header = self.block_info.is_loop_header(); // Move block arguments into position - let args = op.args.as_slice(&self.function.f.dfg.value_lists); - self.schedule_operands(args, inst_info.block_arguments(destination)) + let span = self.function.f.dfg.inst_span(inst_info.inst); + let args = op.successor.args.as_slice(&self.function.f.dfg.value_lists); + self.schedule_operands(args, inst_info.block_arguments(destination), span) .unwrap_or_else(|err| { panic!("failed to schedule operands for {}: {err:?}", inst_info.inst) }); @@ -322,7 +331,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { if in_loop_header { // We're in a loop header, emit the target block inside a while loop let body_blk = self.masm_block_id(destination); - self.emit_ops([Op::PushU8(1), Op::While(body_blk)]); + self.emit_ops([Op::PushU8(1), Op::While(body_blk)], span); tasks.push(Task::Block { block: destination, controlling_loop, @@ -348,17 +357,17 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { }); let target_level = self.loop_level(self.block_info.source); let mut emitter = self.emitter(); - emitter.literal(true); + emitter.literal(true, span); for _ in 0..(current_level - target_level) { - emitter.literal(false); + emitter.literal(false, span); } } } fn emit_cond_br(&mut self, inst_info: &InstInfo, op: &hir::CondBr, tasks: &mut Tasks) { let cond = op.cond; - let then_dest = op.then_dest.0; - let else_dest = op.else_dest.0; + let then_dest = op.then_dest.destination; + let else_dest = op.else_dest.destination; // Ensure `cond` is on top of the stack, and remove it at the same time assert_eq!( @@ -368,6 +377,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { cond ); + let span = self.function.f.dfg.inst_span(inst_info.inst); if !self.visited { let then_blk = self.masm_block_id(then_dest); let else_blk = self.masm_block_id(else_dest); @@ -377,14 +387,16 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { if self.block_info.is_loop_header() { let body_blk = self.function.f_prime.create_block(); // We always unconditionally enter the loop the first time - self.emit_ops([Op::PushU8(1), Op::While(body_blk)]); - self.emit_op_to(body_blk, Op::If(then_blk, else_blk)); + self.emit_ops([Op::PushU8(1), Op::While(body_blk)], span); + self.emit_op_to(body_blk, Op::If(then_blk, else_blk), span); } else { - self.emit_op(Op::If(then_blk, else_blk)); + self.emit_op(Op::If(then_blk, else_blk), span); } - let successors = - [(then_dest, then_blk, op.then_dest.1), (else_dest, else_blk, op.else_dest.1)]; + let successors = [ + (then_dest, then_blk, op.then_dest.args), + (else_dest, else_blk, op.else_dest.args), + ]; for (block, masm_block, args) in successors.into_iter() { // Make a copy of the operand stack in the current block // to be used as the state of the operand stack in the @@ -400,6 +412,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { inst_info.block_arguments(block), masm_block, &mut stack, + span, ) .unwrap_or_else(|err| { panic!( @@ -440,9 +453,9 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { // prior to this push.1 instruction holds the actual conditional, which // will be evaluated by the `if.true` nested inside the target `while.true` let mut emitter = self.emitter(); - emitter.literal(true); + emitter.literal(true, span); for _ in 0..(current_level - target_level) { - emitter.literal(false); + emitter.literal(false, span); } } } @@ -460,15 +473,16 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { self.function.f.id, op.global ) }); + let span = self.function.f.dfg.inst_span(inst_info.inst); match self.function.f.dfg.global_value(op.global) { hir::GlobalValueData::Load { ref ty, .. } => { let mut emitter = self.inst_emitter(inst_info.inst); - emitter.load_imm(addr, ty.clone()); + emitter.load_imm(addr, ty.clone(), span); } hir::GlobalValueData::IAddImm { .. } | hir::GlobalValueData::Symbol { .. } => { let mut emitter = self.inst_emitter(inst_info.inst); emitter.stack_mut().push(addr); - emitter.inttoptr(&Type::Ptr(Type::U8.into())); + emitter.inttoptr(&Type::Ptr(Type::U8.into()), span); } } } @@ -476,51 +490,52 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_unary_imm_op(&mut self, inst_info: &InstInfo, op: &hir::UnaryOpImm) { use midenc_hir::Immediate; + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); match op.op { hir::Opcode::ImmI1 => { assert_matches!(op.imm, Immediate::I1(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmI8 => { assert_matches!(op.imm, Immediate::I8(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmU8 => { assert_matches!(op.imm, Immediate::U8(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmI16 => { assert_matches!(op.imm, Immediate::I16(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmU16 => { assert_matches!(op.imm, Immediate::U16(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmI32 => { assert_matches!(op.imm, Immediate::I32(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmU32 => { assert_matches!(op.imm, Immediate::U32(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmI64 => { assert_matches!(op.imm, Immediate::I64(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmU64 => { assert_matches!(op.imm, Immediate::U64(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmFelt => { assert_matches!(op.imm, Immediate::Felt(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } hir::Opcode::ImmF64 => { assert_matches!(op.imm, Immediate::F64(_)); - emitter.literal(op.imm); + emitter.literal(op.imm, span); } opcode => unimplemented!("unrecognized unary with immediate opcode: '{opcode}'"), } @@ -529,20 +544,21 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_unary_op(&mut self, inst_info: &InstInfo, op: &hir::UnaryOp) { let inst = inst_info.inst; let result = self.function.f.dfg.first_result(inst); + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst); match op.op { - hir::Opcode::Neg => emitter.neg(), - hir::Opcode::Inv => emitter.inv(), - hir::Opcode::Incr => emitter.incr(), - hir::Opcode::Ilog2 => emitter.ilog2(), - hir::Opcode::Pow2 => emitter.pow2(), - hir::Opcode::Not => emitter.not(), - hir::Opcode::Bnot => emitter.bnot(), - hir::Opcode::Popcnt => emitter.popcnt(), - hir::Opcode::Clz => emitter.clz(), - hir::Opcode::Ctz => emitter.ctz(), - hir::Opcode::Clo => emitter.clo(), - hir::Opcode::Cto => emitter.cto(), + hir::Opcode::Neg => emitter.neg(span), + hir::Opcode::Inv => emitter.inv(span), + hir::Opcode::Incr => emitter.incr(span), + hir::Opcode::Ilog2 => emitter.ilog2(span), + hir::Opcode::Pow2 => emitter.pow2(span), + hir::Opcode::Not => emitter.not(span), + hir::Opcode::Bnot => emitter.bnot(span), + hir::Opcode::Popcnt => emitter.popcnt(span), + hir::Opcode::Clz => emitter.clz(span), + hir::Opcode::Ctz => emitter.ctz(span), + hir::Opcode::Clo => emitter.clo(span), + hir::Opcode::Cto => emitter.cto(span), // This opcode is a no-op hir::Opcode::PtrToInt => { let result_ty = emitter.value_type(result).clone(); @@ -553,32 +569,32 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { // We lower this cast to an assertion, to ensure the value is a valid pointer hir::Opcode::IntToPtr => { let ptr_ty = emitter.value_type(result).clone(); - emitter.inttoptr(&ptr_ty); + emitter.inttoptr(&ptr_ty, span); } // The semantics of cast for now are basically your standard integer coercion rules // // We may eliminate this in favor of more specific casts in the future hir::Opcode::Cast => { let dst_ty = emitter.value_type(result).clone(); - emitter.cast(&dst_ty); + emitter.cast(&dst_ty, span); } hir::Opcode::Bitcast => { let dst_ty = emitter.value_type(result).clone(); - emitter.bitcast(&dst_ty); + emitter.bitcast(&dst_ty, span); } hir::Opcode::Trunc => { let dst_ty = emitter.value_type(result).clone(); - emitter.trunc(&dst_ty); + emitter.trunc(&dst_ty, span); } hir::Opcode::Zext => { let dst_ty = emitter.value_type(result).clone(); - emitter.zext(&dst_ty); + emitter.zext(&dst_ty, span); } hir::Opcode::Sext => { let dst_ty = emitter.value_type(result).clone(); - emitter.sext(&dst_ty); + emitter.sext(&dst_ty, span); } - hir::Opcode::IsOdd => emitter.is_odd(), + hir::Opcode::IsOdd => emitter.is_odd(span), opcode => unimplemented!("unrecognized unary opcode: '{opcode}'"), } } @@ -586,37 +602,40 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_binary_imm_op(&mut self, inst_info: &InstInfo, op: &hir::BinaryOpImm) { use midenc_hir::Overflow; + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); let overflow = op.overflow.unwrap_or(Overflow::Checked); match op.op { - hir::Opcode::Eq => emitter.eq_imm(op.imm), - hir::Opcode::Neq => emitter.neq_imm(op.imm), - hir::Opcode::Gt => emitter.gt_imm(op.imm), - hir::Opcode::Gte => emitter.gte_imm(op.imm), - hir::Opcode::Lt => emitter.lt_imm(op.imm), - hir::Opcode::Lte => emitter.lte_imm(op.imm), - hir::Opcode::Add => emitter.add_imm(op.imm, overflow), - hir::Opcode::Sub => emitter.sub_imm(op.imm, overflow), - hir::Opcode::Mul => emitter.mul_imm(op.imm, overflow), - hir::Opcode::Div if overflow.is_checked() => emitter.checked_div_imm(op.imm), - hir::Opcode::Div => emitter.unchecked_div_imm(op.imm), - hir::Opcode::Min => emitter.min_imm(op.imm), - hir::Opcode::Max => emitter.max_imm(op.imm), - hir::Opcode::Mod if overflow.is_checked() => emitter.checked_mod_imm(op.imm), - hir::Opcode::Mod => emitter.unchecked_mod_imm(op.imm), - hir::Opcode::DivMod if overflow.is_checked() => emitter.checked_divmod_imm(op.imm), - hir::Opcode::DivMod => emitter.unchecked_divmod_imm(op.imm), - hir::Opcode::Exp => emitter.exp_imm(op.imm), - hir::Opcode::And => emitter.and_imm(op.imm), - hir::Opcode::Band => emitter.band_imm(op.imm), - hir::Opcode::Or => emitter.or_imm(op.imm), - hir::Opcode::Bor => emitter.bor_imm(op.imm), - hir::Opcode::Xor => emitter.xor_imm(op.imm), - hir::Opcode::Bxor => emitter.bxor_imm(op.imm), - hir::Opcode::Shl => emitter.shl_imm(op.imm), - hir::Opcode::Shr => emitter.shr_imm(op.imm), - hir::Opcode::Rotl => emitter.rotl_imm(op.imm), - hir::Opcode::Rotr => emitter.rotr_imm(op.imm), + hir::Opcode::Eq => emitter.eq_imm(op.imm, span), + hir::Opcode::Neq => emitter.neq_imm(op.imm, span), + hir::Opcode::Gt => emitter.gt_imm(op.imm, span), + hir::Opcode::Gte => emitter.gte_imm(op.imm, span), + hir::Opcode::Lt => emitter.lt_imm(op.imm, span), + hir::Opcode::Lte => emitter.lte_imm(op.imm, span), + hir::Opcode::Add => emitter.add_imm(op.imm, overflow, span), + hir::Opcode::Sub => emitter.sub_imm(op.imm, overflow, span), + hir::Opcode::Mul => emitter.mul_imm(op.imm, overflow, span), + hir::Opcode::Div if overflow.is_checked() => emitter.checked_div_imm(op.imm, span), + hir::Opcode::Div => emitter.unchecked_div_imm(op.imm, span), + hir::Opcode::Min => emitter.min_imm(op.imm, span), + hir::Opcode::Max => emitter.max_imm(op.imm, span), + hir::Opcode::Mod if overflow.is_checked() => emitter.checked_mod_imm(op.imm, span), + hir::Opcode::Mod => emitter.unchecked_mod_imm(op.imm, span), + hir::Opcode::DivMod if overflow.is_checked() => { + emitter.checked_divmod_imm(op.imm, span) + } + hir::Opcode::DivMod => emitter.unchecked_divmod_imm(op.imm, span), + hir::Opcode::Exp => emitter.exp_imm(op.imm, span), + hir::Opcode::And => emitter.and_imm(op.imm, span), + hir::Opcode::Band => emitter.band_imm(op.imm, span), + hir::Opcode::Or => emitter.or_imm(op.imm, span), + hir::Opcode::Bor => emitter.bor_imm(op.imm, span), + hir::Opcode::Xor => emitter.xor_imm(op.imm, span), + hir::Opcode::Bxor => emitter.bxor_imm(op.imm, span), + hir::Opcode::Shl => emitter.shl_imm(op.imm, span), + hir::Opcode::Shr => emitter.shr_imm(op.imm, span), + hir::Opcode::Rotl => emitter.rotl_imm(op.imm, span), + hir::Opcode::Rotr => emitter.rotr_imm(op.imm, span), opcode => unimplemented!("unrecognized binary with immediate opcode: '{opcode}'"), } } @@ -624,37 +643,38 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_binary_op(&mut self, inst_info: &InstInfo, op: &hir::BinaryOp) { use midenc_hir::Overflow; + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); let overflow = op.overflow.unwrap_or(Overflow::Checked); match op.op { - hir::Opcode::Eq => emitter.eq(), - hir::Opcode::Neq => emitter.neq(), - hir::Opcode::Gt => emitter.gt(), - hir::Opcode::Gte => emitter.gte(), - hir::Opcode::Lt => emitter.lt(), - hir::Opcode::Lte => emitter.lte(), - hir::Opcode::Add => emitter.add(overflow), - hir::Opcode::Sub => emitter.sub(overflow), - hir::Opcode::Mul => emitter.mul(overflow), - hir::Opcode::Div if overflow.is_checked() => emitter.checked_div(), - hir::Opcode::Div => emitter.unchecked_div(), - hir::Opcode::Min => emitter.min(), - hir::Opcode::Max => emitter.max(), - hir::Opcode::Mod if overflow.is_checked() => emitter.checked_mod(), - hir::Opcode::Mod => emitter.unchecked_mod(), - hir::Opcode::DivMod if overflow.is_checked() => emitter.checked_divmod(), - hir::Opcode::DivMod => emitter.unchecked_divmod(), - hir::Opcode::Exp => emitter.exp(), - hir::Opcode::And => emitter.and(), - hir::Opcode::Band => emitter.band(), - hir::Opcode::Or => emitter.or(), - hir::Opcode::Bor => emitter.bor(), - hir::Opcode::Xor => emitter.xor(), - hir::Opcode::Bxor => emitter.bxor(), - hir::Opcode::Shl => emitter.shl(), - hir::Opcode::Shr => emitter.shr(), - hir::Opcode::Rotl => emitter.rotl(), - hir::Opcode::Rotr => emitter.rotr(), + hir::Opcode::Eq => emitter.eq(span), + hir::Opcode::Neq => emitter.neq(span), + hir::Opcode::Gt => emitter.gt(span), + hir::Opcode::Gte => emitter.gte(span), + hir::Opcode::Lt => emitter.lt(span), + hir::Opcode::Lte => emitter.lte(span), + hir::Opcode::Add => emitter.add(overflow, span), + hir::Opcode::Sub => emitter.sub(overflow, span), + hir::Opcode::Mul => emitter.mul(overflow, span), + hir::Opcode::Div if overflow.is_checked() => emitter.checked_div(span), + hir::Opcode::Div => emitter.unchecked_div(span), + hir::Opcode::Min => emitter.min(span), + hir::Opcode::Max => emitter.max(span), + hir::Opcode::Mod if overflow.is_checked() => emitter.checked_mod(span), + hir::Opcode::Mod => emitter.unchecked_mod(span), + hir::Opcode::DivMod if overflow.is_checked() => emitter.checked_divmod(span), + hir::Opcode::DivMod => emitter.unchecked_divmod(span), + hir::Opcode::Exp => emitter.exp(span), + hir::Opcode::And => emitter.and(span), + hir::Opcode::Band => emitter.band(span), + hir::Opcode::Or => emitter.or(span), + hir::Opcode::Bor => emitter.bor(span), + hir::Opcode::Xor => emitter.xor(span), + hir::Opcode::Bxor => emitter.bxor(span), + hir::Opcode::Shl => emitter.shl(span), + hir::Opcode::Shr => emitter.shr(span), + hir::Opcode::Rotl => emitter.rotl(span), + hir::Opcode::Rotr => emitter.rotr(span), opcode => unimplemented!("unrecognized binary opcode: '{opcode}'"), } } @@ -664,32 +684,39 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { } fn emit_load_op(&mut self, inst_info: &InstInfo, op: &hir::LoadOp) { + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); - emitter.load(op.ty.clone()); + emitter.load(op.ty.clone(), span); } fn emit_primop_imm(&mut self, inst_info: &InstInfo, op: &hir::PrimOpImm) { let args = op.args.as_slice(&self.function.f.dfg.value_lists); + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); match op.op { hir::Opcode::Assert => { assert_eq!(args.len(), 1); - emitter - .assert(Some(op.imm.as_u32().expect("invalid assertion error code immediate"))); + emitter.assert( + Some(op.imm.as_u32().expect("invalid assertion error code immediate")), + span, + ); } hir::Opcode::Assertz => { assert_eq!(args.len(), 1); - emitter.assertz(Some( - op.imm.as_u32().expect("invalid assertion error code immediate"), - )); + emitter.assertz( + Some(op.imm.as_u32().expect("invalid assertion error code immediate")), + span, + ); } hir::Opcode::AssertEq => { - emitter.assert_eq_imm(op.imm); + emitter.assert_eq_imm(op.imm, span); } // Store a value at a constant address hir::Opcode::Store => { - emitter - .store_imm(op.imm.as_u32().expect("invalid address immediate: out of range")); + emitter.store_imm( + op.imm.as_u32().expect("invalid address immediate: out of range"), + span, + ); } opcode => unimplemented!("unrecognized primop with immediate opcode: '{opcode}'"), } @@ -697,65 +724,66 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_primop(&mut self, inst_info: &InstInfo, op: &hir::PrimOp) { let args = op.args.as_slice(&self.function.f.dfg.value_lists); + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); match op.op { // Pop a value of the given type off the stack and assert it's value is one hir::Opcode::Assert => { assert_eq!(args.len(), 1); - emitter.assert(None); + emitter.assert(None, span); } // Pop a value of the given type off the stack and assert it's value is zero hir::Opcode::Assertz => { assert_eq!(args.len(), 1); - emitter.assertz(None); + emitter.assertz(None, span); } // Pop two values of the given type off the stack and assert equality hir::Opcode::AssertEq => { assert_eq!(args.len(), 2); - emitter.assert_eq(); + emitter.assert_eq(span); } // Allocate a local and push its address on the operand stack hir::Opcode::Alloca => { assert!(args.is_empty()); let result = emitter.dfg().first_result(inst_info.inst); let ty = emitter.value_type(result).clone(); - emitter.alloca(&ty); + emitter.alloca(&ty, span); } // Store a value at a given pointer hir::Opcode::Store => { assert_eq!(args.len(), 2); - emitter.store(); + emitter.store(span); } // Grow the heap by `num_pages` pages hir::Opcode::MemGrow => { assert_eq!(args.len(), 1); - emitter.mem_grow(); + emitter.mem_grow(span); } // Return the size of the heap in pages hir::Opcode::MemSize => { assert_eq!(args.len(), 0); - emitter.mem_size(); + emitter.mem_size(span); } // Write `count` copies of `value` starting at the destination address hir::Opcode::MemSet => { assert_eq!(args.len(), 3); - emitter.memset(); + emitter.memset(span); } // Copy `count * sizeof(ctrl_ty)` bytes from source to destination address hir::Opcode::MemCpy => { assert_eq!(args.len(), 3); - emitter.memcpy(); + emitter.memcpy(span); } // Conditionally select between two values hir::Opcode::Select => { assert_eq!(args.len(), 3); - emitter.select(); + emitter.select(span); } // This instruction should not be reachable at runtime, so we emit an assertion // that will always fail if for some reason it is reached hir::Opcode::Unreachable => { // assert(false) - emitter.emit_all(&[Op::PushU32(0), Op::Assert]); + emitter.emit_all(&[Op::PushU32(0), Op::Assert], span); } opcode => unimplemented!("unrecognized primop with immediate opcode: '{opcode}'"), } @@ -764,10 +792,11 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { fn emit_call_op(&mut self, inst_info: &InstInfo, op: &hir::Call) { assert_ne!(op.callee, self.function.f.id, "unexpected recursive call"); + let span = self.function.f.dfg.inst_span(inst_info.inst); let mut emitter = self.inst_emitter(inst_info.inst); match op.op { - hir::Opcode::Syscall => emitter.syscall(op.callee), - hir::Opcode::Call => emitter.exec(op.callee), + hir::Opcode::Syscall => emitter.syscall(op.callee, span), + hir::Opcode::Call => emitter.exec(op.callee, span), opcode => unimplemented!("unrecognized procedure call opcode: '{opcode}'"), } } @@ -811,7 +840,8 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { ) { while let Some((prev, new)) = rewrites.pop() { for mut op in asm.blocks[prev].ops.iter().cloned() { - match op { + let span = op.span(); + match &mut *op { Op::If(ref mut then_blk, ref mut else_blk) => { let prev_then_blk = *then_blk; let prev_else_blk = *else_blk; @@ -828,19 +858,19 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { Op::Exec(id) => { self.function.f_prime.register_absolute_invocation_target( miden_assembly::ast::InvokeKind::Exec, - id, + *id, ); } Op::Call(id) => { self.function.f_prime.register_absolute_invocation_target( miden_assembly::ast::InvokeKind::Call, - id, + *id, ); } Op::Syscall(id) => { self.function.f_prime.register_absolute_invocation_target( miden_assembly::ast::InvokeKind::SysCall, - id, + *id, ); } Op::LocAddr(_) @@ -854,7 +884,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { } _ => (), } - self.function.f_prime.body.block_mut(new).push(op); + self.function.f_prime.body.block_mut(new).push(op.into_inner(), span); } } } @@ -940,7 +970,7 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { // we've found so far, and then reset our cursor to the top if unused_batch { let mut emitter = self.emitter(); - emitter.dropn(batch_size); + emitter.dropn(batch_size, SourceSpan::default()); batch_size = 0; current_index = 0; continue; @@ -971,33 +1001,34 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { .count(); let mut emitter = self.emitter(); if unused_chunk_size > 1 { - emitter.movdn(unused_chunk_size as u8); - emitter.dropn(unused_chunk_size); + emitter.movdn(unused_chunk_size as u8, SourceSpan::default()); + emitter.dropn(unused_chunk_size, SourceSpan::default()); } else { - emitter.swap(1); - emitter.drop(); + emitter.swap(1, SourceSpan::default()); + emitter.drop(SourceSpan::default()); } } // We've got multiple unused values together, so choose instead // to move the unused value to the top and drop it _ => { let mut emitter = self.emitter(); - emitter.movup(current_index as u8); - emitter.drop(); + emitter.movup(current_index as u8, SourceSpan::default()); + emitter.drop(SourceSpan::default()); } } batch_size = 0; current_index = 0; } } else { - self.schedule_operands(&unused, &constraints).unwrap_or_else(|err| { - panic!( - "failed to schedule unused operands for {}: {err:?}", - self.block_info.source - ) - }); + self.schedule_operands(&unused, &constraints, SourceSpan::default()) + .unwrap_or_else(|err| { + panic!( + "failed to schedule unused operands for {}: {err:?}", + self.block_info.source + ) + }); let mut emitter = self.emitter(); - emitter.dropn(unused.len()); + emitter.dropn(unused.len(), SourceSpan::default()); } } } @@ -1006,11 +1037,12 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { &mut self, expected: &[hir::Value], constraints: &[Constraint], + span: SourceSpan, ) -> Result<(), SolverError> { match OperandMovementConstraintSolver::new(expected, constraints, &self.stack) { Ok(solver) => { let mut emitter = self.emitter(); - solver.solve_and_apply(&mut emitter) + solver.solve_and_apply(&mut emitter, span) } Err(SolverError::AlreadySolved) => Ok(()), Err(err) => { @@ -1025,11 +1057,12 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { constraints: &[Constraint], block: masm::BlockId, stack: &mut OperandStack, + span: SourceSpan, ) -> Result<(), SolverError> { match OperandMovementConstraintSolver::new(expected, constraints, stack) { Ok(solver) => { let mut emitter = OpEmitter::new(self.function.f_prime, block, stack); - solver.solve_and_apply(&mut emitter) + solver.solve_and_apply(&mut emitter, span) } Err(SolverError::AlreadySolved) => Ok(()), Err(err) => { @@ -1064,8 +1097,16 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { } // Entering a top-level loop, set the controlling loop (None, controlling_loop @ Some(_)) => { + for l in self.function.loops.loops() { + dbg!(l, self.function.loops.loop_header(l)); + dbg!(self.function.loops.is_in_loop(current_block, l)); + dbg!(self.function.loops.is_in_loop(target_block, l)); + } assert!(is_first_visit); - assert_eq!(self.controlling_loop, None); + assert_eq!( + self.controlling_loop, None, + "expected no controlling loop entering {target_block} from {current_block}" + ); controlling_loop } // Escaping a loop @@ -1139,18 +1180,18 @@ impl<'b, 'f: 'b> BlockEmitter<'b, 'f> { } #[inline] - fn emit_op(&mut self, op: Op) { - self.current_block().push(op); + fn emit_op(&mut self, op: Op, span: SourceSpan) { + self.current_block().push(op, span); } #[inline] - fn emit_op_to(&mut self, block: masm::BlockId, op: Op) { - self.block(block).push(op); + fn emit_op_to(&mut self, block: masm::BlockId, op: Op, span: SourceSpan) { + self.block(block).push(op, span); } #[inline] - fn emit_ops(&mut self, ops: impl IntoIterator) { - self.current_block().extend(ops); + fn emit_ops(&mut self, ops: impl IntoIterator, span: SourceSpan) { + self.current_block().extend(ops.into_iter().map(|op| Span::new(span, op))); } fn controlling_loop_level(&self) -> Option { diff --git a/codegen/masm/src/codegen/opt/operands/solver.rs b/codegen/masm/src/codegen/opt/operands/solver.rs index 0304a8a71..9677b5c80 100644 --- a/codegen/masm/src/codegen/opt/operands/solver.rs +++ b/codegen/masm/src/codegen/opt/operands/solver.rs @@ -1,4 +1,4 @@ -use midenc_hir as hir; +use midenc_hir::{self as hir, SourceSpan}; use smallvec::SmallVec; use super::{tactics::Tactic, *}; @@ -232,6 +232,7 @@ impl OperandMovementConstraintSolver { pub fn solve_and_apply( self, emitter: &mut crate::codegen::emit::OpEmitter<'_>, + span: SourceSpan, ) -> Result<(), SolverError> { match self.context.arity() { // No arguments, nothing to solve @@ -241,7 +242,7 @@ impl OperandMovementConstraintSolver { let expected = self.context.expected()[0]; if let Some(current_position) = self.context.stack().position(&expected.value) { if current_position > 0 { - emitter.move_operand_to_position(current_position, 0, false); + emitter.move_operand_to_position(current_position, 0, false, span); } } else { assert!( @@ -258,7 +259,7 @@ impl OperandMovementConstraintSolver { ) }, ); - emitter.copy_operand_to_position(current_position, 0, false); + emitter.copy_operand_to_position(current_position, 0, false, span); } Ok(()) @@ -269,16 +270,16 @@ impl OperandMovementConstraintSolver { for action in actions.into_iter() { match action { Action::Copy(index) => { - emitter.copy_operand_to_position(index as usize, 0, false); + emitter.copy_operand_to_position(index as usize, 0, false, span); } Action::Swap(index) => { - emitter.swap(index); + emitter.swap(index, span); } Action::MoveUp(index) => { - emitter.movup(index); + emitter.movup(index, span); } Action::MoveDown(index) => { - emitter.movdn(index); + emitter.movdn(index, span); } } } diff --git a/codegen/masm/src/codegen/scheduler.rs b/codegen/masm/src/codegen/scheduler.rs index 73f374784..02f0b4a1a 100644 --- a/codegen/masm/src/codegen/scheduler.rs +++ b/codegen/masm/src/codegen/scheduler.rs @@ -146,12 +146,6 @@ pub struct BlockInfo { pub source: hir::Block, /// The target MASM block which will be emitted from this info pub target: masm::BlockId, - /// The id of the last instruction in the source HIR block, - /// this is commonly used to check for liveness after the end - /// of a block - pub last_inst: hir::Inst, - /// The innermost loop to which this block belongs - pub innermost_loop: Option, /// If set, indicates that this block is the loop header /// for the specified loop. pub loop_header: Option, @@ -212,12 +206,6 @@ impl Schedule { /// optimizer would. #[derive(Debug, Clone)] pub enum ScheduleOp { - /// Always the first instruction in a schedule, represents entry into a function - Init(hir::Block), - /// Push the current block context on the context stack, and switch to the given block context - Enter(hir::Block), - /// Pop the most recent block context from the context stack and switch to it - Exit, /// Emit the given instruction, using the provided analysis Inst(Rc), /// Drop the first occurrence of the given value on the operand stack @@ -233,7 +221,7 @@ pub enum Plan { /// This represents entering a block, so all further instructions /// are scheduled in the context of the given block until an ExitBlock /// meta-instruction is encountered. - Start(hir::Block), + Start, /// Schedule execution of an instruction's pre-requisites PreInst(Rc), /// Schedule execution of the given instruction @@ -280,24 +268,16 @@ impl<'a> Scheduler<'a> { pub fn build(mut self) -> Schedule { self.precompute_block_infos(); - let entry_block_id = self.f.dfg.entry_block(); let mut blockq = SmallVec::<[hir::Block; 8]>::from_slice(self.domtree.cfg_postorder()); while let Some(block_id) = blockq.pop() { - let is_entry_block = block_id == entry_block_id; let schedule = &mut self.schedule.block_schedules[block_id]; - if is_entry_block { - schedule.push(ScheduleOp::Init(block_id)); - } else { - schedule.push(ScheduleOp::Enter(block_id)); - } - let block_info = self.schedule.block_infos.get(block_id).cloned().unwrap(); let block_scheduler = BlockScheduler { f: self.f, liveness: self.liveness, block_info, inst_infos: Default::default(), - worklist: SmallVec::from_iter([Plan::Start(block_id)]), + worklist: SmallVec::from_iter([Plan::Start]), }; block_scheduler.schedule(schedule); } @@ -318,8 +298,6 @@ impl<'a> Scheduler<'a> { // Set the controlling loop let loop_header = self.loops.is_loop_header(block_id); - let last_inst = self.f.dfg.last_inst(block_id).unwrap(); - let innermost_loop = self.loops.innermost_loop(block_id); let depgraph = build_dependency_graph(block_id, self.f, self.liveness); let treegraph = OrderedTreeGraph::new(&depgraph) .expect("unable to topologically sort treegraph for block"); @@ -327,8 +305,6 @@ impl<'a> Scheduler<'a> { let info = Rc::new(BlockInfo { source: block_id, target: masm_block_id, - last_inst, - innermost_loop, loop_header, depgraph, treegraph, @@ -353,10 +329,8 @@ impl<'a> BlockScheduler<'a> { // here, we will emit scheduling operations in "normal" order. while let Some(plan) = self.worklist.pop() { match plan { - Plan::Start(_) => self.visit_block(), - Plan::Finish => { - scheduled_ops.push(ScheduleOp::Exit); - } + Plan::Start => self.visit_block(), + Plan::Finish => continue, // We're emitting code required to execute an instruction, such as materialization // of data dependencies used as direct arguments. This is only // emitted when an instruction has arguments which are derived from @@ -692,7 +666,10 @@ impl<'a> BlockScheduler<'a> { }); match self.f.dfg.analyze_branch(inst) { - BranchInfo::SingleDest(block, block_args) => { + BranchInfo::SingleDest(hir::SuccessorInfo { + destination: block, + args: block_args, + }) => { inst_info.successors.push(Successor { block, arg_count: block_args.len() as u16, @@ -728,7 +705,7 @@ impl<'a> BlockScheduler<'a> { index as usize, "successor ordering constraint violation: {arg:?}" ); - let jt = hir::JumpTable { + let succ = hir::SuccessorInfo { destination: block, args: block_args, }; @@ -736,7 +713,7 @@ impl<'a> BlockScheduler<'a> { block_args[index as usize], arg_id, arg_source_id, - Some(&[jt]), + Some(&[succ]), )); } Node::Argument(arg) => { @@ -746,11 +723,11 @@ impl<'a> BlockScheduler<'a> { } } } - BranchInfo::MultiDest(ref jts) => { - for jt in jts.iter() { + BranchInfo::MultiDest(ref succs) => { + for succ in succs.iter() { inst_info.successors.push(Successor { - block: jt.destination, - arg_count: jt.args.len() as u16, + block: succ.destination, + arg_count: succ.args.len() as u16, }); } for (succ_idx, arg) in self @@ -775,7 +752,7 @@ impl<'a> BlockScheduler<'a> { inst_args[index as usize], arg_id, arg_source_id, - Some(jts), + Some(succs), )); } Node::Argument(ArgumentNode::Indirect { @@ -784,9 +761,9 @@ impl<'a> BlockScheduler<'a> { debug_assert_eq!( succ_idx - inst_args.len() - - jts[..(successor as usize)] + - succs[..(successor as usize)] .iter() - .map(|jt| jt.args.len()) + .map(|succ| succ.args.len()) .sum::(), index as usize, "successor ordering constraint violation: {arg:?}" @@ -794,12 +771,12 @@ impl<'a> BlockScheduler<'a> { if !arg_source_id.is_stack() { inst_info.pre.insert(arg_source_id); } - let block_arg = jts[successor as usize].args[index as usize]; + let block_arg = succs[successor as usize].args[index as usize]; inst_info.args.push(self.constraint( block_arg, arg_id, arg_source_id, - Some(jts), + Some(succs), )); } Node::Argument(ArgumentNode::Conditional { @@ -808,9 +785,9 @@ impl<'a> BlockScheduler<'a> { debug_assert_eq!( succ_idx - inst_args.len() - - jts[..(successor as usize)] + - succs[..(successor as usize)] .iter() - .map(|jt| jt.args.len()) + .map(|succ| succ.args.len()) .sum::(), index as usize, "successor ordering constraint violation: {arg:?}" @@ -823,12 +800,12 @@ impl<'a> BlockScheduler<'a> { //inst_info.post.insert(arg_source_id); inst_info.pre.insert(arg_source_id); } - let block_arg = jts[successor as usize].args[index as usize]; + let block_arg = succs[successor as usize].args[index as usize]; inst_info.args.push(self.constraint( block_arg, arg_id, arg_source_id, - Some(jts), + Some(succs), )); } _ => unreachable!(), @@ -961,7 +938,7 @@ impl<'a> BlockScheduler<'a> { arg: hir::Value, arg_node: NodeId, arg_sourced_from: NodeId, - successors: Option<&[hir::JumpTable<'_>]>, + successors: Option<&[hir::SuccessorInfo<'_>]>, ) -> Constraint { if cfg!(debug_assertions) { assert_matches!( @@ -1069,9 +1046,9 @@ fn build_dependency_graph( } match function.dfg.analyze_branch(inst) { - BranchInfo::SingleDest(_, args) => { + BranchInfo::SingleDest(succ) => { // Add edges representing these data dependencies in later blocks - for (arg_idx, arg) in args.iter().copied().enumerate() { + for (arg_idx, arg) in succ.args.iter().copied().enumerate() { let arg_node = ArgumentNode::Indirect { inst, index: arg_idx.try_into().expect("too many successor arguments"), @@ -1080,15 +1057,15 @@ fn build_dependency_graph( graph.add_data_dependency(node_id, arg_node, arg, pp, function); } } - BranchInfo::MultiDest(ref jts) => { + BranchInfo::MultiDest(ref succs) => { // Preprocess the arguments which are used so we can determine materialization // requirements - for jt in jts.iter() { - for arg in jt.args.iter().copied() { + for succ in succs.iter() { + for arg in succ.args.iter().copied() { block_arg_uses .entry(arg) .or_insert_with(Default::default) - .insert(jt.destination); + .insert(succ.destination); } } // For each successor, check if we should implicitly require an argument along that @@ -1096,11 +1073,11 @@ fn build_dependency_graph( // somewhere downstream. We only consider block arguments passed to // at least one other successor, and which are not already explicitly // provided to this successor. - let materialization_threshold = jts.len(); + let materialization_threshold = succs.len(); // Finally, add edges to the dependency graph representing the nature of each // argument - for (succ_idx, jt) in jts.iter().enumerate() { - for (arg_idx, arg) in jt.args.iter().copied().enumerate() { + for (succ_idx, succ) in succs.iter().enumerate() { + for (arg_idx, arg) in succ.args.iter().copied().enumerate() { let is_conditionally_materialized = block_arg_uses[&arg].len() < materialization_threshold; let must_materialize = @@ -1372,11 +1349,11 @@ fn dce( let successor = successor as usize; let index = index as usize; let value = match &branch_info { - BranchInfo::SingleDest(_, args) => { + BranchInfo::SingleDest(succ) => { assert_eq!(successor, 0); - args[index] + succ.args[index] } - BranchInfo::MultiDest(ref jts) => jts[successor].args[index], + BranchInfo::MultiDest(ref succs) => succs[successor].args[index], BranchInfo::NotABranch => unreachable!( "indirect/conditional arguments are only valid as successors of a \ branch instruction" diff --git a/codegen/masm/src/convert.rs b/codegen/masm/src/convert.rs index 9ae861d58..aa19d5f66 100644 --- a/codegen/masm/src/convert.rs +++ b/codegen/masm/src/convert.rs @@ -9,7 +9,7 @@ use midenc_session::Session; use crate::{ codegen::{FunctionEmitter, OperandStack, Scheduler, TypedValue}, - masm, + masm, MasmArtifact, }; type ProgramGlobalVariableAnalysis = analysis::GlobalVariableAnalysis; @@ -48,7 +48,7 @@ impl PassInfo for ConvertHirToMasm { impl ConversionPass for ConvertHirToMasm { type From = Box; - type To = Box; + type To = MasmArtifact; fn convert( &mut self, @@ -56,42 +56,37 @@ impl ConversionPass for ConvertHirToMasm { analyses: &mut AnalysisManager, session: &Session, ) -> ConversionResult { - let mut masm_program = Box::new(masm::Program::from_hir(&program)); + // Ensure global variable analysis is computed + let globals = + analyses.get_or_compute::(&program, session)?; + + let mut artifact = if program.has_entrypoint() { + masm::Program::from_hir(&program, &globals) + .map(Box::new) + .map(MasmArtifact::Executable)? + } else { + MasmArtifact::Library(Box::new(masm::Library::from_hir(&program, &globals))) + }; + + // Move link libraries to artifact + let libraries = core::mem::take(program.libraries_mut()); + for lib in libraries.into_values() { + artifact.link_library(lib); + } // Remove the set of modules to compile from the program let modules = program.modules_mut().take(); - // Ensure global variable analysis is computed - analyses.get_or_compute::(&program, session)?; - for module in modules.into_iter() { // Convert the module let mut convert_to_masm = ConvertHirToMasm::::default(); let masm_module = convert_to_masm.convert(module, analyses, session)?; - // If this module makes use of any intrinsics modules, and those modules are not - // already present, add them to the program. - for import in masm_module - .imports - .iter() - .filter(|import| import.name.as_str().starts_with("intrinsics::")) - { - if masm_program.contains(import.name) { - continue; - } - match masm::intrinsics::load(import.name.as_str(), &session.codemap) { - Some(loaded) => { - masm_program.insert(Box::new(loaded)); - } - None => unimplemented!("unrecognized intrinsic module: '{}'", &import.name), - } - } - // Add to the final Miden Assembly program - masm_program.insert(masm_module); + artifact.insert(masm_module); } - Ok(masm_program) + Ok(artifact) } } @@ -153,6 +148,8 @@ impl<'a> ConversionPass for ConvertHirToMasm<&'a hir::Function> { ) -> ConversionResult { use midenc_hir::ProgramAnalysisKey; + println!("{f}"); + let mut f_prime = masm::Function::new(f.id, f.signature.clone()); // Start at the function entry diff --git a/codegen/masm/src/emulator/functions.rs b/codegen/masm/src/emulator/functions.rs index 9f4d4bec4..45c6165ca 100644 --- a/codegen/masm/src/emulator/functions.rs +++ b/codegen/masm/src/emulator/functions.rs @@ -341,7 +341,7 @@ impl Instruction { #[inline(always)] pub fn op(&self, function: &Function) -> Option { - function.body.get(self.ip) + function.body.get(self.ip).map(|op| op.into_inner()) } } @@ -484,7 +484,7 @@ impl Activation { #[cfg(test)] mod tests { - use midenc_hir::{assert_matches, Signature}; + use midenc_hir::{assert_matches, Signature, SourceSpan}; use super::*; @@ -736,6 +736,7 @@ mod tests { } fn test_function() -> Arc { + let span = SourceSpan::default(); let mut function = Function::new("test::main".parse().unwrap(), Signature::new(vec![], vec![])); let then_blk = function.create_block(); @@ -743,29 +744,29 @@ mod tests { let while_blk = function.create_block(); { let body = function.block_mut(function.body.id()); - body.push(Op::PushU8(2)); - body.push(Op::PushU8(1)); - body.push(Op::Dup(1)); - body.push(Op::Dup(1)); - body.push(Op::U32Lt); - body.push(Op::If(then_blk, else_blk)); - body.push(Op::Exec("test::foo".parse().unwrap())); + body.push(Op::PushU8(2), span); + body.push(Op::PushU8(1), span); + body.push(Op::Dup(1), span); + body.push(Op::Dup(1), span); + body.push(Op::U32Lt, span); + body.push(Op::If(then_blk, else_blk), span); + body.push(Op::Exec("test::foo".parse().unwrap()), span); } { let then_body = function.block_mut(then_blk); - then_body.push(Op::PushU8(1)); - then_body.push(Op::While(while_blk)); + then_body.push(Op::PushU8(1), span); + then_body.push(Op::While(while_blk), span); } { let else_body = function.block_mut(else_blk); - else_body.push(Op::U32Max); + else_body.push(Op::U32Max, span); } { let while_body = function.block_mut(while_blk); - while_body.push(Op::Dup(1)); - while_body.push(Op::Dup(1)); - while_body.push(Op::Incr); - while_body.push(Op::U32Lt); + while_body.push(Op::Dup(1), span); + while_body.push(Op::Dup(1), span); + while_body.push(Op::Incr, span); + while_body.push(Op::U32Lt, span); } Arc::new(function) diff --git a/codegen/masm/src/emulator/mod.rs b/codegen/masm/src/emulator/mod.rs index c3c80e6d1..9a85a809c 100644 --- a/codegen/masm/src/emulator/mod.rs +++ b/codegen/masm/src/emulator/mod.rs @@ -300,7 +300,7 @@ impl Emulator { self.load_module(module)?; cursor.move_next(); } - self.entrypoint = program.entrypoint; + self.entrypoint = Some(program.entrypoint()); // TODO: Load data segments diff --git a/codegen/masm/src/lib.rs b/codegen/masm/src/lib.rs index 66ef3e0d9..e54336edf 100644 --- a/codegen/masm/src/lib.rs +++ b/codegen/masm/src/lib.rs @@ -1,4 +1,5 @@ #![feature(array_windows)] +#![feature(iter_array_chunks)] #![feature(is_sorted)] mod codegen; @@ -8,8 +9,10 @@ mod masm; #[cfg(test)] mod tests; +use miden_assembly::library::CompiledLibrary; use midenc_hir::{ self as hir, + diagnostics::Report, pass::{RewritePass, RewriteSet}, }; use midenc_session::Session; @@ -23,27 +26,47 @@ pub use self::{ masm::*, }; -/// This error type represents all of the errors produced by [MasmCompiler] -#[derive(Debug, thiserror::Error)] -pub enum CompilerError { - /// Two or more modules conflict with each other - #[error(transparent)] - ModuleConflict(#[from] hir::ModuleConflictError), - /// An error occurred at link-time - #[error(transparent)] - Linker(#[from] hir::LinkerError), - /// An error occurred during analysis - #[error(transparent)] - Analysis(#[from] hir::pass::AnalysisError), - /// An error occurred during application of a rewrite - #[error(transparent)] - Rewrite(#[from] hir::pass::RewriteError), - /// An error occurred during application of a conversion - #[error(transparent)] - Conversion(#[from] hir::pass::ConversionError), +pub type CompilerResult = Result; + +/// The artifact produced by lowering an [hir::Program] to Miden Assembly +/// +/// This type is used in compilation pipelines to abstract over the type of output requested. +pub enum MasmArtifact { + /// An executable program, with a defined entrypoint + Executable(Box), + /// A library, linkable into a program as needed + Library(Box), } +impl MasmArtifact { + /// Get an iterator over the modules in this library + pub fn modules(&self) -> impl Iterator + '_ { + match self { + Self::Executable(ref program) => program.library().modules(), + Self::Library(ref lib) => lib.modules(), + } + } + + pub fn insert(&mut self, module: Box) { + match self { + Self::Executable(ref mut program) => program.insert(module), + Self::Library(ref mut lib) => lib.insert(module), + } + } -pub type CompilerResult = Result; + pub fn link_library(&mut self, lib: CompiledLibrary) { + match self { + Self::Executable(ref mut program) => program.link_library(lib), + Self::Library(ref mut library) => library.link_library(lib), + } + } + + pub fn unwrap_executable(self) -> Box { + match self { + Self::Executable(program) => program, + Self::Library(_) => panic!("tried to unwrap a mast library as an executable"), + } + } +} /// [MasmCompiler] is a compiler from Miden IR to MASM IR, an intermediate representation /// of Miden Assembly which is used within the Miden compiler framework for various purposes, @@ -67,7 +90,7 @@ impl<'a> MasmCompiler<'a> { } /// Compile an [hir::Program] that has been linked and is ready to be compiled. - pub fn compile(&mut self, mut input: Box) -> CompilerResult> { + pub fn compile(&mut self, mut input: Box) -> CompilerResult { use midenc_hir::pass::ConversionPass; let mut rewrites = default_rewrites([], self.session); @@ -79,28 +102,23 @@ impl<'a> MasmCompiler<'a> { } let mut convert_to_masm = ConvertHirToMasm::::default(); - let mut program = convert_to_masm.convert(input, &mut self.analyses, self.session)?; - - // Ensure standard library is linked - for module in intrinsics::load_stdlib(&self.session.codemap) { - program.insert(Box::new(module.clone())); - } + let mut artifact = convert_to_masm.convert(input, &mut self.analyses, self.session)?; // Ensure intrinsics modules are linked - program.insert(Box::new( - intrinsics::load("intrinsics::mem", &self.session.codemap) + artifact.insert(Box::new( + intrinsics::load("intrinsics::mem", &self.session.source_manager) .expect("undefined intrinsics module"), )); - program.insert(Box::new( - intrinsics::load("intrinsics::i32", &self.session.codemap) + artifact.insert(Box::new( + intrinsics::load("intrinsics::i32", &self.session.source_manager) .expect("undefined intrinsics module"), )); - program.insert(Box::new( - intrinsics::load("intrinsics::i64", &self.session.codemap) + artifact.insert(Box::new( + intrinsics::load("intrinsics::i64", &self.session.source_manager) .expect("undefined intrinsics module"), )); - Ok(program) + Ok(artifact) } /// Compile a single [hir::Module] as a program. @@ -109,10 +127,15 @@ impl<'a> MasmCompiler<'a> { /// rewrites have been applied. If one of these invariants is not upheld, compilation /// may fail. pub fn compile_module(&mut self, input: Box) -> CompilerResult> { + assert!(input.entrypoint().is_some(), "cannot compile a program without an entrypoint"); + let program = hir::ProgramBuilder::new(&self.session.diagnostics).with_module(input)?.link()?; - self.compile(program) + match self.compile(program)? { + MasmArtifact::Executable(program) => Ok(program), + _ => unreachable!("expected compiler to produce an executable, got a library"), + } } /// Compile a set of [hir::Module] as a program. @@ -131,7 +154,12 @@ impl<'a> MasmCompiler<'a> { let program = builder.link()?; - self.compile(program) + assert!(program.has_entrypoint(), "cannot compile a program without an entrypoint"); + + match self.compile(program)? { + MasmArtifact::Executable(program) => Ok(program), + _ => unreachable!("expected compiler to produce an executable, got a library"), + } } } diff --git a/codegen/masm/src/masm/function.rs b/codegen/masm/src/masm/function.rs index 652f954b3..c7215dfad 100644 --- a/codegen/masm/src/masm/function.rs +++ b/codegen/masm/src/masm/function.rs @@ -6,8 +6,11 @@ use miden_assembly::{ ast::{self, ProcedureName}, LibraryNamespace, LibraryPath, }; -use miden_diagnostics::{SourceSpan, Spanned}; -use midenc_hir::{formatter::PrettyPrint, AttributeSet, FunctionIdent, Ident, Signature, Type}; +use midenc_hir::{ + diagnostics::{SourceSpan, Span, Spanned}, + formatter::PrettyPrint, + AttributeSet, FunctionIdent, Ident, Signature, Type, +}; use smallvec::SmallVec; use super::*; @@ -158,17 +161,13 @@ impl Function { kind: ast::InvokeKind, target: FunctionIdent, ) { - let module_name_span = miden_assembly::SourceSpan::new( - target.module.span.start_index().0..target.module.span.end_index().0, - ); - let module_id = ast::Ident::new_unchecked(miden_assembly::Span::new( + let module_name_span = target.module.span; + let module_id = ast::Ident::new_unchecked(Span::new( module_name_span, Arc::from(target.module.as_str().to_string().into_boxed_str()), )); - let name_span = miden_assembly::SourceSpan::new( - target.function.span.start_index().0..target.function.span.end_index().0, - ); - let id = ast::Ident::new_unchecked(miden_assembly::Span::new( + let name_span = target.function.span; + let id = ast::Ident::new_unchecked(Span::new( name_span, Arc::from(target.function.as_str().to_string().into_boxed_str()), )); @@ -190,9 +189,11 @@ impl Function { pub fn from_ast(module: Ident, proc: &ast::Procedure) -> Box { use midenc_hir::{Linkage, Symbol}; + let proc_span = proc.name().span(); + let proc_name = Symbol::intern(AsRef::::as_ref(proc.name())); let id = FunctionIdent { module, - function: Ident::with_empty_span(Symbol::intern(AsRef::::as_ref(proc.name()))), + function: Ident::new(proc_name, proc_span), }; let mut signature = Signature::new(vec![], vec![]); @@ -218,7 +219,6 @@ impl Function { pub fn to_ast( &self, - codemap: &miden_diagnostics::CodeMap, imports: &midenc_hir::ModuleImportInfo, locals: &BTreeSet, ) -> ast::Procedure { @@ -229,31 +229,17 @@ impl Function { } else { ast::Visibility::Private }; - let source_id = self.span.source_id(); - let span = - miden_assembly::SourceSpan::new(self.span.start_index().0..self.span.end_index().0); - let source_file = codemap.get(source_id).ok().map(|sf| { - let nf = miden_assembly::diagnostics::SourceFile::new( - sf.name().as_str().unwrap(), - sf.source().to_string(), - ); - Arc::new(nf) - }); - let name_span = miden_assembly::SourceSpan::new( - self.name.function.span.start_index().0..self.name.function.span.end_index().0, - ); - let id = ast::Ident::new_unchecked(miden_assembly::Span::new( - name_span, + let id = ast::Ident::new_unchecked(Span::new( + self.name.function.span, Arc::from(self.name.function.as_str().to_string().into_boxed_str()), )); let name = ast::ProcedureName::new_unchecked(id); - let body = self.body.to_block(codemap, imports, locals); + let body = self.body.to_block(imports, locals); let num_locals = u16::try_from(self.locals.len()).expect("too many locals"); - let mut proc = ast::Procedure::new(span, visibility, name, num_locals, body) - .with_source_file(source_file); + let mut proc = ast::Procedure::new(self.span, visibility, name, num_locals, body); proc.extend_invoked(self.invoked().cloned()); proc } diff --git a/codegen/masm/src/masm/import.rs b/codegen/masm/src/masm/import.rs deleted file mode 100644 index 96c44f648..000000000 --- a/codegen/masm/src/masm/import.rs +++ /dev/null @@ -1,129 +0,0 @@ -use core::{ - hash::{Hash, Hasher}, - str::FromStr, -}; - -use anyhow::bail; -use miden_diagnostics::{SourceSpan, Spanned}; -use midenc_hir::Symbol; - -/// This represents an import statement in Miden Assembly -#[derive(Debug, Copy, Clone, Spanned)] -pub struct Import { - /// The source span corresponding to this import statement, if applicable - #[span] - pub span: SourceSpan, - /// The fully-qualified name of the imported module, e.g. `std::math::u64` - pub name: Symbol, - /// The name to which the imported module is aliased locally, e.g. `u64` - /// is the alias for `use std::math::u64`, which is the default behavior. - /// - /// However, custom aliases are permitted, and we may use this to disambiguate - /// imported modules, e.g. `use std::math::u64->my_u64` will result in the - /// alias for this import being `my_u64`. - pub alias: Symbol, -} -impl Import { - /// Returns true if this import has a custom alias, or if it uses the - /// default aliasing behavior for imports - pub fn is_aliased(&self) -> bool { - !self.name.as_str().ends_with(self.alias.as_str()) - } - - /// Returns true if this import conflicts with `other` - /// - /// A conflict arises when the same name is used to reference two different - /// imports locally within a module, i.e. the aliases conflict - pub fn conflicts_with(&self, other: &Self) -> bool { - self.alias == other.alias && self.name != other.name - } -} -impl Eq for Import {} -impl PartialEq for Import { - fn eq(&self, other: &Self) -> bool { - // If the names are different, the imports can't be equivalent - if self.name != other.name { - return false; - } - // Otherwise, equivalence depends on the aliasing of the import - match (self.is_aliased(), other.is_aliased()) { - (true, true) => { - // Two imports that are custom aliased are equivalent only if - // both the fully-qualified name and the alias are identical - self.alias == other.alias - } - (true, false) | (false, true) => { - // If one import is aliased and the other is not, the imports - // are never equivalent, because they can't possibly refer to - // the same module by the same name - false - } - (false, false) => { - // Two unaliased imports are the same if their names are the same - true - } - } - } -} -impl PartialOrd for Import { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for Import { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.name - .cmp(&other.name) - .then_with(|| self.alias.cmp(&other.alias)) - } -} -impl Hash for Import { - fn hash(&self, state: &mut H) { - self.name.hash(state); - self.alias.hash(state); - } -} -impl FromStr for Import { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let s = s.trim(); - let s = s.strip_prefix("use ").unwrap_or(s); - if s.contains(char::is_whitespace) { - bail!( - "invalid import '{}': unexpected whitespace in identifier", - s - ); - } - let (name, alias) = match s.rsplit_once("->") { - None => match s.rsplit_once("::") { - None => { - let name = Symbol::intern(s); - (name, name) - } - Some((_, alias)) if alias.is_empty() => { - bail!("invalid import '{}': trailing '::' is not allowed", s) - } - Some((_, alias)) => { - let name = Symbol::intern(s); - let alias = Symbol::intern(alias); - (name, alias) - } - }, - Some((_, alias)) if alias.is_empty() => { - bail!("invalid import '{}': alias cannot be empty", s) - } - Some((fqn, alias)) => { - let name = Symbol::intern(fqn); - let alias = Symbol::intern(alias); - (name, alias) - } - }; - - Ok(Self { - span: SourceSpan::UNKNOWN, - name, - alias, - }) - } -} diff --git a/codegen/masm/src/masm/intrinsics.rs b/codegen/masm/src/masm/intrinsics.rs index c214a2237..63c8b1398 100644 --- a/codegen/masm/src/masm/intrinsics.rs +++ b/codegen/masm/src/masm/intrinsics.rs @@ -1,5 +1,5 @@ use miden_assembly::{ast::ModuleKind, LibraryPath}; -use miden_diagnostics::{CodeMap, FileName}; +use midenc_hir::diagnostics::{PrintDiagnostic, SourceManager}; use super::Module; @@ -20,46 +20,16 @@ const INTRINSICS: [(&str, &str, &str); 3] = [ /// This helper loads the named module from the set of intrinsics modules defined in this crate. /// /// Expects the fully-qualified name to be given, e.g. `intrinsics::mem` -pub fn load>(name: N, codemap: &CodeMap) -> Option { +pub fn load>(name: N, source_manager: &dyn SourceManager) -> Option { let name = name.as_ref(); let (name, source, filename) = INTRINSICS.iter().copied().find(|(n, ..)| *n == name)?; - let id = codemap.add(FileName::Virtual(filename.into()), source.to_string()); - let source_file = codemap.get(id).unwrap(); + let source_file = source_manager.load(filename, source.to_string()); let path = LibraryPath::new(name).expect("invalid module name"); - match Module::parse_source_file(path, ModuleKind::Library, source_file, codemap) { + match Module::parse(ModuleKind::Library, path, source_file.clone()) { Ok(module) => Some(module), - Err(err) => match err { - crate::LoadModuleError::Report(report) => { - let report = miden_assembly::diagnostics::reporting::PrintDiagnostic::new( - report.into_report(), - ); - panic!("failed to parse intrinsic module: {report}"); - } - other => panic!("unexpected syntax error in intrinsic module: {other}"), - }, + Err(err) => { + let err = PrintDiagnostic::new(err); + panic!("failed to parse intrinsic module: {err}"); + } } } - -/// This helper loads the Miden Standard Library modules from the current miden-stdlib crate -pub fn load_stdlib(codemap: &CodeMap) -> &'static [Module] { - use std::sync::OnceLock; - - use miden_assembly::Library; - use miden_diagnostics::SourceSpan; - use miden_stdlib::StdLibrary; - - static LOADED: OnceLock> = OnceLock::new(); - - LOADED - .get_or_init(|| { - let library = StdLibrary::default(); - - let mut loaded = Vec::with_capacity(library.modules().len()); - for module in library.modules() { - let ir_module = Module::from_ast(module, SourceSpan::UNKNOWN, codemap); - loaded.push(ir_module); - } - loaded - }) - .as_slice() -} diff --git a/codegen/masm/src/masm/mod.rs b/codegen/masm/src/masm/mod.rs index 2f7e7e07a..27b0ee741 100644 --- a/codegen/masm/src/masm/mod.rs +++ b/codegen/masm/src/masm/mod.rs @@ -11,8 +11,8 @@ pub use midenc_hir::{ pub use self::{ function::{FrozenFunctionList, Function, FunctionList}, - module::{FrozenModuleTree, LoadModuleError, Module, ModuleTree}, - program::Program, + module::{FrozenModuleTree, Module, ModuleTree}, + program::{Library, Program}, region::Region, }; diff --git a/codegen/masm/src/masm/module.rs b/codegen/masm/src/masm/module.rs index 8a7249e1b..42df2fb79 100644 --- a/codegen/masm/src/masm/module.rs +++ b/codegen/masm/src/masm/module.rs @@ -1,42 +1,19 @@ -use std::{ - collections::BTreeSet, - fmt, - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{collections::BTreeSet, fmt, path::Path, sync::Arc}; use intrusive_collections::{intrusive_adapter, RBTree, RBTreeAtomicLink}; use miden_assembly::{ ast::{self, ModuleKind}, - diagnostics::{RelatedError, Report, SourceFile as MasmSourceFile}, - LibraryNamespace, LibraryPath, + LibraryPath, +}; +use midenc_hir::{ + diagnostics::{Report, SourceFile, SourceSpan, Span, Spanned}, + formatter::PrettyPrint, + FunctionIdent, Ident, Symbol, }; -use miden_diagnostics::{CodeMap, SourceFile, SourceIndex, SourceSpan}; -use midenc_hir::{formatter::PrettyPrint, FunctionIdent, Ident, Symbol}; +use midenc_session::Emit; use super::{function::Functions, FrozenFunctionList, Function, ModuleImportInfo}; -#[derive(Debug, thiserror::Error)] -pub enum LoadModuleError { - #[error("failed to load module from disk: {0}")] - Io(#[from] std::io::Error), - #[error("invalid path to module: '{}' is not a file", .0.display())] - InvalidPath(PathBuf), - #[error(transparent)] - InvalidIdent(#[from] miden_assembly::ast::IdentError), - #[error(transparent)] - InvalidModulePath(#[from] miden_assembly::PathError), - #[error(transparent)] - InvalidNamespace(#[from] miden_assembly::library::LibraryNamespaceError), - #[error(transparent)] - Report(#[from] RelatedError), -} -impl From for LoadModuleError { - fn from(report: Report) -> Self { - Self::Report(RelatedError::new(report)) - } -} - /// This represents a single compiled Miden Assembly module in a form that is /// designed to integrate well with the rest of our IR. You can think of this /// as an intermediate representation corresponding to the Miden Assembly AST, @@ -81,6 +58,18 @@ impl Module { } } + /// Parse a [Module] from `source` using the given [ModuleKind] and [LibraryPath] + pub fn parse( + kind: ModuleKind, + path: LibraryPath, + source: Arc, + ) -> Result { + let span = source.source_span(); + let mut parser = ast::Module::parser(kind); + let ast = parser.parse(path, source)?; + Ok(Self::from_ast(&ast, span)) + } + /// Returns true if this module is a kernel module pub fn is_kernel(&self) -> bool { self.kind.is_kernel() @@ -112,49 +101,7 @@ impl Module { self.functions.iter().any(|f| f.name.function == name) } - /// Parse a [Module] from the given string - pub fn parse_source_file( - name: LibraryPath, - kind: ModuleKind, - source_file: Arc, - codemap: &CodeMap, - ) -> Result { - let filename = source_file.name().as_str().expect("invalid source file name"); - let module = ast::Module::parse( - name, - kind, - Arc::new(MasmSourceFile::new(filename, source_file.source().to_string())), - )?; - let span = source_file.source_span(); - Ok(Self::from_ast(&module, span, codemap)) - } - - /// Parse a [Module] from the given file path - pub fn parse_file>( - ns: Option, - kind: ModuleKind, - path: P, - codemap: &CodeMap, - ) -> Result { - let path = path.as_ref(); - let id = codemap.add_file(path)?; - let source_file = codemap.get(id).unwrap(); - let fallback_ns = match path.parent().and_then(|p| p.to_str()) { - None => LibraryNamespace::Anon, - Some(parent_dirname) => parent_dirname.parse::()?, - }; - let ns = ns.unwrap_or(fallback_ns); - let name = ast::Ident::new(path.file_stem().unwrap().to_str().unwrap())?; - let module_path = LibraryPath::new_from_components(ns, [name]); - let module = ast::Module::parse_file(module_path, kind, path)?; - let span = source_file.source_span(); - Ok(Self::from_ast(&module, span, codemap)) - } - - pub fn from_ast(ast: &ast::Module, span: SourceSpan, _codemap: &CodeMap) -> Self { - use miden_assembly::Spanned as MasmSpanned; - - let source_id = span.source_id(); + pub fn from_ast(ast: &ast::Module, span: SourceSpan) -> Self { let mut module = Self::new(ast.path().clone(), ast.kind()); module.span = span; module.docs = ast.docs().map(|s| s.to_string()); @@ -162,9 +109,6 @@ impl Module { let mut imports = ModuleImportInfo::default(); for import in ast.imports() { let span = import.name.span(); - let start = SourceIndex::new(source_id, (span.start() as u32).into()); - let end = SourceIndex::new(source_id, (span.end() as u32).into()); - let span = SourceSpan::new(start, end); let alias = Symbol::intern(import.name.as_str()); let name = if import.is_aliased() { Symbol::intern(import.path.last()) @@ -216,29 +160,13 @@ impl Module { } /// Convert this module into its [miden_assembly::ast::Module] representation. - pub fn to_ast(&self, codemap: &miden_diagnostics::CodeMap) -> Result { - let source_id = self.span.source_id(); - let source_file = if let Ok(source_file) = codemap.get(source_id) { - let file = miden_assembly::diagnostics::SourceFile::new( - source_file.name().as_str().unwrap(), - source_file.source().to_string(), - ); - Some(Arc::new(file)) - } else { - None - }; - let span = - miden_assembly::SourceSpan::new(self.span.start_index().0..self.span.end_index().0); - let mut ast = ast::Module::new(self.kind, self.name.clone()) - .with_source_file(source_file) - .with_span(span); - ast.set_docs(self.docs.clone().map(miden_assembly::Span::unknown)); + pub fn to_ast(&self) -> Result { + let mut ast = ast::Module::new(self.kind, self.name.clone()).with_span(self.span); + ast.set_docs(self.docs.clone().map(Span::unknown)); // Create module import table for ir_import in self.imports.iter() { - let ir_span = ir_import.span; - let span = - miden_assembly::SourceSpan::new(ir_span.start_index().0..ir_span.end_index().0); + let span = ir_import.span; let name = ast::Ident::new_with_span(span, ir_import.alias.as_str()).map_err(Report::msg)?; let path = LibraryPath::new(ir_import.name.as_str()).expect("invalid import path"); @@ -259,11 +187,7 @@ impl Module { } for function in self.functions.iter() { - ast.define_procedure(ast::Export::Procedure(function.to_ast( - codemap, - &self.imports, - &locals, - )))?; + ast.define_procedure(ast::Export::Procedure(function.to_ast(&self.imports, &locals)))?; } Ok(ast) @@ -274,11 +198,7 @@ impl Module { /// /// For example, if this module is named `std::math::u64`, then it will be written to /// `/std/math/u64.masm` - pub fn write_to_directory>( - &self, - codemap: &miden_diagnostics::CodeMap, - dir: P, - ) -> std::io::Result<()> { + pub fn write_to_directory>(&self, dir: P) -> std::io::Result<()> { use std::fs::File; let mut path = dir.as_ref().to_path_buf(); @@ -289,17 +209,7 @@ impl Module { assert!(path.set_extension("masm")); let mut out = File::create(&path)?; - self.emit(codemap, &mut out) - } - - /// Write this module as Miden Assembly text to `out` - pub fn emit( - &self, - codemap: &miden_diagnostics::CodeMap, - out: &mut dyn std::io::Write, - ) -> std::io::Result<()> { - let ast = self.to_ast(codemap).map_err(std::io::Error::other)?; - out.write_fmt(format_args!("{}", &ast)) + self.write_to(&mut out) } } impl midenc_hir::formatter::PrettyPrint for Module { @@ -375,7 +285,8 @@ impl midenc_session::Emit for Module { } fn write_to(&self, mut writer: W) -> std::io::Result<()> { - writer.write_fmt(format_args!("{}", self)) + let ast = self.to_ast().map_err(std::io::Error::other)?; + writer.write_fmt(format_args!("{}", &ast)) } } @@ -415,6 +326,13 @@ impl Default for Modules { } } impl Modules { + pub fn len(&self) -> usize { + match self { + Self::Open(ref tree) => tree.iter().count(), + Self::Frozen(ref tree) => tree.iter().count(), + } + } + pub fn iter(&self) -> impl Iterator + '_ { match self { Self::Open(ref tree) => ModulesIter::Open(tree.iter()), diff --git a/codegen/masm/src/masm/program.rs b/codegen/masm/src/masm/program.rs index aaf08211e..c1db436e4 100644 --- a/codegen/masm/src/masm/program.rs +++ b/codegen/masm/src/masm/program.rs @@ -3,32 +3,53 @@ use std::{fmt, path::Path, sync::Arc}; use hir::{Signature, Symbol}; use miden_assembly::{ ast::{ModuleKind, ProcedureName}, + library::{CompiledLibrary, KernelLibrary}, LibraryNamespace, }; -use midenc_hir::{self as hir, DataSegmentTable, FunctionIdent, Ident}; +use miden_core::crypto::hash::Rpo256; +use midenc_hir::{ + self as hir, diagnostics::Report, DataSegmentTable, Felt, FieldElement, FunctionIdent, Ident, + SourceSpan, +}; +use midenc_hir_analysis::GlobalVariableAnalysis; +use midenc_session::Session; use super::{module::Modules, *}; -/// A [Program] represents a complete set of modules which are intended to -/// be shipped together as an artifact, either as an executable, or as a library -/// to be integrated into a larger executable. -/// -/// Modules are stored in a [Program] in a b-tree map, keyed by the module name. -/// This is done to make accessing modules by name efficient, and to ensure a -/// stable ordering for compiled programs when emitted as text. -#[derive(Default)] +inventory::submit! { + midenc_session::CompileFlag::new("test_harness") + .long("test-harness") + .action(midenc_session::FlagAction::SetTrue) + .help("If present, causes the code generator to emit extra code for the VM test harness") + .help_heading("Testing") +} + +/// A [Program] represents a complete set of modules which are intended to be shipped and executed +/// together. pub struct Program { - /// The set of modules which belong to this program - modules: Modules, - /// The data segment table for this program - pub segments: DataSegmentTable, + /// The code for this program + library: Library, /// The function identifier for the program entrypoint, if applicable - pub entrypoint: Option, + entrypoint: FunctionIdent, } impl Program { - /// Create a new, empty [Program] - pub fn empty() -> Self { - Self::default() + /// Create a new [Program] initialized from a [DataSegmentTable], a set of [Module]s, and an + /// optional entrypoint function. + /// + /// A `main.masm` module will be generated which invokes the given entrypoint on startup, after + /// initializing the global heap of the root context, based on the provided data segment table. + /// + /// You should generally prefer to use [Program::from_hir], but this constructor allows you to + /// manually produce a MASM program from its constituent parts. + pub fn new(entrypoint: FunctionIdent, segments: DataSegmentTable, modules: M) -> Self + where + M: IntoIterator>, + { + let library = Library::new(segments, modules); + Self { + library, + entrypoint, + } } /// Create a new [Program] initialized from an [hir::Program]. @@ -41,57 +62,409 @@ impl Program { /// invokes the entrypoint /// /// None of the HIR modules will have been added yet - pub fn from_hir(program: &hir::Program) -> Self { - let mut modules = Modules::default(); - - // Create executable module if we have an entrypoint - let entrypoint = program.entrypoint(); - if let Some(entry) = entrypoint { - let mut exe = - Box::new(Module::new(LibraryNamespace::Exec.into(), ModuleKind::Executable)); - exe.imports.add(entry); - let entry_module = exe - .imports - .alias(&entry.module) - .expect("something went wrong when adding entrypoint import"); - let start_id = FunctionIdent { - module: Ident::with_empty_span(Symbol::intern(LibraryNamespace::EXEC_PATH)), - function: Ident::with_empty_span(Symbol::intern(ProcedureName::MAIN_PROC_NAME)), - }; - let start_sig = Signature::new([], []); - let mut start = Box::new(Function::new(start_id, start_sig)); - { - let body = start.body_mut(); - body.push(Op::Exec(FunctionIdent { - module: entry_module, - function: entry.function, - })); + pub fn from_hir( + program: &hir::Program, + globals: &GlobalVariableAnalysis, + ) -> Result { + let Some(entrypoint) = program.entrypoint() else { + return Err(Report::msg("invalid program: no entrypoint")); + }; + let library = Library::from_hir(program, globals); + Ok(Self { + library, + entrypoint, + }) + } + + /// Link this [Program] against the given kernel during assembly + pub fn link_kernel(&mut self, kernel: KernelLibrary) { + self.library.link_kernel(kernel); + } + + /// Link this [Program] against the given library during assembly + pub fn link_library(&mut self, library: CompiledLibrary) { + self.library.link_library(library); + } + + /// Get the set of [CompiledLibrary] this program links against + pub fn link_libraries(&self) -> &[CompiledLibrary] { + self.library.link_libraries() + } + + /// Generate an executable module which when run expects the raw data segment data to be + /// provided on the advice stack in the same order as initialization, and the operands of + /// the entrypoint function on the operand stack. + fn generate_main(&self, entrypoint: FunctionIdent, emit_test_harness: bool) -> Box { + let mut exe = Box::new(Module::new(LibraryNamespace::Exec.into(), ModuleKind::Executable)); + let start_id = FunctionIdent { + module: Ident::with_empty_span(Symbol::intern(LibraryNamespace::EXEC_PATH)), + function: Ident::with_empty_span(Symbol::intern(ProcedureName::MAIN_PROC_NAME)), + }; + let start_sig = Signature::new([], []); + let mut start = Box::new(Function::new(start_id, start_sig)); + { + let body = start.body_mut(); + self.emit_data_segment_initialization(body); + if emit_test_harness { + self.emit_test_harness(body); } - exe.push_back(start); - modules.insert(exe); + body.push(Op::Exec(entrypoint), SourceSpan::default()); } + exe.push_back(start); + exe + } - let segments = program.segments().clone(); + fn emit_test_harness(&self, block: &mut Block) { + let span = SourceSpan::default(); + // Advice Stack: [dest_ptr, num_words, ...] + block.push(Op::AdvPush(2), span); // => [num_words, dest_ptr] on operand stack + block.push(Op::Exec("std::mem::pipe_words_to_memory".parse().unwrap()), span); + // Drop the commitment + block.push(Op::Drop, span); + // If we know the stack pointer address, update it to the value of `'write_ptr`, but cast + // into the Rust address space (multiplying it by 16). So a word address of 1, is equal to + // a byte address of 16, because each field element holds 4 bytes, and there are 4 elements + // in a word. + // + // If we don't know the stack pointer, just drop the `'write_ptr` value + if let Some(sp) = self.stack_pointer() { + block.push(Op::U32OverflowingMulImm(16), span); + block.push(Op::Assertz, span); + // Align the stack pointer to a word boundary + let elem_addr = (sp / 4) + (sp % 4 > 0) as u32; + let word_addr = (elem_addr / 4) + (elem_addr % 4 > 0) as u32; + block.push(Op::MemStoreImm(word_addr), span); + } else { + block.push(Op::Drop, span); + } + } + + /// Emit the sequence of instructions necessary to consume rodata from the advice stack and + /// populate the global heap with the data segments of this program, verifying that the + /// commitments match. + fn emit_data_segment_initialization(&self, block: &mut Block) { + // Emit data segment initialization code + // + // NOTE: This depends on the program being executed with the data for all data + // segments having been pushed on the advice stack in the same order as visited + // here, with the same encoding. The program will fail to execute if it is not + // set up correctly. + // + // TODO(pauls): To facilitate automation of this, we should emit a file to disk + // that includes the raw encoding of the data we expect to be placed on the advice + // stack, in a manner which allows us to simply read that file as an array of felt + // and use that directly via `AdviceInputs` + let pipe_preimage_to_memory = "std::mem::pipe_preimage_to_memory".parse().unwrap(); + for segment in self.library.segments.iter() { + // Don't bother emitting anything for zeroed segments + if segment.is_zeroed() { + continue; + } + let size = segment.size(); + let offset = segment.offset(); + let base = NativePtr::from_ptr(offset); + let segment_data = segment.init(); + + // TODO(pauls): Do we ever have a need for data segments which are not aligned + // to an word boundary? If so, we need to implement that + // support when emitting the entry for a program + assert_eq!( + base.offset, + 0, + "unsupported data segment alignment {}: must be aligned to a 32 byte boundary", + base.alignment() + ); + assert_eq!( + base.index, + 0, + "unsupported data segment alignment {}: must be aligned to a 32 byte boundary", + base.alignment() + ); + + // Compute the commitment for the data + let num_elements = size.next_multiple_of(4) / 4; + let num_words = num_elements.next_multiple_of(4) / 4; + let mut elements = Vec::with_capacity(num_elements as usize); + // TODO(pauls): If the word containing the first element overlaps with the + // previous segment, then ensure the overlapping elements + // are mixed together, so that the data is preserved, and + // the commitment is correct + let mut iter = segment_data.as_slice().iter().copied().array_chunks::<4>(); + elements.extend(iter.by_ref().map(|bytes| Felt::new(u32::from_be_bytes(bytes) as u64))); + if let Some(remainder) = iter.into_remainder() { + let mut chunk = [0u8; 4]; + for (i, byte) in remainder.into_iter().enumerate() { + chunk[i] = byte; + } + elements.push(Felt::new(u32::from_be_bytes(chunk) as u64)); + } + elements.resize(num_elements as usize, Felt::ZERO); + let digest = Rpo256::hash_elements(&elements); + let span = SourceSpan::default(); + + // COM + block.push(Op::Pushw(digest.into()), span); + // write_ptr + block.push(Op::PushU32(base.waddr), span); + // num_words + block.push(Op::PushU32(num_words), span); + // [num_words, write_ptr, COM, ..] -> [write_ptr'] + block.push(Op::Exec(pipe_preimage_to_memory), span); + // drop write_ptr' + block.push(Op::Drop, span); + } + } + + /// Get the expected [miden_processor::AdviceInputs] needed to execute this program. + pub fn advice_inputs(&self) -> miden_processor::AdviceInputs { + use miden_processor::AdviceInputs; + + let mut stack = Vec::with_capacity( + self.library + .segments + .iter() + .map(|segment| segment.size() as usize) + .sum::() + / 4, + ); + + let mut current_size = 0usize; + for segment in self.library.segments.iter() { + if segment.is_zeroed() { + continue; + } + let size = segment.size() as usize; + let num_elements = size.next_multiple_of(4) / 4; + let num_words = num_elements.next_multiple_of(4) / 4; + let mut iter = segment.init().as_slice().iter().copied().array_chunks::<4>(); + stack.extend(iter.by_ref().map(|bytes| Felt::new(u32::from_be_bytes(bytes) as u64))); + if let Some(remainder) = iter.into_remainder() { + let mut chunk = [0u8; 4]; + for (i, byte) in remainder.into_iter().enumerate() { + chunk[i] = byte; + } + stack.push(Felt::new(u32::from_be_bytes(chunk) as u64)); + } + let num_elements_with_padding = num_words * 4; + stack.resize(current_size + num_elements_with_padding, Felt::ZERO); + current_size += num_elements_with_padding; + } + + AdviceInputs::default().with_stack(stack) + } + + #[inline(always)] + pub fn entrypoint(&self) -> FunctionIdent { + self.entrypoint + } + + #[inline(always)] + pub fn stack_pointer(&self) -> Option { + self.library.stack_pointer + } + + /// Freezes this program, preventing further modifications + pub fn freeze(mut self: Box) -> Arc { + self.library.modules.freeze(); + Arc::from(self) + } + + /// Get an iterator over the modules in this program + pub fn modules(&self) -> impl Iterator + '_ { + self.library.modules.iter() + } + + /// Access the frozen module tree of this program, and panic if not frozen + pub fn unwrap_frozen_modules(&self) -> &FrozenModuleTree { + self.library.unwrap_frozen_modules() + } + + /// Insert a module into this program. + /// + /// The insertion order is not preserved - modules are ordered by name. + /// + /// NOTE: This function will panic if the program has been frozen + pub fn insert(&mut self, module: Box) { + self.library.insert(module) + } + + /// Get a reference to a module in this program by name + pub fn get(&self, name: &Q) -> Option<&Module> + where + Q: ?Sized + Ord, + Ident: core::borrow::Borrow, + { + self.library.get(name) + } + + /// Returns true if this program contains a [Module] named `name` + pub fn contains(&self, name: N) -> bool + where + Ident: PartialEq, + { + self.library.contains(name) + } + + /// Write this [Program] to the given output directory. + pub fn write_to_directory>(&self, path: P) -> std::io::Result<()> { + let path = path.as_ref(); + assert!(path.is_dir()); + + self.library.write_to_directory(path)?; + + let main = self.generate_main(self.entrypoint, /* test_harness= */ false); + main.write_to_directory(path)?; + + Ok(()) + } + + // Assemble this program to MAST + pub fn assemble(&self, session: &Session) -> Result, Report> { + use miden_assembly::{Assembler, CompileOptions}; + + let mut assembler = Assembler::new(session.source_manager.clone()) + .with_debug_mode(session.options.emit_debug_decorators()); + + // Link extra libraries + for library in self.library.libraries.iter() { + assembler.add_compiled_library(library)?; + } + + // Assemble library + for module in self.library.modules.iter() { + let kind = module.kind; + let module = module.to_ast().map(Box::new)?; + assembler.add_module_with_options( + module, + CompileOptions { + kind, + warnings_as_errors: false, + path: None, + }, + )?; + } + + let emit_test_harness = session.get_flag("test_harness"); + let main = self.generate_main(self.entrypoint, emit_test_harness); + let main = main.to_ast().map(Box::new)?; + println!("{main}"); + assembler.assemble_program(main).map(Arc::new) + } + + pub(crate) fn library(&self) -> &Library { + &self.library + } +} + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.library, f) + } +} + +/// A [Library] represents a set of modules and its dependencies, which are compiled/assembled +/// together into a single artifact, and then linked into a [Program] for execution at a later +/// time. +/// +/// Modules are stored in a [Library] in a B-tree map, keyed by the module name. This is done to +/// make accessing modules by name efficient, and to ensure a stable ordering for compiled programs +/// when emitted as text. +#[derive(Default)] +pub struct Library { + /// The set of modules which belong to this program + modules: Modules, + /// The set of libraries to link this program against + libraries: Vec, + /// The kernel library to link against + kernel: Option, + /// The data segment table for this program + pub segments: DataSegmentTable, + /// The address of the `__stack_pointer` global, if such a global has been defined + stack_pointer: Option, +} +impl Library { + /// Create a new, empty [Library] + pub fn empty() -> Self { + Self::default() + } + + /// Create a new [Library] initialized from a [DataSegmentTable] and a set of [Module]s. + /// + /// You should generally prefer to use [Library::from_hir], but this constructor allows you to + /// manually produce a MASM program from its constituent parts. + pub fn new(segments: DataSegmentTable, modules: M) -> Self + where + M: IntoIterator>, + { + let mut module_tree = ModuleTree::default(); + for module in modules { + module_tree.insert(module); + } + let modules = Modules::Open(module_tree); Self { modules, + libraries: vec![], + kernel: None, segments, - entrypoint, + stack_pointer: None, } } - /// Freezes this program, preventing further modifications - pub fn freeze(mut self: Box) -> Arc { + /// Create a new [Library] initialized from an [hir::Program]. + /// + /// The resulting [Library] will have the following: + /// + /// * Data segments described by the original [hir::Program] + /// + /// None of the HIR modules will have been added yet + pub fn from_hir( + program: &hir::Program, + globals: &GlobalVariableAnalysis, + ) -> Self { + let stack_pointer = program.globals().find("__stack_pointer".parse().unwrap()); + let stack_pointer = if let Some(stack_pointer) = stack_pointer { + let global_table_offset = globals.layout().global_table_offset(); + Some(global_table_offset + unsafe { program.globals().offset_of(stack_pointer) }) + } else { + None + }; + Self { + modules: Modules::default(), + libraries: vec![], + kernel: None, + segments: program.segments().clone(), + stack_pointer, + } + } + + /// Link this [Library] against the given kernel during assembly + pub fn link_kernel(&mut self, kernel: KernelLibrary) { + self.kernel = Some(kernel); + } + + /// Link this [Library] against the given library during assembly + pub fn link_library(&mut self, library: CompiledLibrary) { + self.libraries.push(library); + } + + /// Get the set of [CompiledLibrary] this library links against + pub fn link_libraries(&self) -> &[CompiledLibrary] { + self.libraries.as_slice() + } + + /// Freezes this library, preventing further modifications + pub fn freeze(mut self: Box) -> Arc { self.modules.freeze(); Arc::from(self) } - /// Get an iterator over the modules in this program + /// Get an iterator over the modules in this library pub fn modules(&self) -> impl Iterator + '_ { self.modules.iter() } - /// Access the frozen module tree of this program, and panic if not frozen + /// Access the frozen module tree of this library, and panic if not frozen pub fn unwrap_frozen_modules(&self) -> &FrozenModuleTree { match self.modules { Modules::Frozen(ref modules) => modules, @@ -99,7 +472,7 @@ impl Program { } } - /// Insert a module into this program. + /// Insert a module into this library. /// /// The insertion order is not preserved - modules are ordered by name. /// @@ -108,15 +481,7 @@ impl Program { self.modules.insert(module); } - pub fn is_executable(&self) -> bool { - self.entrypoint.is_some() - } - - pub fn is_library(&self) -> bool { - self.entrypoint.is_none() - } - - /// Get a reference to a module in this program by name + /// Get a reference to a module in this library by name pub fn get(&self, name: &Q) -> Option<&Module> where Q: ?Sized + Ord, @@ -125,7 +490,7 @@ impl Program { self.modules.get(name) } - /// Returns true if this program contains a [Module] named `name` + /// Returns true if this library contains a [Module] named `name` pub fn contains(&self, name: N) -> bool where Ident: PartialEq, @@ -133,37 +498,49 @@ impl Program { self.modules.iter().any(|m| m.id == name) } - /// Write this [Program] to the given output directory. - /// - /// The provided [miden_diagnostics::CodeMap] is used for computing source locations. - pub fn write_to_directory>( - &self, - codemap: &miden_diagnostics::CodeMap, - path: P, - ) -> std::io::Result<()> { + /// Write this [Library] to the given output directory. + pub fn write_to_directory>(&self, path: P) -> std::io::Result<()> { let path = path.as_ref(); assert!(path.is_dir()); for module in self.modules.iter() { - module.write_to_directory(codemap, path)?; + module.write_to_directory(path)?; } Ok(()) } + + // Assemble this library to MAST + pub fn assemble(&self, session: &Session) -> Result, Report> { + use miden_assembly::Assembler; + + let mut assembler = Assembler::new(session.source_manager.clone()) + .with_debug_mode(session.options.emit_debug_decorators()); + + // Link extra libraries + for library in self.libraries.iter() { + assembler.add_compiled_library(library)?; + } + + // Assemble library + let mut modules = Vec::with_capacity(self.modules.len()); + for module in self.modules.iter() { + let module = module.to_ast().map(Box::new)?; + modules.push(module); + } + assembler.assemble_library(modules).map(Arc::new) + } } -impl fmt::Display for Program { +impl fmt::Display for Library { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for module in self.modules.iter() { - if module.name.is_exec_path() { + // Don't print intrinsic modules + if module.id.as_str().starts_with("intrinsics::") { continue; } - writeln!(f, "mod {}\n", &module.name)?; - writeln!(f, "{}", module)?; - } - if let Some(module) = self.modules.iter().find(|m| m.name.is_exec_path()) { - writeln!(f, "program\n")?; + writeln!(f, "# mod {}\n", &module.name)?; writeln!(f, "{}", module)?; } diff --git a/codegen/masm/src/masm/region.rs b/codegen/masm/src/masm/region.rs index 02ca85a2e..c710469cd 100644 --- a/codegen/masm/src/masm/region.rs +++ b/codegen/masm/src/masm/region.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeSet, fmt}; use cranelift_entity::PrimaryMap; use miden_assembly::ast; -use midenc_hir::{formatter::PrettyPrint, FunctionIdent, Ident}; +use midenc_hir::{diagnostics::Span, formatter::PrettyPrint, FunctionIdent, Ident}; use smallvec::smallvec; use super::*; @@ -53,7 +53,7 @@ impl Region { } /// Get the instruction under `ip`, if valid - pub fn get(&self, ip: InstructionPointer) -> Option { + pub fn get(&self, ip: InstructionPointer) -> Option> { self.blocks[ip.block].ops.get(ip.index).copied() } @@ -85,11 +85,10 @@ impl Region { /// local/external function maps to handle calls present in the body of the region. pub fn to_block( &self, - codemap: &miden_diagnostics::CodeMap, imports: &ModuleImportInfo, locals: &BTreeSet, ) -> ast::Block { - emit_block(self.body, &self.blocks, codemap, imports, locals) + emit_block(self.body, &self.blocks, imports, locals) } /// Create a [Region] from a [miden_assembly::ast::CodeBody] and the set of imports @@ -149,11 +148,13 @@ fn import_block( for op in block.iter() { match op { ast::Op::Inst(ix) => { + let span = ix.span(); let current_block = region.block_mut(current_block_id); - let mut ops = Op::from_masm(current_module, (**ix).clone()); - current_block.append(&mut ops); + let ops = Op::from_masm(current_module, (**ix).clone()); + current_block.extend(ops.into_iter().map(|op| Span::new(span, op))); } ast::Op::If { + span, ref then_blk, ref else_blk, .. @@ -162,22 +163,25 @@ fn import_block( let else_blk_id = region.create_block(); import_block(current_module, region, then_blk_id, then_blk); import_block(current_module, region, else_blk_id, else_blk); - region.block_mut(current_block_id).push(Op::If(then_blk_id, else_blk_id)); + region.block_mut(current_block_id).push(Op::If(then_blk_id, else_blk_id), *span); } ast::Op::Repeat { - count, ref body, .. + span, + count, + ref body, + .. } => { let body_blk = region.create_block(); import_block(current_module, region, body_blk, body); let count = u16::try_from(*count).unwrap_or_else(|_| { panic!("invalid repeat count: expected {count} to be less than 255") }); - region.block_mut(current_block_id).push(Op::Repeat(count, body_blk)); + region.block_mut(current_block_id).push(Op::Repeat(count, body_blk), *span); } - ast::Op::While { ref body, .. } => { + ast::Op::While { span, ref body, .. } => { let body_blk = region.create_block(); import_block(current_module, region, body_blk, body); - region.block_mut(current_block_id).push(Op::While(body_blk)); + region.block_mut(current_block_id).push(Op::While(body_blk), *span); } } } @@ -189,34 +193,31 @@ fn import_block( fn emit_block( block_id: BlockId, blocks: &PrimaryMap, - codemap: &miden_diagnostics::CodeMap, imports: &ModuleImportInfo, locals: &BTreeSet, ) -> ast::Block { let current_block = &blocks[block_id]; let mut ops = Vec::with_capacity(current_block.ops.len()); for op in current_block.ops.iter().copied() { - match op { + let span = op.span(); + match op.into_inner() { Op::If(then_blk, else_blk) => { - let then_blk = emit_block(then_blk, blocks, codemap, imports, locals); - let else_blk = emit_block(else_blk, blocks, codemap, imports, locals); + let then_blk = emit_block(then_blk, blocks, imports, locals); + let else_blk = emit_block(else_blk, blocks, imports, locals); ops.push(ast::Op::If { - span: Default::default(), + span, then_blk, else_blk, }); } Op::While(blk) => { - let body = emit_block(blk, blocks, codemap, imports, locals); - ops.push(ast::Op::While { - span: Default::default(), - body, - }); + let body = emit_block(blk, blocks, imports, locals); + ops.push(ast::Op::While { span, body }); } Op::Repeat(n, blk) => { - let body = emit_block(blk, blocks, codemap, imports, locals); + let body = emit_block(blk, blocks, imports, locals); ops.push(ast::Op::Repeat { - span: Default::default(), + span, count: n as u32, body, }); @@ -225,7 +226,7 @@ fn emit_block( ops.extend( op.into_masm(imports, locals) .into_iter() - .map(|inst| ast::Op::Inst(miden_assembly::Span::unknown(inst))), + .map(|inst| ast::Op::Inst(Span::new(span, inst))), ); } } diff --git a/codegen/masm/src/tests.rs b/codegen/masm/src/tests.rs index 9a0830d64..a0dc28116 100644 --- a/codegen/masm/src/tests.rs +++ b/codegen/masm/src/tests.rs @@ -127,7 +127,6 @@ impl TestByEmulationHarness { convert_to_masm .convert(function, &mut analyses, &self.context.session) .map(Box::new) - .map_err(CompilerError::Conversion) } pub fn set_cycle_budget(&mut self, budget: usize) { @@ -285,7 +284,7 @@ fn fib_emulator() { .expect("failed to link program"); let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); + let program = compiler.compile(program).expect("compilation failed").unwrap_executable(); println!("{}", program.get("test").unwrap()); @@ -341,7 +340,7 @@ fn codegen_fundamental_if() { let program = builder.with_entrypoint(id).link().expect("failed to link program"); let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); + let program = compiler.compile(program).expect("compilation failed").unwrap_executable(); let a = Felt::new(3); let b = Felt::new(4); @@ -407,7 +406,7 @@ fn codegen_fundamental_loops() { let program = builder.with_entrypoint(id).link().expect("failed to link program"); let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); + let program = compiler.compile(program).expect("compilation failed").unwrap_executable(); let a = Felt::new(3); let n = Felt::new(4); @@ -438,7 +437,7 @@ fn codegen_sum_matrix() { // Compile let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); + let program = compiler.compile(program).expect("compilation failed").unwrap_executable(); println!("{}", program.get("test").unwrap()); @@ -479,7 +478,7 @@ fn i32_checked_neg() { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -527,7 +526,11 @@ fn codegen_mem_store_sw_load_sw() { let program = builder.with_entrypoint(id).link().expect("failed to link program"); let mut compiler = MasmCompiler::new(&context.session); - let program = compiler.compile(program).expect("compilation failed").freeze(); + let program = compiler + .compile(program) + .expect("compilation failed") + .unwrap_executable() + .freeze(); // eprintln!("{}", program); @@ -603,7 +606,7 @@ macro_rules! proptest_unary_numeric_op_impl { // Compile let mut compiler = MasmCompiler::new(&harness.context.session); - let program = compiler.compile(program).expect("compilation failed"); + let program = compiler.compile(program).expect("compilation failed").unwrap_executable(); harness .emulator @@ -635,15 +638,15 @@ macro_rules! proptest_unary_numeric_op_impl { } proptest_unary_numeric_op!(u64::clz, u64 => u32, leading_zeros); -proptest_unary_numeric_op!(i128::clz, i128 => u32, leading_zeros); +//proptest_unary_numeric_op!(i128::clz, i128 => u32, leading_zeros); proptest_unary_numeric_op!(u64::ctz, u64 => u32, trailing_zeros); -proptest_unary_numeric_op!(i128::ctz, i128 => u32, trailing_zeros); +//proptest_unary_numeric_op!(i128::ctz, i128 => u32, trailing_zeros); proptest_unary_numeric_op!(u64::clo, u64 => u32, leading_ones); -proptest_unary_numeric_op!(i128::clo, i128 => u32, leading_ones); +//proptest_unary_numeric_op!(i128::clo, i128 => u32, leading_ones); proptest_unary_numeric_op!(u64::cto, u64 => u32, trailing_ones); -proptest_unary_numeric_op!(i128::cto, i128 => u32, trailing_ones); +//proptest_unary_numeric_op!(i128::cto, i128 => u32, trailing_ones); proptest_unary_numeric_op!(u64::ilog2, u64 => u32, ilog2, 1..u64::MAX); -proptest_unary_numeric_op!(i128::ilog2, i128 => u32, ilog2, 1..i128::MAX); +//proptest_unary_numeric_op!(i128::ilog2, i128 => u32, ilog2, 1..i128::MAX); trait ToCanonicalRepr { fn ir_type() -> Type; @@ -811,7 +814,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsics module"), ) .freeze(), @@ -871,7 +874,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -895,7 +898,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -927,7 +930,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -959,7 +962,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -993,7 +996,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -1018,7 +1021,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -1044,7 +1047,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -1069,7 +1072,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), @@ -1095,7 +1098,7 @@ proptest! { .emulator .load_module( Box::new( - intrinsics::load("intrinsics::i32", &harness.context.session.codemap) + intrinsics::load("intrinsics::i32", &harness.context.session.source_manager) .expect("undefined intrinsic module"), ) .freeze(), diff --git a/frontend-wasm/Cargo.toml b/frontend-wasm/Cargo.toml index 2fdfd3024..ee49e77c3 100644 --- a/frontend-wasm/Cargo.toml +++ b/frontend-wasm/Cargo.toml @@ -12,22 +12,23 @@ readme.workspace = true edition.workspace = true [dependencies] -miden-core.workspace = true -midenc-hir.workspace = true -midenc-hir-type.workspace = true -miden-diagnostics.workspace = true -thiserror.workspace = true -smallvec.workspace = true -log.workspace = true anyhow.workspace = true -wasmparser = "0.118.1" +addr2line = "0.24" derive_more.workspace = true -indexmap.workspace = true -gimli = { version = "0.28.0", default-features = false, features = [ +gimli = { version = "0.31", default-features = false, features = [ 'read', 'std', ] } +indexmap.workspace = true +log.workspace = true +miden-core.workspace = true +midenc-hir.workspace = true +midenc-hir-type.workspace = true +midenc-session.workspace = true rustc-hash.workspace = true +smallvec.workspace = true +thiserror.workspace = true +wasmparser = "0.214" [dev-dependencies] wat.workspace = true diff --git a/frontend-wasm/src/code_translator/mod.rs b/frontend-wasm/src/code_translator/mod.rs index 5e55db029..9b33a2339 100644 --- a/frontend-wasm/src/code_translator/mod.rs +++ b/frontend-wasm/src/code_translator/mod.rs @@ -13,18 +13,17 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 -use std::collections::hash_map; - -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; use midenc_hir::{ - cranelift_entity::packed_option::ReservedValue, Block, FieldElement, Immediate, Inst, - InstBuilder, Type, Type::*, Value, + cranelift_entity::packed_option::ReservedValue, + diagnostics::{DiagnosticsHandler, IntoDiagnostic, Report, Severity, SourceSpan}, + Block, FieldElement, Immediate, Inst, InstBuilder, Type, + Type::*, + Value, }; -use rustc_hash::FxHashMap; use wasmparser::{MemArg, Operator}; use crate::{ - error::{WasmError, WasmResult}, + error::WasmResult, intrinsics::{convert_intrinsics_call, is_miden_intrinsics_module}, miden_abi::{is_miden_abi_module, transform::transform_miden_abi_call}, module::{ @@ -57,7 +56,7 @@ pub fn translate_operator( span: SourceSpan, ) -> WasmResult<()> { if !state.reachable { - translate_unreachable_operator(op, builder, state, mod_types, span)?; + translate_unreachable_operator(op, builder, state, mod_types, diagnostics, span)?; return Ok(()); } @@ -85,13 +84,13 @@ pub fn translate_operator( Operator::GlobalGet { global_index } => { let global_index = GlobalIndex::from_u32(*global_index); let name = module.global_name(global_index); - let ty = ir_type(module.globals[global_index].ty)?; + let ty = ir_type(module.globals[global_index].ty, diagnostics)?; state.push1(builder.ins().load_symbol(name.as_str(), ty, span)); } Operator::GlobalSet { global_index } => { let global_index = GlobalIndex::from_u32(*global_index); let name = module.global_name(global_index); - let ty = ir_type(module.globals[global_index].ty)?; + let ty = ir_type(module.globals[global_index].ty, diagnostics)?; let ptr = builder .ins() .symbol_addr(name.as_str(), Ptr(ty.clone().into()), span); @@ -132,9 +131,9 @@ pub fn translate_operator( } Operator::Nop => {} /***************************** Control flow blocks *********************************/ - Operator::Block { blockty } => translate_block(blockty, builder, state, mod_types, span)?, - Operator::Loop { blockty } => translate_loop(blockty, builder, state, mod_types, span)?, - Operator::If { blockty } => translate_if(blockty, state, builder, mod_types, span)?, + Operator::Block { blockty } => translate_block(blockty, builder, state, mod_types, diagnostics, span)?, + Operator::Loop { blockty } => translate_loop(blockty, builder, state, mod_types, diagnostics, span)?, + Operator::If { blockty } => translate_if(blockty, state, builder, mod_types, diagnostics, span)?, Operator::Else => translate_else(state, builder, span)?, Operator::End => translate_end(state, builder, span), @@ -154,7 +153,7 @@ pub fn translate_operator( diagnostics, )?; } - Operator::CallIndirect { type_index: _, table_index: _, table_byte: _ } => { + Operator::CallIndirect { type_index: _, table_index: _ } => { // TODO: } /******************************* Memory management *********************************/ @@ -510,100 +509,6 @@ pub fn translate_operator( Ok(()) } -fn translate_br_table( - targets: &wasmparser::BrTable<'_>, - state: &mut FuncTranslationState, - builder: &mut FunctionBuilderExt, - span: SourceSpan, -) -> Result<(), WasmError> { - let default = targets.default(); - let mut min_depth = default; - for depth in targets.targets() { - let depth = depth?; - if depth < min_depth { - min_depth = depth; - } - } - let jump_args_count = { - let i = state.control_stack.len() - 1 - (min_depth as usize); - let min_depth_frame = &state.control_stack[i]; - if min_depth_frame.is_loop() { - min_depth_frame.num_param_values() - } else { - min_depth_frame.num_return_values() - } - }; - let val = state.pop1(); - let val = if builder.data_flow_graph().value_type(val) != &U32 { - builder.ins().cast(val, U32, span) - } else { - val - }; - let mut data = Vec::with_capacity(targets.len() as usize); - if jump_args_count == 0 { - // No jump arguments - for depth in targets.targets() { - let depth = depth?; - let block = { - let i = state.control_stack.len() - 1 - (depth as usize); - let frame = &mut state.control_stack[i]; - frame.set_branched_to_exit(); - frame.br_destination() - }; - data.push((depth, block)); - } - let def_block = { - let i = state.control_stack.len() - 1 - (default as usize); - let frame = &mut state.control_stack[i]; - frame.set_branched_to_exit(); - frame.br_destination() - }; - builder.ins().switch(val, data, def_block, span); - } else { - // Here we have jump arguments, but Midens's switch op doesn't support them - // We then proceed to split the edges going out of the br_table - let return_count = jump_args_count; - let mut dest_block_sequence = vec![]; - let mut dest_block_map = FxHashMap::default(); - for depth in targets.targets() { - let depth = depth?; - let branch_block = match dest_block_map.entry(depth as usize) { - hash_map::Entry::Occupied(entry) => *entry.get(), - hash_map::Entry::Vacant(entry) => { - let block = builder.create_block(); - dest_block_sequence.push((depth as usize, block)); - *entry.insert(block) - } - }; - data.push((depth, branch_block)); - } - let default_branch_block = match dest_block_map.entry(default as usize) { - hash_map::Entry::Occupied(entry) => *entry.get(), - hash_map::Entry::Vacant(entry) => { - let block = builder.create_block(); - dest_block_sequence.push((default as usize, block)); - *entry.insert(block) - } - }; - builder.ins().switch(val, data, default_branch_block, span); - for (depth, dest_block) in dest_block_sequence { - builder.switch_to_block(dest_block); - builder.seal_block(dest_block); - let real_dest_block = { - let i = state.control_stack.len() - 1 - depth; - let frame = &mut state.control_stack[i]; - frame.set_branched_to_exit(); - frame.br_destination() - }; - let destination_args = state.peekn_mut(return_count); - builder.ins().br(real_dest_block, destination_args, span); - } - state.popn(return_count); - } - state.reachable = false; - Ok(()) -} - fn translate_load( ptr_ty: Type, memarg: &MemArg, @@ -842,14 +747,75 @@ fn translate_br_if_args( (br_destination, inputs) } +fn translate_br_table( + br_targets: &wasmparser::BrTable<'_>, + state: &mut FuncTranslationState, + builder: &mut FunctionBuilderExt, + span: SourceSpan, +) -> Result<(), Report> { + let mut targets = Vec::default(); + for depth in br_targets.targets() { + let depth = depth.into_diagnostic()?; + + targets.push(depth); + } + targets.sort(); + + let default_depth = br_targets.default(); + let min_depth = + core::cmp::min(targets.iter().copied().min().unwrap_or(default_depth), default_depth); + + let argc = { + let i = state.control_stack.len() - 1 - (min_depth as usize); + let min_depth_frame = &state.control_stack[i]; + if min_depth_frame.is_loop() { + min_depth_frame.num_param_values() + } else { + min_depth_frame.num_return_values() + } + }; + + let default_block = { + let i = state.control_stack.len() - 1 - (default_depth as usize); + let frame = &mut state.control_stack[i]; + frame.set_branched_to_exit(); + frame.br_destination() + }; + + let val = state.pop1(); + let val = if builder.data_flow_graph().value_type(val) != &U32 { + builder.ins().cast(val, U32, span) + } else { + val + }; + + let switch_builder = builder.ins().switch(val, span); + let switch_builder = targets.into_iter().fold(switch_builder, |acc, depth| { + let block = { + let i = state.control_stack.len() - 1 - (depth as usize); + let frame = &mut state.control_stack[i]; + frame.set_branched_to_exit(); + frame.br_destination() + }; + let args = state.peekn_mut(argc); + acc.case(depth, block, args) + }); + switch_builder.or_else(default_block, state.peekn_mut(argc)); + + state.popn(argc); + state.reachable = false; + Ok(()) +} + fn translate_block( blockty: &wasmparser::BlockType, builder: &mut FunctionBuilderExt, state: &mut FuncTranslationState, mod_types: &ModuleTypes, + diagnostics: &DiagnosticsHandler, span: SourceSpan, ) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; + let blockty = BlockType::from_wasm(blockty, mod_types, diagnostics)?; let next = builder.create_block_with_params(blockty.results.clone(), span); state.push_block(next, blockty.params.len(), blockty.results.len()); Ok(()) @@ -966,9 +932,10 @@ fn translate_if( state: &mut FuncTranslationState, builder: &mut FunctionBuilderExt, mod_types: &ModuleTypes, + diagnostics: &DiagnosticsHandler, span: SourceSpan, ) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; + let blockty = BlockType::from_wasm(blockty, mod_types, diagnostics)?; let cond = state.pop1(); // cond is expected to be a i32 value let cond_i1 = builder.ins().neq_imm(cond, Immediate::I32(0), span); @@ -1023,9 +990,10 @@ fn translate_loop( builder: &mut FunctionBuilderExt, state: &mut FuncTranslationState, mod_types: &ModuleTypes, + diagnostics: &DiagnosticsHandler, span: SourceSpan, ) -> WasmResult<()> { - let blockty = BlockType::from_wasm(blockty, mod_types)?; + let blockty = BlockType::from_wasm(blockty, mod_types, diagnostics)?; let loop_body = builder.create_block_with_params(blockty.params.clone(), span); let next = builder.create_block_with_params(blockty.results.clone(), span); builder.ins().br(loop_body, state.peekn(blockty.params.len()), span); @@ -1044,6 +1012,7 @@ fn translate_unreachable_operator( builder: &mut FunctionBuilderExt, state: &mut FuncTranslationState, mod_types: &ModuleTypes, + diagnostics: &DiagnosticsHandler, span: SourceSpan, ) -> WasmResult<()> { debug_assert!(!state.reachable); @@ -1051,7 +1020,7 @@ fn translate_unreachable_operator( Operator::If { blockty } => { // Push a placeholder control stack entry. The if isn't reachable, // so we don't have any branches anywhere. - let blockty = BlockType::from_wasm(&blockty, mod_types)?; + let blockty = BlockType::from_wasm(&blockty, mod_types, diagnostics)?; state.push_if( Block::reserved_value(), ElseData::NoElse { diff --git a/frontend-wasm/src/code_translator/tests.rs b/frontend-wasm/src/code_translator/tests.rs index 16a04a027..4352fc7c2 100644 --- a/frontend-wasm/src/code_translator/tests.rs +++ b/frontend-wasm/src/code_translator/tests.rs @@ -3,11 +3,13 @@ use core::fmt::Write; use expect_test::expect; use midenc_hir::Ident; -use crate::{test_utils::test_diagnostics, translate, WasmTranslationConfig}; +use crate::{test_utils::test_context, translate, WasmTranslationConfig}; /// Check IR generated for a Wasm op(s). /// Wrap Wasm ops in a function and check the IR generated for the entry block of that function. fn check_op(wat_op: &str, expected_ir: expect_test::Expect) { + let context = test_context(); + let wat = format!( r#" (module @@ -18,8 +20,7 @@ fn check_op(wat_op: &str, expected_ir: expect_test::Expect) { )"#, ); let wasm = wat::parse_str(wat).unwrap(); - let diagnostics = test_diagnostics(); - let module = translate(&wasm, &WasmTranslationConfig::default(), &diagnostics) + let module = translate(&wasm, &WasmTranslationConfig::default(), &context.session) .unwrap() .unwrap_one_module(); let func = module.function(Ident::from("test_wrapper")).unwrap(); diff --git a/frontend-wasm/src/code_translator/tests_unsupported.rs b/frontend-wasm/src/code_translator/tests_unsupported.rs index 0b546aea4..aa21631eb 100644 --- a/frontend-wasm/src/code_translator/tests_unsupported.rs +++ b/frontend-wasm/src/code_translator/tests_unsupported.rs @@ -1,5 +1,4 @@ -use miden_diagnostics::SourceSpan; -use midenc_hir::{CallConv, Linkage, ModuleBuilder, Signature}; +use midenc_hir::{CallConv, Linkage, ModuleBuilder, Signature, SourceSpan}; use wasmparser::{MemArg, Operator, Operator::*}; use super::translate_operator; @@ -10,13 +9,13 @@ use crate::{ module_translation_state::ModuleTranslationState, Module, }, - test_utils::test_diagnostics, + test_utils::test_context, }; fn check_unsupported(op: &Operator) { - let diagnostics = test_diagnostics(); + let context = test_context(); let mod_name = "noname"; - let module_info = Module::new(); + let module_info = Module::default(); let mut module_builder = ModuleBuilder::new(mod_name); let sig = Signature { params: vec![], @@ -29,7 +28,8 @@ fn check_unsupported(op: &Operator) { let mod_types = Default::default(); let mut state = FuncTranslationState::new(); let mut builder_ext = FunctionBuilderExt::new(&mut module_func_builder, &mut fb_ctx); - let mut module_state = ModuleTranslationState::new(&module_info, &mod_types, vec![]); + let mut module_state = + ModuleTranslationState::new(&module_info, &mod_types, vec![], &context.session.diagnostics); let result = translate_operator( op, &mut builder_ext, @@ -37,7 +37,7 @@ fn check_unsupported(op: &Operator) { &mut module_state, &module_info, &mod_types, - &diagnostics, + &context.session.diagnostics, SourceSpan::default(), ); assert!(result.is_err(), "Expected unsupported op error for {:?}", op); @@ -45,7 +45,6 @@ fn check_unsupported(op: &Operator) { result.unwrap_err().to_string(), format!("Unsupported Wasm: Wasm op {:?} is not supported", op) ); - assert!(diagnostics.has_errors(), "Expected diagnostics to have errors"); } // Wasm Spec v1.0 diff --git a/frontend-wasm/src/component/build_ir.rs b/frontend-wasm/src/component/build_ir.rs index d22744381..16f76f062 100644 --- a/frontend-wasm/src/component/build_ir.rs +++ b/frontend-wasm/src/component/build_ir.rs @@ -1,37 +1,40 @@ -use miden_diagnostics::DiagnosticsHandler; -use wasmparser::WasmFeatures; +use midenc_hir::diagnostics::Report; +use midenc_session::Session; use super::{ inline, translator::ComponentTranslator, ComponentTypesBuilder, LinearComponentTranslation, ParsedRootComponent, }; -use crate::{component::ComponentParser, error::WasmResult, WasmTranslationConfig}; +use crate::{ + component::ComponentParser, error::WasmResult, supported_component_model_features, + WasmTranslationConfig, +}; /// Translate a Wasm component binary into Miden IR component pub fn translate_component( wasm: &[u8], config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, + session: &Session, ) -> WasmResult { - let (mut component_types_builder, parsed_component) = parse(config, wasm, diagnostics)?; + let (mut component_types_builder, parsed_component) = parse(config, wasm, session)?; let linearized_component_translation = inline(&mut component_types_builder, &parsed_component)?; let component_types = component_types_builder.finish(); let parsed_modules = parsed_component.static_modules; - let translator = ComponentTranslator::new(component_types, parsed_modules, config, diagnostics); + let translator = ComponentTranslator::new(component_types, parsed_modules, config, session); translator.translate(linearized_component_translation) } fn parse<'data>( config: &WasmTranslationConfig, wasm: &'data [u8], - diagnostics: &DiagnosticsHandler, -) -> Result<(ComponentTypesBuilder, ParsedRootComponent<'data>), crate::WasmError> { - let wasm_features = WasmFeatures::all(); - let mut validator = wasmparser::Validator::new_with_features(wasm_features); + session: &Session, +) -> Result<(ComponentTypesBuilder, ParsedRootComponent<'data>), Report> { + let mut validator = + wasmparser::Validator::new_with_features(supported_component_model_features()); let mut component_types_builder = Default::default(); let component_parser = - ComponentParser::new(config, &mut validator, &mut component_types_builder); - let parsed_component = component_parser.parse(wasm, diagnostics)?; + ComponentParser::new(config, session, &mut validator, &mut component_types_builder); + let parsed_component = component_parser.parse(wasm)?; Ok((component_types_builder, parsed_component)) } @@ -55,7 +58,7 @@ fn inline( &parsed_component.static_modules, &parsed_component.static_components, ) - .map_err(|e| crate::WasmError::Unsupported(e.to_string()))?; + .map_err(|e| Report::msg(e))?; Ok(component_dfg.finish()) } @@ -66,9 +69,7 @@ mod tests { use midenc_hir_type::Type; use super::*; - use crate::{ - component::StaticModuleIndex, config::ImportMetadata, test_utils::test_diagnostics, - }; + use crate::{component::StaticModuleIndex, config::ImportMetadata, test_utils::test_context}; #[test] fn translate_simple() { @@ -97,10 +98,10 @@ mod tests { "# .to_string(); let wasm = wat::parse_str(wat).unwrap(); - let diagnostics = test_diagnostics(); + let context = test_context(); let config = Default::default(); let (mut component_types_builder, parsed_component) = - parse(&config, &wasm, &diagnostics).unwrap(); + parse(&config, &wasm, &context.session).unwrap(); let component_translation = inline(&mut component_types_builder, &parsed_component).unwrap(); @@ -119,7 +120,7 @@ mod tests { component_types, parsed_component.static_modules, &config, - &diagnostics, + &context.session, ); let ir = translator.translate(component_translation).unwrap(); @@ -176,7 +177,7 @@ mod tests { ) "#.to_string(); let wasm = wat::parse_str(wat).unwrap(); - let diagnostics = test_diagnostics(); + let context = test_context(); let interface_function_ident = InterfaceFunctionIdent { interface: InterfaceIdent::from_full_ident("miden:add/add@1.0.0".to_string()), function: Symbol::intern("add"), @@ -195,7 +196,7 @@ mod tests { ..Default::default() }; let (mut component_types_builder, parsed_component) = - parse(&config, &wasm, &diagnostics).unwrap(); + parse(&config, &wasm, &context.session).unwrap(); let component_translation = inline(&mut component_types_builder, &parsed_component).unwrap(); assert_eq!(parsed_component.static_modules.len(), 1); @@ -224,7 +225,7 @@ mod tests { component_types, parsed_component.static_modules, &config, - &diagnostics, + &context.session, ); let ir = translator.translate(component_translation).unwrap(); diff --git a/frontend-wasm/src/component/info.rs b/frontend-wasm/src/component/info.rs index 3e4126c42..5cf0e4c53 100644 --- a/frontend-wasm/src/component/info.rs +++ b/frontend-wasm/src/component/info.rs @@ -61,6 +61,7 @@ pub struct LinearComponentTranslation { /// NB: Lots of the component model is not yet implemented in the runtime so /// this is going to undergo a lot of churn. #[derive(Default, Debug)] +#[allow(dead_code)] pub struct LinearComponent { /// A list of typed values that this component imports. /// @@ -213,6 +214,7 @@ pub enum GlobalInitializer { /// Declares a new defined resource within this component. /// /// Contains information about the destructor, for example. + #[allow(dead_code)] Resource(Resource), } @@ -223,6 +225,7 @@ pub struct ExtractMemory { /// The index of the memory being defined. pub index: RuntimeMemoryIndex, /// Where this memory is being extracted from. + #[allow(dead_code)] pub export: CoreExport, } @@ -260,6 +263,7 @@ pub enum InstantiateModule { /// This is similar to `Upvar` but notably the imports are provided as a /// two-level named map since import resolution order needs to happen at /// runtime. + #[allow(dead_code)] Import(RuntimeImportIndex, IndexMap>), } @@ -357,14 +361,17 @@ pub enum Export { options: CanonicalOptions, }, /// A module defined within this component is exported. + #[allow(dead_code)] ModuleStatic(StaticModuleIndex), /// A module imported into this component is exported. + #[allow(dead_code)] ModuleImport(RuntimeImportIndex), /// A nested instance is being exported which has recursively defined /// `Export` items. Instance(IndexMap), /// An exported type from a component or instance, currently only /// informational. + #[allow(dead_code)] Type(TypeDef), } @@ -372,12 +379,14 @@ pub enum Export { #[derive(Debug, Clone)] pub struct CanonicalOptions { /// The component instance that this bundle was associated with. + #[allow(dead_code)] pub instance: RuntimeComponentInstanceIndex, /// The encoding used for strings. pub string_encoding: StringEncoding, /// The memory used by these options, if specified. + #[allow(dead_code)] pub memory: Option, /// The realloc function used by these options, if specified. @@ -403,6 +412,7 @@ pub enum StringEncoding { /// This will have the effect of initializing runtime state for this resource, /// namely the destructor is fetched and stored. #[derive(Debug)] +#[allow(dead_code)] pub struct Resource { /// The local index of the resource being defined. pub index: DefinedResourceIndex, @@ -442,12 +452,15 @@ pub enum Trampoline { /// A `resource.new` intrinsic which will inject a new resource into the /// table specified. + #[allow(dead_code)] ResourceNew(TypeResourceTableIndex), /// Same as `ResourceNew`, but for the `resource.rep` intrinsic. + #[allow(dead_code)] ResourceRep(TypeResourceTableIndex), /// Same as `ResourceNew`, but for the `resource.drop` intrinsic. + #[allow(dead_code)] ResourceDrop(TypeResourceTableIndex), /// An intrinsic used by FACT-generated modules which will transfer an owned diff --git a/frontend-wasm/src/component/parser.rs b/frontend-wasm/src/component/parser.rs index 4590e7509..a06002497 100644 --- a/frontend-wasm/src/component/parser.rs +++ b/frontend-wasm/src/component/parser.rs @@ -6,8 +6,11 @@ use std::{collections::HashMap, mem}; use indexmap::IndexMap; -use miden_diagnostics::DiagnosticsHandler; -use midenc_hir::cranelift_entity::PrimaryMap; +use midenc_hir::{ + cranelift_entity::PrimaryMap, + diagnostics::{IntoDiagnostic, Severity}, +}; +use midenc_session::Session; use rustc_hash::FxHashMap; use wasmparser::{ types::{ @@ -28,7 +31,7 @@ use crate::{ }, }, translation_utils::BuildFxHasher, - unsupported_diag, WasmError, WasmTranslationConfig, + unsupported_diag, WasmTranslationConfig, }; /// Structure used to parse a Wasm component @@ -36,6 +39,9 @@ pub struct ComponentParser<'a, 'data> { /// Configuration options for the translation. config: &'a WasmTranslationConfig, + /// The current compilation session + session: &'a Session, + /// The current component being parsed. /// /// This will get swapped out as parsing traverses the body of a @@ -285,11 +291,13 @@ impl<'a, 'data> ComponentParser<'a, 'data> { /// Creates a new parsing state ready to parse a component. pub fn new( config: &'a WasmTranslationConfig, + session: &'a Session, validator: &'a mut Validator, types: &'a mut ComponentTypesBuilder, ) -> Self { Self { config, + session, result: ParsedComponent::default(), validator, types, @@ -301,14 +309,10 @@ impl<'a, 'data> ComponentParser<'a, 'data> { } /// Parses the given the Wasm component - pub fn parse( - mut self, - component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> Result, crate::WasmError> { + pub fn parse(mut self, component: &'data [u8]) -> WasmResult> { let mut remaining = component; loop { - let payload = match self.parser.parse(remaining, true)? { + let payload = match self.parser.parse(remaining, true).into_diagnostic()? { Chunk::Parsed { payload, consumed } => { remaining = &remaining[consumed..]; payload @@ -316,7 +320,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { Chunk::NeedMoreData(_) => unreachable!(), }; - match self.parse_payload(payload, component, diagnostics)? { + match self.parse_payload(payload, component)? { Action::KeepGoing => {} Action::Skip(n) => remaining = &remaining[n..], Action::Done => break, @@ -337,7 +341,6 @@ impl<'a, 'data> ComponentParser<'a, 'data> { &mut self, payload: Payload<'data>, component: &'data [u8], - diagnostics: &DiagnosticsHandler, ) -> WasmResult { match payload { Payload::Version { @@ -345,7 +348,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { encoding, range, } => { - self.validator.version(num, encoding, &range)?; + self.validator.version(num, encoding, &range).into_diagnostic()?; match encoding { Encoding::Component => {} @@ -356,7 +359,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { } Payload::End(offset) => { assert!(self.result.types.is_none()); - self.result.types = Some(self.validator.end(offset)?); + self.result.types = Some(self.validator.end(offset).into_diagnostic()?); // Exit the current lexical scope. If there is no parent (no // frame currently on the stack) then parsing is finished. // Otherwise that means that a nested component has been @@ -377,20 +380,31 @@ impl<'a, 'data> ComponentParser<'a, 'data> { .push(LocalInitializer::ComponentStatic(static_idx, closure_args)); } Payload::ComponentTypeSection(s) => self.component_type_section(s)?, - Payload::CoreTypeSection(s) => self.validator.core_type_section(&s)?, + Payload::CoreTypeSection(s) => { + self.validator.core_type_section(&s).into_diagnostic()? + } Payload::ComponentImportSection(s) => self.component_import_section(s)?, Payload::ComponentCanonicalSection(s) => self.component_canonical_section(s)?, - Payload::ModuleSection { parser, range } => { - self.module_section(range.clone(), parser, component, diagnostics)?; + Payload::ModuleSection { + parser, + unchecked_range: range, + } => { + self.module_section(range.clone(), parser, component)?; return Ok(Action::Skip(range.end - range.start)); } - Payload::ComponentSection { parser, range } => self.component_section(range, parser)?, + Payload::ComponentSection { + parser, + unchecked_range: range, + } => self.component_section(range, parser)?, Payload::InstanceSection(s) => self.core_instance_section(s)?, Payload::ComponentInstanceSection(s) => self.component_instance_section(s)?, Payload::ComponentExportSection(s) => self.component_export_section(s)?, Payload::ComponentStartSection { start, range } => { - self.validator.component_start_section(&start, &range)?; - unsupported_diag!(diagnostics, "component start section is not supported"); + self.validator.component_start_section(&start, &range).into_diagnostic()?; + unsupported_diag!( + &self.session.diagnostics, + "component start section is not supported" + ); } Payload::ComponentAliasSection(s) => self.component_alias_section(s)?, // All custom sections are ignored at this time. @@ -402,8 +416,8 @@ impl<'a, 'data> ComponentParser<'a, 'data> { // if it gets past validation provide a helpful error message to // debug. other => { - self.validator.payload(&other)?; - unsupported_diag!(diagnostics, "unsupported section {other:?}"); + self.validator.payload(&other).into_diagnostic()?; + unsupported_diag!(&self.session.diagnostics, "unsupported section {other:?}"); } } @@ -413,7 +427,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_type_section( &mut self, s: wasmparser::ComponentTypeSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // When we see a type section the types are validated and then parsed. // Each active type definition is recorded in the // `ComponentTypesBuilder` tables, or this component's active scope. @@ -422,10 +436,10 @@ impl<'a, 'data> ComponentParser<'a, 'data> { // `Version` and `End` since multiple type sections can appear within a // component. let mut component_type_index = self.validator.types(0).unwrap().component_type_count(); - self.validator.component_type_section(&s)?; + self.validator.component_type_section(&s).into_diagnostic()?; let types = self.validator.types(0).unwrap(); for ty in s { - match ty? { + match ty.into_diagnostic()? { wasmparser::ComponentType::Resource { rep, dtor } => { let rep = convert_valtype(rep); let id = types.component_any_type_at(component_type_index).unwrap_resource(); @@ -448,13 +462,13 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_import_section( &mut self, s: wasmparser::ComponentImportSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Processing the import section at this point is relatively simple // which is to simply record the name of the import and the type // information associated with it. - self.validator.component_import_section(&s)?; + self.validator.component_import_section(&s).into_diagnostic()?; for import in s { - let import = import?; + let import = import.into_diagnostic()?; let types = self.validator.types(0).unwrap(); let ty = types.component_entity_type_of_import(import.name.0).unwrap(); self.result.initializers.push(LocalInitializer::Import(import.name, ty)); @@ -465,14 +479,14 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_canonical_section( &mut self, s: wasmparser::ComponentCanonicalSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Entries in the canonical section will get initializers recorded // with the listed options for lifting/lowering. let mut core_func_index = self.validator.types(0).unwrap().function_count(); - self.validator.component_canonical_section(&s)?; + self.validator.component_canonical_section(&s).into_diagnostic()?; for func in s { let types = self.validator.types(0).unwrap(); - let init = match func? { + let init = match func.into_diagnostic()? { wasmparser::CanonicalFunction::Lift { type_index, core_func_index, @@ -529,8 +543,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { range: std::ops::Range, parser: Parser, component: &'data [u8], - diagnostics: &DiagnosticsHandler, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Core wasm modules are parsed inline directly here with the // `ModuleEnvironment` from core wasm compilation. This will return // to the caller the size of the module so it knows how many bytes @@ -539,13 +552,13 @@ impl<'a, 'data> ComponentParser<'a, 'data> { // Note that this is just initial type parsing of the core wasm // module and actual function translation is deferred until this // entire process has completed. - self.validator.module_section(&range)?; + self.validator.module_section(&range).into_diagnostic()?; let parsed_module = ModuleEnvironment::new( self.config, self.validator, self.types.module_types_builder_mut(), ) - .parse(parser, &component[range.start..range.end], diagnostics)?; + .parse(parser, &component[range.start..range.end], &self.session.diagnostics)?; let static_idx = self.static_modules.push(parsed_module); self.result.initializers.push(LocalInitializer::ModuleStatic(static_idx)); // Set a fallback name for the newly added parsed module to be used if @@ -562,7 +575,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { &mut self, range: std::ops::Range, parser: Parser, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // When a sub-component is found then the current parsing state // is pushed onto the `lexical_scopes` stack. This will subsequently // get popped as part of `Payload::End` processing above. @@ -571,7 +584,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { // starts empty since it will only get populated if parsing of // the nested component ends up aliasing some outer module or // component. - self.validator.component_section(&range)?; + self.validator.component_section(&range).into_diagnostic()?; self.lexical_scopes.push(LexicalScope { parser: mem::replace(&mut self.parser, parser), parsed_component: mem::take(&mut self.result), @@ -583,14 +596,14 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn core_instance_section( &mut self, s: wasmparser::InstanceSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Both core wasm instances and component instances record // initializers of what form of instantiation is performed which // largely just records the arguments given from wasmparser into a // `HashMap` for processing later during inlining. - self.validator.instance_section(&s)?; + self.validator.instance_section(&s).into_diagnostic()?; for instance in s { - let init = match instance? { + let init = match instance.into_diagnostic()? { wasmparser::Instance::Instantiate { module_index, args } => { let index = ModuleIndex::from_u32(module_index); instantiate_module(index, &args) @@ -607,11 +620,11 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_instance_section( &mut self, s: wasmparser::ComponentInstanceSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { let mut index = self.validator.types(0).unwrap().component_instance_count(); - self.validator.component_instance_section(&s)?; + self.validator.component_instance_section(&s).into_diagnostic()?; for instance in s { - let init = match instance? { + let init = match instance.into_diagnostic()? { wasmparser::ComponentInstance::Instantiate { component_index, args, @@ -634,15 +647,15 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_export_section( &mut self, s: wasmparser::ComponentExportSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Exports don't actually fill out the `initializers` array but // instead fill out the one other field in a `ParsedComponent`, the // `exports` field (as one might imagine). This for now simply // records the index of what's exported and that's tracked further // later during inlining. - self.validator.component_export_section(&s)?; + self.validator.component_export_section(&s).into_diagnostic()?; for export in s { - let export = export?; + let export = export.into_diagnostic()?; let item = self.kind_to_item(export.kind, export.index)?; let prev = self.result.exports.insert(export.name.0, item); assert!(prev.is_none()); @@ -654,13 +667,13 @@ impl<'a, 'data> ComponentParser<'a, 'data> { fn component_alias_section( &mut self, s: wasmparser::ComponentAliasSectionReader<'data>, - ) -> Result<(), crate::WasmError> { + ) -> WasmResult<()> { // Aliases of instance exports (either core or component) will be // recorded as an initializer of the appropriate type with outer // aliases handled specially via upvars and type processing. - self.validator.component_alias_section(&s)?; + self.validator.component_alias_section(&s).into_diagnostic()?; for alias in s { - let init = match alias? { + let init = match alias.into_diagnostic()? { wasmparser::ComponentAlias::InstanceExport { kind: _, instance_index, @@ -742,9 +755,7 @@ impl<'a, 'data> ComponentParser<'a, 'data> { ComponentItem::Component(index) } wasmparser::ComponentExternalKind::Value => { - return Err(WasmError::Unsupported( - "component values are not supported".to_string(), - )); + unsupported_diag!(&self.session.diagnostics, "component values are not supported"); } wasmparser::ComponentExternalKind::Type => { let types = self.validator.types(0).unwrap(); diff --git a/frontend-wasm/src/component/translator.rs b/frontend-wasm/src/component/translator.rs index af62d16e3..e28ad0580 100644 --- a/frontend-wasm/src/component/translator.rs +++ b/frontend-wasm/src/component/translator.rs @@ -1,9 +1,10 @@ -use miden_diagnostics::DiagnosticsHandler; use midenc_hir::{ - cranelift_entity::PrimaryMap, CanonAbiImport, ComponentBuilder, ComponentExport, FunctionIdent, - FunctionType, Ident, InterfaceFunctionIdent, InterfaceIdent, Symbol, + cranelift_entity::PrimaryMap, diagnostics::Severity, CanonAbiImport, ComponentBuilder, + ComponentExport, FunctionIdent, FunctionType, Ident, InterfaceFunctionIdent, InterfaceIdent, + Symbol, }; use midenc_hir_type::Abi; +use midenc_session::Session; use rustc_hash::FxHashMap; use super::{ @@ -23,7 +24,7 @@ use crate::{ types::{EntityIndex, FuncIndex}, Module, ModuleImport, }, - WasmError, WasmTranslationConfig, + unsupported_diag, WasmTranslationConfig, }; /// A translator from the linearized Wasm component model to the Miden IR component @@ -42,7 +43,7 @@ pub struct ComponentTranslator<'a, 'data> { reallocs: FxHashMap, /// The post return functions used in CanonicalOptions in this component post_returns: FxHashMap, - diagnostics: &'a DiagnosticsHandler, + session: &'a Session, } impl<'a, 'data> ComponentTranslator<'a, 'data> { @@ -50,13 +51,13 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { component_types: ComponentTypes, parsed_modules: PrimaryMap>, config: &'a WasmTranslationConfig, - diagnostics: &'a DiagnosticsHandler, + session: &'a Session, ) -> Self { Self { component_types, parsed_modules, config, - diagnostics, + session, module_instances_source: PrimaryMap::new(), lower_imports: FxHashMap::default(), reallocs: FxHashMap::default(), @@ -70,7 +71,7 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { wasm_translation: LinearComponentTranslation, ) -> WasmResult { let mut component_builder: midenc_hir::ComponentBuilder<'a> = - midenc_hir::ComponentBuilder::new(self.diagnostics); + midenc_hir::ComponentBuilder::new(&self.session.diagnostics); dbg!(&wasm_translation.component.initializers); for initializer in &wasm_translation.component.initializers { match initializer { @@ -89,9 +90,10 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { } GlobalInitializer::ExtractMemory(mem) => { if mem.index.as_u32() > 0 { - return Err(WasmError::Unsupported( - "Only one memory is supported in the component".to_string(), - )); + unsupported_diag!( + &self.session.diagnostics, + "only one memory is supported in the component" + ); } } GlobalInitializer::ExtractRealloc(realloc) => { @@ -103,9 +105,10 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { self.post_returns.insert(post_return.index, func_id); } GlobalInitializer::Resource(_) => { - return Err(WasmError::Unsupported( - "Resource global initializers are not yet supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "resource global initializers are not yet supported" + ); } } } @@ -121,15 +124,16 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { instantiate_module: &InstantiateModule, component_builder: &mut ComponentBuilder<'_>, wasm_translation: &LinearComponentTranslation, - ) -> Result<(), WasmError> { + ) -> WasmResult<()> { match instantiate_module { InstantiateModule::Static(static_module_idx, args) => { if self.module_instances_source.values().any(|idx| *idx == *static_module_idx) { - return Err(WasmError::Unsupported(format!( + unsupported_diag!( + &self.session.diagnostics, "A module with a static index {} is already instantiated. We don't \ support multiple instantiations of the same module.", static_module_idx.as_u32() - ))); + ); } self.module_instances_source.push(*static_module_idx); // TODO: create and init module instance tables @@ -142,9 +146,10 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { module_args.push(self.module_arg_from_export(export)?); } CoreDef::InstanceFlags(_) => { - return Err(WasmError::Unsupported( - "Wasm component instance flags are not supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "Wasm component instance flags are not supported" + ); } CoreDef::Trampoline(trampoline_idx) => { let trampoline = &wasm_translation.trampolines[*trampoline_idx]; @@ -160,21 +165,26 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { } } let module_types = self.component_types.module_types(); - let mut module_state = - ModuleTranslationState::new(module, module_types, module_args); + let mut module_state = ModuleTranslationState::new( + module, + module_types, + module_args, + &self.session.diagnostics, + ); let ir_module = build_ir_module( self.parsed_modules.get_mut(*static_module_idx).unwrap(), module_types, &mut module_state, self.config, - self.diagnostics, + self.session, )?; component_builder.add_module(ir_module.into()).expect("module is already added"); } InstantiateModule::Import(..) => { - return Err(WasmError::Unsupported( - "Imported Wasm core module instantiation is not supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "Imported Wasm core module instantiation is not supported" + ); } }; Ok(()) @@ -188,7 +198,7 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { idx: usize, wasm_component: &LinearComponent, component_builder: &mut ComponentBuilder<'_>, - ) -> Result { + ) -> WasmResult { match trampoline { Trampoline::LowerImport { index, @@ -203,9 +213,11 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { component_builder.add_import(function_id, component_import.clone()); Ok(ModuleArgument::ComponentImport(component_import)) } - _ => Err(WasmError::Unsupported(format!( - "Not yet implemented trampoline type {trampoline:?}" - ))), + _ => unsupported_diag!( + &self.session.diagnostics, + "Not yet implemented trampoline type {:?}", + trampoline + ), } } @@ -232,13 +244,15 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { EntityIndex::Memory(_) => { unreachable!("Attempt to export memory from a module instance. ") } - EntityIndex::Global(_) => Err(WasmError::Unsupported( - "Exporting of core module globals are not yet supported".to_string(), - )), + EntityIndex::Global(_) => unsupported_diag!( + &self.session.diagnostics, + "Exporting of core module globals are not yet supported" + ), }, - ExportItem::Name(_) => Err(WasmError::Unsupported( - "Named core module exports are not yet supported".to_string(), - )), + ExportItem::Name(_) => unsupported_diag!( + &self.session.diagnostics, + "Named core module exports are not yet supported" + ), } } @@ -252,9 +266,7 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { ) -> WasmResult { let (import_idx, import_names) = &wasm_component.imports[runtime_import_index]; if import_names.len() != 1 { - return Err(crate::WasmError::Unsupported( - "multi-name imports not supported".to_string(), - )); + unsupported_diag!(&self.session.diagnostics, "multi-name imports not supported"); } let import_func_name = import_names.first().unwrap(); let (full_interface_name, _) = wasm_component.import_types[*import_idx].clone(); @@ -263,10 +275,15 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { function: Symbol::intern(import_func_name), }; let Some(import_metadata) = self.config.import_metadata.get(&interface_function) else { - return Err(crate::WasmError::MissingImportMetadata(format!( - "Import metadata for interface function {:?} not found", - &interface_function, - ))); + return Err(self + .session + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!( + "wasm error: import metadata for interface function {interface_function:?} \ + not found" + )) + .into_report()); }; let lifted_func_ty = convert_lifted_func_ty(&signature, &self.component_types); @@ -301,11 +318,15 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { Ok(()) } Export::ModuleStatic(_) => { - Err(WasmError::Unsupported("Static module exports are not supported".to_string())) + unsupported_diag!( + &self.session.diagnostics, + "Static module exports are not supported" + ); } - Export::ModuleImport(_) => Err(WasmError::Unsupported( - "Exporting of an imported module is not supported".to_string(), - )), + Export::ModuleImport(_) => unsupported_diag!( + &self.session.diagnostics, + "Exporting of an imported module is not supported" + ), Export::Type(_) => { // Besides the function exports the individual type are also exported from the // component We can ignore them for now @@ -342,15 +363,18 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { ExportItem::Index(idx) => match idx { EntityIndex::Function(func_idx) => module.func_name(func_idx), EntityIndex::Table(_) | EntityIndex::Memory(_) | EntityIndex::Global(_) => { - return Err(WasmError::Unsupported(format!( - "Exporting of non-function entity {core_export:?} is not supported" - ))); + unsupported_diag!( + &self.session.diagnostics, + "Exporting of non-function entity {:?} is not supported", + core_export + ); } }, ExportItem::Name(_) => { - return Err(WasmError::Unsupported( - "Named exports are not yet supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "Named exports are not yet supported" + ); } }; @@ -360,14 +384,16 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { } } CoreDef::InstanceFlags(_) => { - return Err(WasmError::Unsupported( - "Component instance flags exports are not supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "Component instance flags exports are not supported" + ); } CoreDef::Trampoline(_) => { - return Err(WasmError::Unsupported( - "Trampoline core module exports are not supported".to_string(), - )) + unsupported_diag!( + &self.session.diagnostics, + "Trampoline core module exports are not supported" + ); } }) } @@ -377,10 +403,10 @@ impl<'a, 'data> ComponentTranslator<'a, 'data> { options: &CanonicalOptions, ) -> WasmResult { if options.string_encoding != StringEncoding::Utf8 { - return Err(WasmError::Unsupported( + unsupported_diag!( + &self.session.diagnostics, "UTF-8 is expected in CanonicalOptions, string transcoding is not yet supported" - .to_string(), - )); + ); } Ok(midenc_hir::CanonicalOptions { realloc: options.realloc.map(|idx| self.reallocs[&idx]), diff --git a/frontend-wasm/src/component/types/mod.rs b/frontend-wasm/src/component/types/mod.rs index 4531e92cf..008608e5a 100644 --- a/frontend-wasm/src/component/types/mod.rs +++ b/frontend-wasm/src/component/types/mod.rs @@ -9,10 +9,9 @@ pub mod resources; use core::{hash::Hash, ops::Index}; use anyhow::{bail, Result}; -use indexmap::IndexSet; use midenc_hir::cranelift_entity::{EntityRef, PrimaryMap}; use rustc_hash::FxHashMap; -use wasmparser::{names::KebabString, types}; +use wasmparser::{collections::IndexSet, names::KebabString, types}; use self::resources::ResourcesBuilder; use crate::{ @@ -1030,8 +1029,8 @@ impl From<&wasmparser::PrimitiveValType> for InterfaceType { wasmparser::PrimitiveValType::U32 => InterfaceType::U32, wasmparser::PrimitiveValType::S64 => InterfaceType::S64, wasmparser::PrimitiveValType::U64 => InterfaceType::U64, - wasmparser::PrimitiveValType::Float32 => InterfaceType::Float32, - wasmparser::PrimitiveValType::Float64 => InterfaceType::Float64, + wasmparser::PrimitiveValType::F32 => InterfaceType::Float32, + wasmparser::PrimitiveValType::F64 => InterfaceType::Float64, wasmparser::PrimitiveValType::Char => InterfaceType::Char, wasmparser::PrimitiveValType::String => InterfaceType::String, } diff --git a/frontend-wasm/src/config.rs b/frontend-wasm/src/config.rs index bb3927b70..62e6c171a 100644 --- a/frontend-wasm/src/config.rs +++ b/frontend-wasm/src/config.rs @@ -1,4 +1,4 @@ -use alloc::{borrow::Cow, collections::BTreeMap}; +use alloc::{borrow::Cow, collections::BTreeMap, fmt}; use miden_core::crypto::hash::RpoDigest; use midenc_hir::InterfaceFunctionIdent; @@ -6,11 +6,16 @@ use midenc_hir::InterfaceFunctionIdent; /// Represents Miden VM codegen metadata for a function import. /// This struct will have more fields in the future e.g. where the function /// for this MAST hash is located (to be loaded by the VM) -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ImportMetadata { /// The MAST root hash of the function to be used in codegen pub digest: RpoDigest, } +impl fmt::Debug for ImportMetadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entry(&"digest", &self.digest.to_hex()).finish() + } +} /// Configuration for the WASM translation. #[derive(Debug)] @@ -41,7 +46,7 @@ impl Default for WasmTranslationConfig { source_name: Cow::Borrowed("noname"), override_name: None, generate_native_debuginfo: false, - parse_wasm_debuginfo: false, + parse_wasm_debuginfo: true, import_metadata: Default::default(), } } diff --git a/frontend-wasm/src/error.rs b/frontend-wasm/src/error.rs index fdefa99d2..a42b399cf 100644 --- a/frontend-wasm/src/error.rs +++ b/frontend-wasm/src/error.rs @@ -1,5 +1,7 @@ -use miden_diagnostics::{Diagnostic, ToDiagnostic}; -use midenc_hir::SymbolConflictError; +use midenc_hir::{ + diagnostics::{miette, Diagnostic, Report}, + SymbolConflictError, +}; use thiserror::Error; /// A WebAssembly translation error. @@ -7,13 +9,14 @@ use thiserror::Error; /// When a WebAssembly function can't be translated, one of these error codes will be returned /// to describe the failure. #[allow(missing_docs)] -#[derive(Error, Debug)] +#[derive(Error, Debug, Diagnostic)] pub enum WasmError { /// The input WebAssembly code is invalid. /// /// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly /// code. This should never happen for validated WebAssembly code. - #[error("Invalid input WebAssembly code at offset {offset}: {message}")] + #[error("invalid input WebAssembly code at offset {offset}: {message}")] + #[diagnostic()] InvalidWebAssembly { /// A string describing the validation error. message: String, @@ -22,34 +25,33 @@ pub enum WasmError { }, /// A feature used by the WebAssembly code is not supported by the Miden IR. - #[error("Unsupported Wasm: {0}")] + #[error("unsupported WebAssembly code: {0}")] + #[diagnostic()] Unsupported(String), /// Too many functions were declared in a module #[error("Too many declared functions in the module")] + #[diagnostic()] FuncNumLimitExceeded, /// Duplicate symbol names were found in a module - #[error("{0}")] + #[error(transparent)] + #[diagnostic(transparent)] SymbolConflictError(#[from] SymbolConflictError), - /// Unable to translate function to HIR - #[error("Failed to build function. See diagnostics for details")] - InvalidFunctionError, - - /// An unknown error occurred - #[error("Unexpected: {0}")] - Unexpected(String), - - /// An error occurred during IR program linking - #[error("Failed to link module. See diagnostics for details")] - LinkerError(#[from] midenc_hir::LinkerError), - - #[error("Import metadata is missing: {0}")] + #[error("import metadata is missing: {0}")] + #[diagnostic()] MissingImportMetadata(String), - #[error("Export metadata is missing: {0}")] + #[error("export metadata is missing: {0}")] + #[diagnostic()] MissingExportMetadata(String), + + #[error(transparent)] + DwarfError(#[from] gimli::Error), + + #[error(transparent)] + Io(#[from] std::io::Error), } impl From for WasmError { @@ -61,25 +63,17 @@ impl From for WasmError { } } -impl ToDiagnostic for WasmError { - fn to_diagnostic(self) -> Diagnostic { - Diagnostic::error().with_message(self.to_string()) - } -} - /// A convenient alias for a `Result` that uses `WasmError` as the error type. -pub type WasmResult = Result; +pub type WasmResult = Result; /// Emit diagnostics and return an `Err(WasmError::Unsupported(msg))` where `msg` the string built /// by calling `format!` on the arguments to this macro. #[macro_export] macro_rules! unsupported_diag { - ($diagnostics:expr, $($arg:tt)*) => { - let message = format!($($arg)*); - $diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message(message.clone()) - .emit(); - return Err($crate::error::WasmError::Unsupported(message)); - } + ($diagnostics:expr, $($arg:tt)*) => {{ + return Err($diagnostics + .diagnostic(Severity::Error) + .with_message(format!($($arg)*)) + .into_report()); + }} } diff --git a/frontend-wasm/src/lib.rs b/frontend-wasm/src/lib.rs index d0f87f1ee..58c311f64 100644 --- a/frontend-wasm/src/lib.rs +++ b/frontend-wasm/src/lib.rs @@ -22,8 +22,9 @@ mod test_utils; use component::build_ir::translate_component; use error::WasmResult; -use miden_diagnostics::DiagnosticsHandler; +use midenc_session::Session; use module::build_ir::translate_module_as_component; +use wasmparser::WasmFeatures; pub use self::{config::*, error::WasmError}; @@ -32,13 +33,31 @@ pub use self::{config::*, error::WasmError}; pub fn translate( wasm: &[u8], config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, + session: &Session, ) -> WasmResult { if wasm[4..8] == [0x01, 0x00, 0x00, 0x00] { // Wasm core module // see https://github.com/WebAssembly/component-model/blob/main/design/mvp/Binary.md#component-definitions - translate_module_as_component(wasm, config, diagnostics) + translate_module_as_component(wasm, config, session) } else { - translate_component(wasm, config, diagnostics) + translate_component(wasm, config, session) } } + +/// The set of core WebAssembly features which we need to or wish to support +pub(crate) fn supported_features() -> WasmFeatures { + WasmFeatures::BULK_MEMORY + | WasmFeatures::FLOATS + | WasmFeatures::FUNCTION_REFERENCES + | WasmFeatures::MULTI_VALUE + | WasmFeatures::MUTABLE_GLOBAL + | WasmFeatures::SATURATING_FLOAT_TO_INT + | WasmFeatures::SIGN_EXTENSION + | WasmFeatures::TAIL_CALL +} + +/// The extended set of WebAssembly features which are enabled when working with the Wasm Component +/// Model +pub(crate) fn supported_component_model_features() -> WasmFeatures { + supported_features() | WasmFeatures::COMPONENT_MODEL +} diff --git a/frontend-wasm/src/miden_abi/stdlib/crypto/hashes.rs b/frontend-wasm/src/miden_abi/stdlib/crypto/hashes.rs index 4a1d42fd7..83b1dd781 100644 --- a/frontend-wasm/src/miden_abi/stdlib/crypto/hashes.rs +++ b/frontend-wasm/src/miden_abi/stdlib/crypto/hashes.rs @@ -3,31 +3,28 @@ use midenc_hir_type::Type::*; use crate::miden_abi::{FunctionTypeMap, ModuleFunctionTypeMap}; -pub(crate) const BLAKE3_HASH_1TO1: &str = "blake3_hash_1to1"; -pub(crate) const BLAKE3_HASH_2TO1: &str = "blake3_hash_2to1"; +pub(crate) const BLAKE3_HASH_1TO1: &str = "hash_1to1"; +pub(crate) const BLAKE3_HASH_2TO1: &str = "hash_2to1"; pub(crate) fn signatures() -> ModuleFunctionTypeMap { let mut m: ModuleFunctionTypeMap = Default::default(); - let mut crypto: FunctionTypeMap = Default::default(); - crypto.insert( + let mut blake3: FunctionTypeMap = Default::default(); + blake3.insert( BLAKE3_HASH_1TO1, //Accepts and returns a 8 Felt elements FunctionType::new( - [Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt], - [Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt], + [I32, I32, I32, I32, I32, I32, I32, I32], + [I32, I32, I32, I32, I32, I32, I32, I32], ), ); - crypto.insert( + blake3.insert( BLAKE3_HASH_2TO1, // Accepts 16 and returns a 8 Felt elements FunctionType::new( - [ - Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt, - Felt, Felt, - ], - [Felt, Felt, Felt, Felt, Felt, Felt, Felt, Felt], + [I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32, I32], + [I32, I32, I32, I32, I32, I32, I32, I32], ), ); - m.insert("std::crypto_hashes", crypto); + m.insert("std::crypto::hashes::blake3", blake3); m } diff --git a/frontend-wasm/src/miden_abi/transform.rs b/frontend-wasm/src/miden_abi/transform.rs index 038c841ae..6b67e2dce 100644 --- a/frontend-wasm/src/miden_abi/transform.rs +++ b/frontend-wasm/src/miden_abi/transform.rs @@ -1,5 +1,9 @@ -use miden_diagnostics::DiagnosticsHandler; -use midenc_hir::{FunctionIdent, Immediate, InstBuilder, SourceSpan, Type::*, Value}; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, SourceSpan}, + FunctionIdent, Immediate, InstBuilder, + Type::*, + Value, +}; use super::{stdlib, tx_kernel}; use crate::module::function_builder_ext::FunctionBuilderExt; @@ -15,20 +19,40 @@ enum TransformStrategy { } /// Get the transformation strategy for a function name -fn get_transform_strategy(function_id: &str) -> TransformStrategy { - match function_id { - tx_kernel::note::GET_INPUTS => TransformStrategy::ListReturn, - tx_kernel::account::ADD_ASSET => TransformStrategy::ReturnViaPointer, - tx_kernel::account::REMOVE_ASSET => TransformStrategy::ReturnViaPointer, - tx_kernel::account::GET_ID => TransformStrategy::NoTransform, - tx_kernel::tx::CREATE_NOTE => TransformStrategy::NoTransform, - stdlib::crypto::hashes::BLAKE3_HASH_1TO1 => TransformStrategy::ReturnViaPointer, - stdlib::crypto::hashes::BLAKE3_HASH_2TO1 => TransformStrategy::ReturnViaPointer, - stdlib::crypto::dsa::RPO_FALCON512_VERIFY => TransformStrategy::NoTransform, - stdlib::mem::PIPE_WORDS_TO_MEMORY => TransformStrategy::ReturnViaPointer, - stdlib::mem::PIPE_DOUBLE_WORDS_TO_MEMORY => TransformStrategy::ReturnViaPointer, - _ => panic!("No transform strategy found for function {}", function_id), +fn get_transform_strategy(module_id: &str, function_id: &str) -> TransformStrategy { + #[allow(clippy::single_match)] + match module_id { + "std::mem" => match function_id { + stdlib::mem::PIPE_WORDS_TO_MEMORY => return TransformStrategy::ReturnViaPointer, + stdlib::mem::PIPE_DOUBLE_WORDS_TO_MEMORY => return TransformStrategy::ReturnViaPointer, + _ => (), + }, + "std::crypto::hashes::blake3" => match function_id { + stdlib::crypto::hashes::BLAKE3_HASH_1TO1 => return TransformStrategy::ReturnViaPointer, + stdlib::crypto::hashes::BLAKE3_HASH_2TO1 => return TransformStrategy::ReturnViaPointer, + _ => (), + }, + "std::crypto::dsa::rpo_falcon512" => match function_id { + stdlib::crypto::dsa::RPO_FALCON512_VERIFY => return TransformStrategy::NoTransform, + _ => (), + }, + "miden::note" => match function_id { + tx_kernel::note::GET_INPUTS => return TransformStrategy::ListReturn, + _ => (), + }, + "miden::account" => match function_id { + tx_kernel::account::ADD_ASSET => return TransformStrategy::ReturnViaPointer, + tx_kernel::account::REMOVE_ASSET => return TransformStrategy::ReturnViaPointer, + tx_kernel::account::GET_ID => return TransformStrategy::NoTransform, + _ => (), + }, + "miden::tx" => match function_id { + tx_kernel::tx::CREATE_NOTE => return TransformStrategy::NoTransform, + _ => (), + }, + _ => (), } + panic!("No transform strategy found for function '{function_id}' in module '{module_id}'"); } /// Transform a function call based on the transformation strategy @@ -40,7 +64,7 @@ pub fn transform_miden_abi_call( diagnostics: &DiagnosticsHandler, ) -> Vec { use TransformStrategy::*; - match get_transform_strategy(func_id.function.as_symbol().as_str()) { + match get_transform_strategy(func_id.module.as_str(), func_id.function.as_str()) { ListReturn => list_return(func_id, args, builder, span, diagnostics), ReturnViaPointer => return_via_pointer(func_id, args, builder, span, diagnostics), NoTransform => no_transform(func_id, args, builder, span, diagnostics), diff --git a/frontend-wasm/src/module/build_ir.rs b/frontend-wasm/src/module/build_ir.rs index 35f10817c..61a741722 100644 --- a/frontend-wasm/src/module/build_ir.rs +++ b/frontend-wasm/src/module/build_ir.rs @@ -1,8 +1,11 @@ use core::mem; -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use midenc_hir::{CallConv, ConstantData, Linkage, MidenAbiImport, ModuleBuilder, Symbol}; -use wasmparser::{Validator, WasmFeatures}; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, IntoDiagnostic, Severity, SourceSpan}, + CallConv, ConstantData, Linkage, MidenAbiImport, ModuleBuilder, Symbol, +}; +use midenc_session::Session; +use wasmparser::Validator; use super::{module_translation_state::ModuleTranslationState, Module}; use crate::{ @@ -14,7 +17,7 @@ use crate::{ module_env::{FunctionBodyData, ModuleEnvironment, ParsedModule}, types::{ir_func_sig, ir_func_type, ir_type, ModuleTypes}, }, - WasmError, WasmTranslationConfig, + WasmTranslationConfig, }; /// Translate a valid Wasm core module binary into Miden IR component building @@ -26,10 +29,9 @@ use crate::{ pub fn translate_module_as_component( wasm: &[u8], config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, + session: &Session, ) -> WasmResult { - let wasm_features = WasmFeatures::default(); - let mut validator = Validator::new_with_features(wasm_features); + let mut validator = Validator::new_with_features(crate::supported_features()); let parser = wasmparser::Parser::new(0); let mut module_types_builder = Default::default(); let mut parsed_module = ModuleEnvironment::new( @@ -37,18 +39,22 @@ pub fn translate_module_as_component( &mut validator, &mut module_types_builder, ) - .parse(parser, wasm, diagnostics)?; + .parse(parser, wasm, &session.diagnostics)?; parsed_module.module.set_name_fallback(config.source_name.clone()); if let Some(name_override) = config.override_name.as_ref() { parsed_module.module.set_name_override(name_override.clone()); } let module_types = module_types_builder.finish(); - let mut module_state = - ModuleTranslationState::new(&parsed_module.module, &module_types, vec![]); + let mut module_state = ModuleTranslationState::new( + &parsed_module.module, + &module_types, + vec![], + &session.diagnostics, + ); let module = - build_ir_module(&mut parsed_module, &module_types, &mut module_state, config, diagnostics)?; - let mut cb = midenc_hir::ComponentBuilder::new(diagnostics); + build_ir_module(&mut parsed_module, &module_types, &mut module_state, config, session)?; + let mut cb = midenc_hir::ComponentBuilder::new(&session.diagnostics); let module_imports = module.imports(); for import_module_id in module_imports.iter_module_names() { if let Some(imports) = module_imports.imported(import_module_id) { @@ -82,12 +88,29 @@ pub fn build_ir_module( module_types: &ModuleTypes, module_state: &mut ModuleTranslationState, _config: &WasmTranslationConfig, - diagnostics: &DiagnosticsHandler, + session: &Session, ) -> WasmResult { let name = parsed_module.module.name(); let mut module_builder = ModuleBuilder::new(name.as_str()); - build_globals(&parsed_module.module, &mut module_builder, diagnostics)?; - build_data_segments(parsed_module, &mut module_builder, diagnostics)?; + build_globals(&parsed_module.module, &mut module_builder, &session.diagnostics)?; + build_data_segments(parsed_module, &mut module_builder, &session.diagnostics)?; + let addr2line = addr2line::Context::from_dwarf(gimli::Dwarf { + debug_abbrev: parsed_module.debuginfo.dwarf.debug_abbrev, + debug_addr: parsed_module.debuginfo.dwarf.debug_addr, + debug_aranges: parsed_module.debuginfo.dwarf.debug_aranges, + debug_info: parsed_module.debuginfo.dwarf.debug_info, + debug_line: parsed_module.debuginfo.dwarf.debug_line, + debug_line_str: parsed_module.debuginfo.dwarf.debug_line_str, + debug_str: parsed_module.debuginfo.dwarf.debug_str, + debug_str_offsets: parsed_module.debuginfo.dwarf.debug_str_offsets, + debug_types: parsed_module.debuginfo.dwarf.debug_types, + locations: parsed_module.debuginfo.dwarf.locations, + ranges: parsed_module.debuginfo.dwarf.ranges, + file_type: parsed_module.debuginfo.dwarf.file_type, + sup: parsed_module.debuginfo.dwarf.sup.clone(), + ..Default::default() + }) + .into_diagnostic()?; let mut func_translator = FuncTranslator::new(); // Although this renders this parsed module invalid(without functiong // bodies), we don't support multiple module instances. Thus, this @@ -98,7 +121,7 @@ pub fn build_ir_module( let func_type = &parsed_module.module.functions[*func_index]; let func_name = &parsed_module.module.func_name(*func_index); let wasm_func_type = module_types[func_type.signature].clone(); - let ir_func_type = ir_func_type(&wasm_func_type)?; + let ir_func_type = ir_func_type(&wasm_func_type, &session.diagnostics)?; let sig = ir_func_sig(&ir_func_type, CallConv::SystemV, Linkage::External); let mut module_func_builder = module_builder.function(func_name.as_str(), sig.clone())?; let FunctionBodyData { validator, body } = body_data; @@ -107,14 +130,13 @@ pub fn build_ir_module( &body, &mut module_func_builder, module_state, - &parsed_module.module, + parsed_module, module_types, - diagnostics, + &addr2line, + session, &mut func_validator, )?; - module_func_builder - .build(diagnostics) - .map_err(|_| WasmError::InvalidFunctionError)?; + module_func_builder.build(&session.diagnostics)?; } let module = module_builder.build(); Ok(*module) @@ -124,7 +146,7 @@ fn build_globals( wasm_module: &Module, module_builder: &mut ModuleBuilder, diagnostics: &DiagnosticsHandler, -) -> Result<(), WasmError> { +) -> WasmResult<()> { for (global_idx, global) in &wasm_module.globals { let global_name = wasm_module .name_section @@ -136,7 +158,7 @@ fn build_globals( let init = ConstantData::from(global_init.to_le_bytes(wasm_module, diagnostics)?); if let Err(e) = module_builder.declare_global_variable( global_name.as_str(), - ir_type(global.ty)?, + ir_type(global.ty, diagnostics)?, Linkage::External, Some(init.clone()), SourceSpan::default(), @@ -146,11 +168,10 @@ fn build_globals( error: {:?}", e ); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) + return Err(diagnostics + .diagnostic(Severity::Error) .with_message(message.clone()) - .emit(); - return Err(WasmError::Unexpected(message)); + .into_report()); } } Ok(()) @@ -160,7 +181,7 @@ fn build_data_segments( translation: &ParsedModule, module_builder: &mut ModuleBuilder, diagnostics: &DiagnosticsHandler, -) -> Result<(), WasmError> { +) -> WasmResult<()> { for (data_segment_idx, data_segment) in &translation.data_segments { let data_segment_name = translation.module.name_section.data_segment_names[&data_segment_idx]; @@ -174,11 +195,10 @@ fn build_data_segments( '{offset}' with error: {:?}", e ); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) + return Err(diagnostics + .diagnostic(Severity::Error) .with_message(message.clone()) - .emit(); - return Err(WasmError::Unexpected(message)); + .into_report()); } } Ok(()) diff --git a/frontend-wasm/src/module/func_translation_state.rs b/frontend-wasm/src/module/func_translation_state.rs index ea066a92a..1ad78bdab 100644 --- a/frontend-wasm/src/module/func_translation_state.rs +++ b/frontend-wasm/src/module/func_translation_state.rs @@ -5,8 +5,7 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 -use miden_diagnostics::SourceSpan; -use midenc_hir::{Block, Inst, InstBuilder, Signature, Value}; +use midenc_hir::{diagnostics::SourceSpan, Block, Inst, InstBuilder, Signature, Value}; use midenc_hir_type::Type; use super::function_builder_ext::FunctionBuilderExt; diff --git a/frontend-wasm/src/module/func_translator.rs b/frontend-wasm/src/module/func_translator.rs index 752281730..1f9177f38 100644 --- a/frontend-wasm/src/module/func_translator.rs +++ b/frontend-wasm/src/module/func_translator.rs @@ -6,17 +6,22 @@ //! //! Based on Cranelift's Wasm -> CLIF translator v11.0.0 -use miden_diagnostics::{DiagnosticsHandler, SourceSpan}; -use midenc_hir::{cranelift_entity::EntityRef, Block, InstBuilder, ModuleFunctionBuilder}; -use wasmparser::{BinaryReader, FuncValidator, FunctionBody, WasmModuleResources}; +use midenc_hir::{ + cranelift_entity::EntityRef, + diagnostics::{DiagnosticsHandler, IntoDiagnostic, SourceManagerExt, SourceSpan}, + Block, InstBuilder, ModuleFunctionBuilder, +}; +use midenc_session::Session; +use wasmparser::{FuncValidator, FunctionBody, WasmModuleResources}; -use super::{module_translation_state::ModuleTranslationState, Module}; +use super::{module_env::ParsedModule, module_translation_state::ModuleTranslationState}; use crate::{ code_translator::translate_operator, error::WasmResult, module::{ func_translation_state::FuncTranslationState, function_builder_ext::{FunctionBuilderContext, FunctionBuilderExt}, + module_env::DwarfReader, types::{convert_valtype, ir_type, ModuleTypes}, }, ssa::Variable, @@ -49,13 +54,12 @@ impl FuncTranslator { body: &FunctionBody<'_>, mod_func_builder: &mut ModuleFunctionBuilder, module_state: &mut ModuleTranslationState, - module: &Module, + module: &ParsedModule<'_>, mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, + addr2line: &addr2line::Context>, + session: &Session, func_validator: &mut FuncValidator, ) -> WasmResult<()> { - let mut reader = body.get_binary_reader(); - let mut builder = FunctionBuilderExt::new(mod_func_builder, &mut self.func_ctx); let entry_block = builder.current_block(); builder.seal_block(entry_block); // Declare all predecessors known. @@ -68,15 +72,26 @@ impl FuncTranslator { builder.append_block_params_for_function_returns(exit_block); self.state.initialize(builder.signature(), exit_block); - parse_local_decls(&mut reader, &mut builder, num_params, func_validator)?; + let mut reader = body.get_locals_reader().into_diagnostic()?; + + parse_local_decls( + &mut reader, + &mut builder, + num_params, + func_validator, + &session.diagnostics, + )?; + + let mut reader = body.get_operators_reader().into_diagnostic()?; parse_function_body( - reader, + &mut reader, &mut builder, &mut self.state, module_state, module, mod_types, - diagnostics, + addr2line, + session, func_validator, )?; @@ -107,20 +122,20 @@ fn declare_parameters(builder: &mut FunctionBuilderExt, entry_block: Block) -> u /// /// Declare local variables, starting from `num_params`. fn parse_local_decls( - reader: &mut BinaryReader, + reader: &mut wasmparser::LocalsReader<'_>, builder: &mut FunctionBuilderExt, num_params: usize, validator: &mut FuncValidator, + diagnostics: &DiagnosticsHandler, ) -> WasmResult<()> { let mut next_local = num_params; - let local_count = reader.read_var_u32()?; + let local_count = reader.get_count(); for _ in 0..local_count { let pos = reader.original_position(); - let count = reader.read_var_u32()?; - let ty = reader.read()?; - validator.define_locals(pos, count, ty)?; - declare_locals(builder, count, ty, &mut next_local)?; + let (count, ty) = reader.read().into_diagnostic()?; + validator.define_locals(pos, count, ty).into_diagnostic()?; + declare_locals(builder, count, ty, &mut next_local, diagnostics)?; } Ok(()) @@ -134,10 +149,11 @@ fn declare_locals( count: u32, wasm_type: wasmparser::ValType, next_local: &mut usize, + diagnostics: &DiagnosticsHandler, ) -> WasmResult<()> { - let ty = ir_type(convert_valtype(wasm_type))?; + let ty = ir_type(convert_valtype(wasm_type), diagnostics)?; // All locals are initialized to 0. - let init = emit_zero(&ty, builder)?; + let init = emit_zero(&ty, builder, diagnostics)?; for _ in 0..count { let local = Variable::new(*next_local); builder.declare_var(local, ty.clone()); @@ -153,35 +169,71 @@ fn declare_locals( /// arguments and locals are declared in the builder. #[allow(clippy::too_many_arguments)] fn parse_function_body( - mut reader: BinaryReader, + reader: &mut wasmparser::OperatorsReader<'_>, builder: &mut FunctionBuilderExt, state: &mut FuncTranslationState, module_state: &mut ModuleTranslationState, - module: &Module, + module: &ParsedModule<'_>, mod_types: &ModuleTypes, - diagnostics: &DiagnosticsHandler, + addr2line: &addr2line::Context>, + session: &Session, func_validator: &mut FuncValidator, ) -> WasmResult<()> { // The control stack is initialized with a single block representing the whole function. debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); + let mut end_span = SourceSpan::default(); while !reader.eof() { let pos = reader.original_position(); - let op = reader.read_operator()?; - func_validator.op(pos, &op)?; + let (op, offset) = reader.read_with_offset().into_diagnostic()?; + func_validator.op(pos, &op).into_diagnostic()?; + + let offset = (offset as u64) + .checked_sub(module.wasm_file.code_section_offset) + .expect("offset occurs before start of code section"); + let mut span = SourceSpan::default(); + if let Some(loc) = addr2line.find_location(offset).into_diagnostic()? { + if let Some(file) = loc.file { + let path = std::path::Path::new(file); + let path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + if path.exists() { + let source_file = session.source_manager.load_file(&path).into_diagnostic()?; + let line = loc.line.and_then(|line| line.checked_sub(1)).unwrap_or(0); + let column = loc.column.and_then(|col| col.checked_sub(1)).unwrap_or(0); + span = source_file.line_column_to_span(line, column).unwrap_or_default(); + } else { + log::debug!( + "failed to locate span for instruction at offset {offset} in function {}", + builder.id() + ); + } + } + } else { + log::debug!( + "failed to locate span for instruction at offset {offset} in function {}", + builder.id() + ); + } + + // Track the span of every END we observe, so we have a span to assign to the return we + // place in the final exit block + if let wasmparser::Operator::End = op { + end_span = span; + } + translate_operator( &op, builder, state, module_state, - module, + &module.module, mod_types, - diagnostics, - SourceSpan::default(), + &session.diagnostics, + span, )?; } let pos = reader.original_position(); - func_validator.finish(pos)?; + func_validator.finish(pos).into_diagnostic()?; // The final `End` operator left us in the exit block where we need to manually add a return // instruction. @@ -189,7 +241,7 @@ fn parse_function_body( // If the exit block is unreachable, it may not have the correct arguments, so we would // generate a return instruction that doesn't match the signature. if state.reachable && !builder.is_unreachable() { - builder.ins().ret(state.stack.first().cloned(), SourceSpan::default()); + builder.ins().ret(state.stack.first().cloned(), end_span); } // Discard any remaining values on the stack. Either we just returned them, diff --git a/frontend-wasm/src/module/function_builder_ext.rs b/frontend-wasm/src/module/function_builder_ext.rs index 0e548312e..f5e41e4cd 100644 --- a/frontend-wasm/src/module/function_builder_ext.rs +++ b/frontend-wasm/src/module/function_builder_ext.rs @@ -1,6 +1,6 @@ -use miden_diagnostics::SourceSpan; use midenc_hir::{ cranelift_entity::{EntitySet, SecondaryMap}, + diagnostics::SourceSpan, Block, Br, CondBr, DataFlowGraph, InsertionPoint, Inst, InstBuilderBase, Instruction, ModuleFunctionBuilder, ProgramPoint, Switch, Value, }; @@ -70,6 +70,10 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { self.inner.data_flow_graph_mut() } + pub fn id(&self) -> midenc_hir::FunctionIdent { + self.inner.id() + } + pub fn signature(&self) -> &midenc_hir::Signature { self.inner.signature() } @@ -341,22 +345,21 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { /// other jump instructions. pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) { self.func_ctx.ssa.remove_block_predecessor(old_block, inst); - match self.data_flow_graph_mut().insts[inst].data.item { + match &mut *self.data_flow_graph_mut().insts[inst].data { Instruction::Br(Br { - ref mut destination, - .. - }) if destination == &old_block => { - *destination = new_block; + ref mut successor, .. + }) if successor.destination == old_block => { + successor.destination = new_block; } Instruction::CondBr(CondBr { - then_dest: (ref mut then_dest, _), - else_dest: (ref mut else_dest, _), + ref mut then_dest, + ref mut else_dest, .. }) => { - if then_dest == &old_block { - *then_dest = new_block; - } else if else_dest == &old_block { - *else_dest = new_block; + if then_dest.destination == old_block { + then_dest.destination = new_block; + } else if else_dest.destination == old_block { + else_dest.destination = new_block; } } Instruction::Switch(Switch { @@ -365,13 +368,13 @@ impl<'a, 'b, 'c> FunctionBuilderExt<'a, 'b, 'c> { ref mut arms, ref mut default, }) => { - for (_, ref mut dest_block) in arms { - if dest_block == &old_block { - *dest_block = new_block; + for arm in arms.iter_mut() { + if arm.successor.destination == old_block { + arm.successor.destination = new_block; } } - if default == &old_block { - *default = new_block; + if default.destination == old_block { + default.destination = new_block; } } _ => panic!("{} must be a branch instruction", inst), @@ -445,38 +448,45 @@ impl<'a, 'b, 'c, 'd> InstBuilderBase<'a> for FuncInstBuilderExt<'a, 'b, 'c, 'd> let opcode = data.opcode(); let inst = self.builder.data_flow_graph_mut().insert_inst(self.ip, data, ty, span); - match &self.builder.inner.data_flow_graph().insts[inst].data.item { - Instruction::Br(Br { destination, .. }) => { + match self.builder.inner.data_flow_graph().insts[inst].data.inner() { + Instruction::Br(Br { successor, .. }) => { // If the user has supplied jump arguments we must adapt the arguments of // the destination block - self.builder.func_ctx.ssa.declare_block_predecessor(*destination, inst); + self.builder.func_ctx.ssa.declare_block_predecessor(successor.destination, inst); } Instruction::CondBr(CondBr { - then_dest: (block_then, _), - else_dest: (block_else, _), + then_dest, + else_dest, .. }) => { - self.builder.func_ctx.ssa.declare_block_predecessor(*block_then, inst); - if block_then != block_else { - self.builder.func_ctx.ssa.declare_block_predecessor(*block_else, inst); + self.builder.func_ctx.ssa.declare_block_predecessor(then_dest.destination, inst); + if then_dest.destination != else_dest.destination { + self.builder + .func_ctx + .ssa + .declare_block_predecessor(else_dest.destination, inst); } } Instruction::Switch(Switch { op: _, arg: _, ref arms, - default: _, + default: default_successor, }) => { // Unlike all other jumps/branches, arms are // capable of having the same successor appear // multiple times, so we must deduplicate. let mut unique = EntitySet::::new(); - for (_, dest_block) in arms { - if !unique.insert(*dest_block) { + let blocks = arms + .iter() + .map(|arm| arm.successor.destination) + .chain([default_successor.destination]); + for block in blocks { + if !unique.insert(block) { continue; } - self.builder.func_ctx.ssa.declare_block_predecessor(*dest_block, inst); + self.builder.func_ctx.ssa.declare_block_predecessor(block, inst); } } inst => debug_assert!(!inst.opcode().is_branch()), diff --git a/frontend-wasm/src/module/mod.rs b/frontend-wasm/src/module/mod.rs index 89eeee7f5..04bcb621f 100644 --- a/frontend-wasm/src/module/mod.rs +++ b/frontend-wasm/src/module/mod.rs @@ -6,9 +6,9 @@ use std::{borrow::Cow, collections::BTreeMap, ops::Range}; use indexmap::IndexMap; -use miden_diagnostics::DiagnosticsHandler; use midenc_hir::{ cranelift_entity::{packed_option::ReservedValue, EntityRef, PrimaryMap}, + diagnostics::{DiagnosticsHandler, Severity}, Ident, Symbol, }; use rustc_hash::FxHashMap; @@ -179,11 +179,6 @@ pub struct ModuleImport { } impl Module { - /// Allocates the module data structures. - pub fn new() -> Self { - Module::default() - } - /// Convert a `DefinedFuncIndex` into a `FuncIndex`. #[inline] pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { diff --git a/frontend-wasm/src/module/module_env.rs b/frontend-wasm/src/module/module_env.rs index f71a13b0b..465d60786 100644 --- a/frontend-wasm/src/module/module_env.rs +++ b/frontend-wasm/src/module/module_env.rs @@ -1,14 +1,14 @@ use std::{ops::Range, path::PathBuf, sync::Arc}; -use miden_diagnostics::DiagnosticsHandler; use midenc_hir::{ cranelift_entity::{packed_option::ReservedValue, PrimaryMap}, + diagnostics::{DiagnosticsHandler, IntoDiagnostic, Report, Severity}, Ident, Symbol, }; use wasmparser::{ - types::CoreTypeId, CompositeType, CustomSectionReader, DataKind, ElementItems, ElementKind, - Encoding, ExternalKind, FuncToValidate, FunctionBody, NameSectionReader, Naming, Operator, - Parser, Payload, TypeRef, Validator, ValidatorResources, + types::CoreTypeId, CustomSectionReader, DataKind, ElementItems, ElementKind, Encoding, + ExternalKind, FuncToValidate, FunctionBody, NameSectionReader, Naming, Operator, Parser, + Payload, TypeRef, Validator, ValidatorResources, }; use super::{ @@ -27,7 +27,7 @@ use crate::{ }, FuncRefIndex, Module, ModuleType, TableSegment, }, - unsupported_diag, WasmError, WasmTranslationConfig, + unsupported_diag, WasmTranslationConfig, }; /// Object containing the standalone environment information. @@ -60,6 +60,9 @@ pub struct ParsedModule<'data> { /// module, or those that can possibly be called. pub exported_signatures: Vec, + /// Metadata about the source Wasm file + pub wasm_file: WasmFileInfo, + /// DWARF debug information, if enabled, parsed from the module. pub debuginfo: DebugInfoData<'data>, @@ -83,19 +86,18 @@ pub struct FunctionBodyData<'a> { pub validator: FuncToValidate, } -#[derive(Debug, Default)] +#[derive(Default)] pub struct DebugInfoData<'a> { pub dwarf: Dwarf<'a>, - pub wasm_file: WasmFileInfo, - debug_loc: gimli::DebugLoc>, - debug_loclists: gimli::DebugLocLists>, - pub debug_ranges: gimli::DebugRanges>, - pub debug_rnglists: gimli::DebugRngLists>, + debug_loc: gimli::DebugLoc>, + debug_loclists: gimli::DebugLocLists>, + pub debug_ranges: gimli::DebugRanges>, + pub debug_rnglists: gimli::DebugRngLists>, } -pub type Dwarf<'input> = gimli::Dwarf>; +pub type Dwarf<'input> = gimli::Dwarf>; -type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>; +pub type DwarfReader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>; #[derive(Debug, Default)] pub struct WasmFileInfo { @@ -140,7 +142,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { diagnostics: &DiagnosticsHandler, ) -> WasmResult> { for payload in parser.parse_all(data) { - self.parse_payload(payload?, diagnostics)?; + self.parse_payload(payload.into_diagnostic()?, diagnostics)?; } Ok(self.result) } @@ -157,11 +159,14 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { encoding, range, } => { - self.validator.version(num, encoding, &range)?; + self.validator.version(num, encoding, &range).into_diagnostic()?; match encoding { Encoding::Module => {} Encoding::Component => { - return Err(WasmError::Unsupported("component model".to_string())); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("wasm error: component model is not supported") + .into_report()); } } } @@ -169,25 +174,25 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { Payload::TypeSection(types) => self.type_section(types)?, Payload::ImportSection(imports) => self.import_section(imports)?, Payload::FunctionSection(functions) => self.function_section(functions)?, - Payload::TableSection(tables) => self.table_section(tables)?, + Payload::TableSection(tables) => self.table_section(tables, diagnostics)?, Payload::MemorySection(memories) => self.memory_section(memories)?, Payload::TagSection(tags) => { - self.validator.tag_section(&tags)?; + self.validator.tag_section(&tags).into_diagnostic()?; // This feature isn't enabled at this time, so we should // never get here. unreachable!(); } - Payload::GlobalSection(globals) => self.global_section(globals)?, + Payload::GlobalSection(globals) => self.global_section(globals, diagnostics)?, Payload::ExportSection(exports) => self.export_section(exports)?, Payload::StartSection { func, range } => self.start_section(func, range)?, - Payload::ElementSection(elements) => self.element_section(elements)?, + Payload::ElementSection(elements) => self.element_section(elements, diagnostics)?, Payload::CodeSectionStart { count, range, .. } => { self.code_section_start(count, range)? } Payload::CodeSectionEntry(body) => self.code_section_entry(body)?, Payload::DataSection(data) => self.data_section(data, diagnostics)?, Payload::DataCountSection { count, range } => { - self.validator.data_count_section(count, &range)?; + self.validator.data_count_section(count, &range).into_diagnostic()?; // Note: the count passed in here is the *total* segment count // There is no way to reserve for just the passive segments as // they are discovered when iterating the data section entries @@ -195,7 +200,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { // the passive count, do not reserve anything here. } Payload::CustomSection(s) if s.name() == "name" => { - let result = self.name_section(NameSectionReader::new(s.data(), s.data_offset())); + let reader = wasmparser::BinaryReader::new( + s.data(), + s.data_offset(), + *self.validator.features(), + ); + let result = self.name_section(NameSectionReader::new(reader)); if let Err(e) = result { log::warn!("failed to parse name section {:?}", e); } @@ -205,15 +215,15 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { // payloads such as `UnknownSection` or those related to the // component model. other => { - self.validator.payload(&other)?; - unsupported_diag!(diagnostics, "unsupported section in wasm file {:?}", other); + self.validator.payload(&other).into_diagnostic()?; + unsupported_diag!(diagnostics, "wasm error: unsupported section {:?}", other); } } Ok(()) } - fn payload_end(&mut self, offset: usize) -> Result<(), WasmError> { - self.validator.end(offset)?; + fn payload_end(&mut self, offset: usize) -> Result<(), Report> { + self.validator.end(offset).into_diagnostic()?; self.result.exported_signatures = self .result .module @@ -232,11 +242,8 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { Ok(()) } - fn type_section( - &mut self, - types: wasmparser::TypeSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.type_section(&types)?; + fn type_section(&mut self, types: wasmparser::TypeSectionReader<'data>) -> Result<(), Report> { + self.validator.type_section(&types).into_diagnostic()?; let num = usize::try_from(types.count()).unwrap(); self.result.module.types.reserve(num); self.types.reserve_wasm_signatures(num); @@ -251,18 +258,18 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn import_section( &mut self, imports: wasmparser::ImportSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.import_section(&imports)?; + ) -> Result<(), Report> { + self.validator.import_section(&imports).into_diagnostic()?; let cnt = usize::try_from(imports.count()).unwrap(); self.result.module.imports.reserve(cnt); for entry in imports { - let import = entry?; + let import = entry.into_diagnostic()?; let ty = match import.ty { TypeRef::Func(index) => { let index = TypeIndex::from_u32(index); let sig_index = self.result.module.types[index].unwrap_function(); self.result.module.num_imported_funcs += 1; - self.result.debuginfo.wasm_file.imported_func_count += 1; + self.result.wasm_file.imported_func_count += 1; EntityType::Function(sig_index) } TypeRef::Memory(ty) => { @@ -289,12 +296,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn function_section( &mut self, functions: wasmparser::FunctionSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.function_section(&functions)?; + ) -> Result<(), Report> { + self.validator.function_section(&functions).into_diagnostic()?; let cnt = usize::try_from(functions.count()).unwrap(); self.result.module.functions.reserve_exact(cnt); for entry in functions { - let sigindex = entry?; + let sigindex = entry.into_diagnostic()?; let ty = TypeIndex::from_u32(sigindex); let sig_index = self.result.module.types[ty].unwrap_function(); self.result.module.push_function(sig_index); @@ -305,12 +312,13 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn table_section( &mut self, tables: wasmparser::TableSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.table_section(&tables)?; + diagnostics: &DiagnosticsHandler, + ) -> Result<(), Report> { + self.validator.table_section(&tables).into_diagnostic()?; let cnt = usize::try_from(tables.count()).unwrap(); self.result.module.tables.reserve_exact(cnt); for entry in tables { - let wasmparser::Table { ty, init } = entry?; + let wasmparser::Table { ty, init } = entry.into_diagnostic()?; let table = convert_table_type(&ty); self.result.module.tables.push(table); let init = match init { @@ -319,7 +327,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { }, wasmparser::TableInit::Expr(cexpr) => { let mut init_expr_reader = cexpr.get_binary_reader(); - match init_expr_reader.read_operator()? { + match init_expr_reader.read_operator().into_diagnostic()? { Operator::RefNull { hty: _ } => TableInitialValue::Null { precomputed: Vec::new(), }, @@ -329,10 +337,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { TableInitialValue::FuncRef(index) } s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in table section: {:?}", + unsupported_diag!( + diagnostics, + "wasm error: unsupported init expr in table section: {:?}", s - ))); + ); } } } @@ -345,8 +354,8 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn memory_section( &mut self, memories: wasmparser::MemorySectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.memory_section(&memories)?; + ) -> Result<(), Report> { + self.validator.memory_section(&memories).into_diagnostic()?; let cnt = usize::try_from(memories.count()).unwrap(); assert_eq!(cnt, 1, "only one memory per module is supported"); Ok(()) @@ -355,14 +364,15 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn global_section( &mut self, globals: wasmparser::GlobalSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.global_section(&globals)?; + diagnostics: &DiagnosticsHandler, + ) -> Result<(), Report> { + self.validator.global_section(&globals).into_diagnostic()?; let cnt = usize::try_from(globals.count()).unwrap(); self.result.module.globals.reserve_exact(cnt); for entry in globals { - let wasmparser::Global { ty, init_expr } = entry?; + let wasmparser::Global { ty, init_expr } = entry.into_diagnostic()?; let mut init_expr_reader = init_expr.get_binary_reader(); - let initializer = match init_expr_reader.read_operator()? { + let initializer = match init_expr_reader.read_operator().into_diagnostic()? { Operator::I32Const { value } => GlobalInit::I32Const(value), Operator::I64Const { value } => GlobalInit::I64Const(value), Operator::F32Const { value } => GlobalInit::F32Const(value.bits()), @@ -374,10 +384,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) } s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in global section: {:?}", + unsupported_diag!( + diagnostics, + "wasm error: unsupported init expr in global section: {:?}", s - ))); + ); } }; let ty = convert_global_type(&ty); @@ -390,12 +401,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn export_section( &mut self, exports: wasmparser::ExportSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.export_section(&exports)?; + ) -> Result<(), Report> { + self.validator.export_section(&exports).into_diagnostic()?; let cnt = usize::try_from(exports.count()).unwrap(); self.result.module.exports.reserve(cnt); for entry in exports { - let wasmparser::Export { name, kind, index } = entry?; + let wasmparser::Export { name, kind, index } = entry.into_diagnostic()?; let entity = match kind { ExternalKind::Func => { let index = FuncIndex::from_u32(index); @@ -414,8 +425,8 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { Ok(()) } - fn start_section(&mut self, func: u32, range: Range) -> Result<(), WasmError> { - self.validator.start_section(func, &range)?; + fn start_section(&mut self, func: u32, range: Range) -> Result<(), Report> { + self.validator.start_section(func, &range).into_diagnostic()?; let func_index = FuncIndex::from_u32(func); self.flag_func_escaped(func_index); debug_assert!(self.result.module.start_func.is_none()); @@ -426,14 +437,15 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { fn element_section( &mut self, elements: wasmparser::ElementSectionReader<'data>, - ) -> Result<(), WasmError> { - self.validator.element_section(&elements)?; + diagnostics: &DiagnosticsHandler, + ) -> Result<(), Report> { + self.validator.element_section(&elements).into_diagnostic()?; for (index, entry) in elements.into_iter().enumerate() { let wasmparser::Element { kind, items, range: _, - } = entry?; + } = entry.into_diagnostic()?; // Build up a list of `FuncIndex` corresponding to all the // entries listed in this segment. Note that it's not @@ -445,7 +457,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { ElementItems::Functions(funcs) => { elements.reserve(usize::try_from(funcs.count()).unwrap()); for func in funcs { - let func = FuncIndex::from_u32(func?); + let func = FuncIndex::from_u32(func.into_diagnostic()?); self.flag_func_escaped(func); elements.push(func); } @@ -453,7 +465,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { ElementItems::Expressions(_ty, funcs) => { elements.reserve(usize::try_from(funcs.count()).unwrap()); for func in funcs { - let func = match func?.get_binary_reader().read_operator()? { + let func = match func + .into_diagnostic()? + .get_binary_reader() + .read_operator() + .into_diagnostic()? + { Operator::RefNull { .. } => FuncIndex::reserved_value(), Operator::RefFunc { function_index } => { let func = FuncIndex::from_u32(function_index); @@ -461,10 +478,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { func } s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in element section: {:?}", + unsupported_diag!( + diagnostics, + "wasm error: unsupported init expr in element section: {:?}", s - ))); + ); } }; elements.push(func); @@ -479,18 +497,20 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { } => { let table_index = TableIndex::from_u32(table_index.unwrap_or(0)); let mut offset_expr_reader = offset_expr.get_binary_reader(); - let (base, offset) = match offset_expr_reader.read_operator()? { - Operator::I32Const { value } => (None, value as u32), - Operator::GlobalGet { global_index } => { - (Some(GlobalIndex::from_u32(global_index)), 0) - } - ref s => { - return Err(WasmError::Unsupported(format!( - "unsupported init expr in element section: {:?}", - s - ))); - } - }; + let (base, offset) = + match offset_expr_reader.read_operator().into_diagnostic()? { + Operator::I32Const { value } => (None, value as u32), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + ref s => { + unsupported_diag!( + diagnostics, + "wasm error: unsupported init expr in element section: {:?}", + s + ); + } + }; self.result.module.table_initialization.segments.push(TableSegment { table_index, @@ -513,33 +533,32 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { Ok(()) } - fn code_section_start(&mut self, count: u32, range: Range) -> Result<(), WasmError> { - self.validator.code_section_start(count, &range)?; + fn code_section_start(&mut self, count: u32, range: Range) -> Result<(), Report> { + self.validator.code_section_start(count, &range).into_diagnostic()?; let cnt = usize::try_from(count).unwrap(); self.result.function_body_inputs.reserve_exact(cnt); - self.result.debuginfo.wasm_file.code_section_offset = range.start as u64; + self.result.wasm_file.code_section_offset = range.start as u64; Ok(()) } - fn code_section_entry(&mut self, mut body: FunctionBody<'data>) -> Result<(), WasmError> { - let validator = self.validator.code_section_entry(&body)?; + fn code_section_entry(&mut self, body: FunctionBody<'data>) -> Result<(), Report> { + let validator = self.validator.code_section_entry(&body).into_diagnostic()?; let func_index = self.result.code_index + self.result.module.num_imported_funcs as u32; let func_index = FuncIndex::from_u32(func_index); if self.config.generate_native_debuginfo { let sig_index = self.result.module.functions[func_index].signature; let sig = &self.types[sig_index]; let mut locals = Vec::new(); - for pair in body.get_locals_reader()? { - let (cnt, ty) = pair?; + for pair in body.get_locals_reader().into_diagnostic()? { + let (cnt, ty) = pair.into_diagnostic()?; let ty = convert_valtype(ty); locals.push((cnt, ty)); } - self.result.debuginfo.wasm_file.funcs.push(FunctionMetadata { + self.result.wasm_file.funcs.push(FunctionMetadata { locals: locals.into_boxed_slice(), params: sig.params().into(), }); } - body.allow_memarg64(false); self.result.function_body_inputs.push(FunctionBodyData { validator, body }); self.result.code_index += 1; Ok(()) @@ -550,7 +569,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { data_section: wasmparser::DataSectionReader<'data>, diagnostics: &DiagnosticsHandler, ) -> WasmResult<()> { - self.validator.data_section(&data_section)?; + self.validator.data_section(&data_section).into_diagnostic()?; let cnt = usize::try_from(data_section.count()).unwrap(); self.result.data_segments.reserve_exact(cnt); for entry in data_section.into_iter() { @@ -558,7 +577,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { kind, data, range: _, - } = entry?; + } = entry.into_diagnostic()?; match kind { DataKind::Active { memory_index, @@ -570,7 +589,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { supported)" ); let mut offset_expr_reader = offset_expr.get_binary_reader(); - let offset = match offset_expr_reader.read_operator()? { + let offset = match offset_expr_reader.read_operator().into_diagnostic()? { Operator::I32Const { value } => DataSegmentOffset::I32Const(value), Operator::GlobalGet { global_index } => { DataSegmentOffset::GetGlobal(GlobalIndex::from_u32(global_index)) @@ -578,7 +597,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { ref s => { unsupported_diag!( diagnostics, - "unsupported init expr in data section offset: {:?}", + "wasm error: unsupported init expr in data section offset: {:?}", s ); } @@ -587,9 +606,10 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { self.result.data_segments.push(segment); } DataKind::Passive => { - return Err(WasmError::Unsupported( - "unsupported passive data segment in data section".to_string(), - )); + unsupported_diag!( + diagnostics, + "wasm error: unsupported passive data segment in data section" + ); } } } @@ -599,10 +619,10 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { /// Parses the Name section of the wasm module. fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> { for subsection in names { - match subsection? { + match subsection.into_diagnostic()? { wasmparser::Name::Function(names) => { for name in names { - let Naming { index, name } = name?; + let Naming { index, name } = name.into_diagnostic()?; // Skip this naming if it's naming a function that // doesn't actually exist. if (index as usize) >= self.result.module.functions.len() { @@ -632,14 +652,14 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { continue; } for f in reader { - let f = f?; + let f = f.into_diagnostic()?; // Skip this naming if it's naming a function that // doesn't actually exist. if (f.index as usize) >= self.result.module.functions.len() { continue; } for name in f.names { - let Naming { index, name } = name?; + let Naming { index, name } = name.into_diagnostic()?; self.result .module @@ -653,7 +673,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { } wasmparser::Name::Global(names) => { for name in names { - let Naming { index, name } = name?; + let Naming { index, name } = name.into_diagnostic()?; if index != u32::max_value() { self.result .module @@ -665,7 +685,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { } wasmparser::Name::Data(names) => { for name in names { - let Naming { index, name } = name?; + let Naming { index, name } = name.into_diagnostic()?; if index != u32::max_value() { self.result .module @@ -680,6 +700,8 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { | wasmparser::Name::Table(_) | wasmparser::Name::Memory(_) | wasmparser::Name::Element(_) + | wasmparser::Name::Field(_) + | wasmparser::Name::Tag(_) | wasmparser::Name::Unknown { .. } => {} } } @@ -770,17 +792,19 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { } fn declare_type(&mut self, id: CoreTypeId) -> WasmResult<()> { + use wasmparser::CompositeInnerType; + let types = self.validator.types(0).unwrap(); let ty = &types[id]; assert!(ty.is_final); assert!(ty.supertype_idx.is_none()); - match &ty.composite_type { - CompositeType::Func(ty) => { + match &ty.composite_type.inner { + CompositeInnerType::Func(ty) => { let wasm = convert_func_type(ty); let sig_index = self.types.wasm_func_type(id, wasm); self.result.module.types.push(ModuleType::Function(sig_index)); } - CompositeType::Array(_) | CompositeType::Struct(_) => unimplemented!(), + CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => unimplemented!(), } Ok(()) } diff --git a/frontend-wasm/src/module/module_translation_state.rs b/frontend-wasm/src/module/module_translation_state.rs index 08e66ba31..0c33b821e 100644 --- a/frontend-wasm/src/module/module_translation_state.rs +++ b/frontend-wasm/src/module/module_translation_state.rs @@ -1,6 +1,8 @@ use miden_core::crypto::hash::RpoDigest; -use miden_diagnostics::DiagnosticsHandler; -use midenc_hir::{AbiParam, CallConv, DataFlowGraph, FunctionIdent, Ident, Linkage, Signature}; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Severity}, + AbiParam, CallConv, DataFlowGraph, FunctionIdent, Ident, Linkage, Signature, +}; use rustc_hash::FxHashMap; use super::{instance::ModuleArgument, ir_func_type, EntityIndex, FuncIndex, Module, ModuleTypes}; @@ -9,7 +11,6 @@ use crate::{ intrinsics::is_miden_intrinsics_module, miden_abi::{is_miden_abi_module, miden_abi_function_type, parse_import_function_digest}, translation_utils::sig_from_funct_type, - WasmError, }; pub struct ModuleTranslationState { @@ -24,7 +25,12 @@ pub struct ModuleTranslationState { } impl ModuleTranslationState { - pub fn new(module: &Module, mod_types: &ModuleTypes, module_args: Vec) -> Self { + pub fn new( + module: &Module, + mod_types: &ModuleTypes, + module_args: Vec, + diagnostics: &DiagnosticsHandler, + ) -> Self { let mut function_import_subst = FxHashMap::default(); if module.imports.len() == module_args.len() { for (import, arg) in module.imports.iter().zip(module_args) { @@ -51,7 +57,7 @@ impl ModuleTranslationState { let mut digests = FxHashMap::default(); for (index, func_type) in &module.functions { let wasm_func_type = mod_types[func_type.signature].clone(); - let ir_func_type = ir_func_type(&wasm_func_type).unwrap(); + let ir_func_type = ir_func_type(&wasm_func_type, diagnostics).unwrap(); let sig = sig_from_funct_type(&ir_func_type, CallConv::SystemV, Linkage::External); if let Some(subst) = function_import_subst.get(&index) { functions.insert(index, (*subst, sig)); @@ -135,11 +141,7 @@ impl ModuleTranslationState { imported (function call) with a different signature", func_id.function, func_id.module ); - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message(message.clone()) - .emit(); - WasmError::Unexpected(message) + diagnostics.diagnostic(Severity::Error).with_message(message).into_report() })?; } Ok(func_id) diff --git a/frontend-wasm/src/module/types.rs b/frontend-wasm/src/module/types.rs index a520335a7..ad130de7d 100644 --- a/frontend-wasm/src/module/types.rs +++ b/frontend-wasm/src/module/types.rs @@ -3,15 +3,15 @@ use core::fmt; use std::{collections::HashMap, ops::Index}; -use hir::Abi; -use miden_diagnostics::DiagnosticsHandler; -use midenc_hir::{cranelift_entity::PrimaryMap, AbiParam, CallConv, Linkage, Signature}; -use midenc_hir_type as hir; +use midenc_hir::{ + cranelift_entity::PrimaryMap, + diagnostics::{DiagnosticsHandler, Severity}, + AbiParam, CallConv, Linkage, Signature, +}; +use midenc_hir_type::{self as hir, Abi}; use wasmparser::types::CoreTypeId; -use crate::{ - component::SignatureIndex, error::WasmResult, module::Module, unsupported_diag, WasmError, -}; +use crate::{component::SignatureIndex, error::WasmResult, module::Module, unsupported_diag}; /// Generates a new index type for each entity. #[macro_export] @@ -410,16 +410,15 @@ impl DataSegmentOffset { DataSegmentOffset::GetGlobal(global_idx) => { let global_init = &module.try_global_initializer(*global_idx, diagnostics)?; match global_init.as_i32(module, diagnostics) { - Err(e) => { - diagnostics - .diagnostic(miden_diagnostics::Severity::Error) + Err(_) => { + return Err(diagnostics + .diagnostic(Severity::Error) .with_message(format!( "Failed to get data segment offset from global init {:?} with \ global index {global_idx:?}", global_init, )) - .emit(); - return Err(e); + .into_report()); } Ok(v) => v, } @@ -448,24 +447,25 @@ impl BlockType { pub fn from_wasm( block_ty: &wasmparser::BlockType, mod_types: &ModuleTypes, + diagnostics: &DiagnosticsHandler, ) -> WasmResult { Ok(match block_ty { wasmparser::BlockType::Empty => Self::default(), wasmparser::BlockType::Type(ty) => Self { params: vec![], - results: vec![ir_type(convert_valtype(*ty))?], + results: vec![ir_type(convert_valtype(*ty), diagnostics)?], }, wasmparser::BlockType::FuncType(ty_index) => { let func_type = &mod_types[SignatureIndex::from_u32(*ty_index)]; let params = func_type .params() .iter() - .map(|t| ir_type(*t)) + .map(|t| ir_type(*t, diagnostics)) .collect::>>()?; let results = func_type .returns() .iter() - .map(|t| ir_type(*t)) + .map(|t| ir_type(*t, diagnostics)) .collect::>>()?; Self { params, results } } @@ -554,16 +554,19 @@ where } /// Converts a Wasm function type into a Miden IR function type -pub fn ir_func_type(ty: &WasmFuncType) -> WasmResult { +pub fn ir_func_type( + ty: &WasmFuncType, + diagnostics: &DiagnosticsHandler, +) -> WasmResult { let params = ty .params() .iter() - .map(|t| ir_type(*t)) + .map(|t| ir_type(*t, diagnostics)) .collect::>>()?; let results = ty .returns() .iter() - .map(|t| ir_type(*t)) + .map(|t| ir_type(*t, diagnostics)) .collect::>>()?; Ok(hir::FunctionType { abi: Abi::Canonical, @@ -573,17 +576,13 @@ pub fn ir_func_type(ty: &WasmFuncType) -> WasmResult { } /// Converts a Wasm type into a Miden IR type -pub fn ir_type(ty: WasmType) -> WasmResult { +pub fn ir_type(ty: WasmType, diagnostics: &DiagnosticsHandler) -> WasmResult { Ok(match ty { WasmType::I32 => hir::Type::I32, WasmType::I64 => hir::Type::I64, WasmType::F32 => hir::Type::Felt, - WasmType::F64 => return Err(WasmError::Unsupported("no f64 type in Miden IR".to_string())), - WasmType::V128 => { - return Err(WasmError::Unsupported("V128 type is not supported".to_string())); - } - WasmType::Ref(_) => { - return Err(WasmError::Unsupported("Ref type is not supported".to_string())); + ty @ (WasmType::F64 | WasmType::V128 | WasmType::Ref(_)) => { + unsupported_diag!(diagnostics, "wasm error: unsupported type '{}'", ty) } }) } @@ -612,10 +611,12 @@ pub fn convert_global_type(ty: &wasmparser::GlobalType) -> Global { /// Converts a wasmparser table type pub fn convert_table_type(ty: &wasmparser::TableType) -> Table { + assert!(!ty.table64, "64-bit tables are not supported"); + Table { wasm_ty: convert_ref_type(ty.element_type), - minimum: ty.initial, - maximum: ty.maximum, + minimum: ty.initial as u32, + maximum: ty.maximum.map(|n| n as u32), } } @@ -648,19 +649,15 @@ pub fn convert_ref_type(ty: wasmparser::RefType) -> WasmRefType { /// Converts a wasmparser heap type pub fn convert_heap_type(ty: wasmparser::HeapType) -> WasmHeapType { + use wasmparser::AbstractHeapType; match ty { - wasmparser::HeapType::Func => WasmHeapType::Func, - wasmparser::HeapType::Extern => WasmHeapType::Extern, - wasmparser::HeapType::Concrete(_) - | wasmparser::HeapType::Any - | wasmparser::HeapType::None - | wasmparser::HeapType::NoExtern - | wasmparser::HeapType::NoFunc - | wasmparser::HeapType::Eq - | wasmparser::HeapType::Struct - | wasmparser::HeapType::Array - | wasmparser::HeapType::I31 => { - unimplemented!("unsupported heap type {ty:?}"); + wasmparser::HeapType::Abstract { ty, shared: _ } => match ty { + AbstractHeapType::Func => WasmHeapType::Func, + AbstractHeapType::Extern => WasmHeapType::Extern, + ty => unimplemented!("unsupported abstract heap type {ty:?}"), + }, + wasmparser::HeapType::Concrete(_) => { + unimplemented!("user-defined types are not supported yet") } } } diff --git a/frontend-wasm/src/ssa.rs b/frontend-wasm/src/ssa.rs index 5975aa97d..7c1a27a76 100644 --- a/frontend-wasm/src/ssa.rs +++ b/frontend-wasm/src/ssa.rs @@ -11,11 +11,11 @@ use core::mem; -use miden_diagnostics::SourceSpan; use midenc_hir::{ cranelift_entity::{ entity_impl, packed_option::PackedOption, EntityList, EntitySet, ListPool, SecondaryMap, }, + diagnostics::SourceSpan, Block, DataFlowGraph, Inst, Value, }; use midenc_hir_type::Type; diff --git a/frontend-wasm/src/test_utils.rs b/frontend-wasm/src/test_utils.rs index a1505b100..fe6b1697c 100644 --- a/frontend-wasm/src/test_utils.rs +++ b/frontend-wasm/src/test_utils.rs @@ -1,25 +1,13 @@ use std::sync::Arc; -use miden_diagnostics::{ - term::termcolor::ColorChoice, CodeMap, DiagnosticsConfig, DiagnosticsHandler, Emitter, - NullEmitter, Verbosity, +use midenc_hir::{ + diagnostics::{ColorChoice, NullEmitter}, + testing::TestContext, }; +use midenc_session::Options; -pub fn default_emitter(color: ColorChoice) -> Arc { - Arc::new(NullEmitter::new(color)) -} - -pub fn test_diagnostics() -> DiagnosticsHandler { - let codemap = Arc::new(CodeMap::new()); - - DiagnosticsHandler::new( - DiagnosticsConfig { - verbosity: Verbosity::Debug, - warnings_as_errors: false, - no_warn: false, - display: Default::default(), - }, - codemap, - default_emitter(ColorChoice::Auto), - ) +pub fn test_context() -> TestContext { + let options = Options::default().with_verbosity(midenc_session::Verbosity::Debug); + let emitter = Arc::new(NullEmitter::new(ColorChoice::Auto)); + TestContext::default_with_opts_and_emitter(options, Some(emitter)) } diff --git a/frontend-wasm/src/translation_utils.rs b/frontend-wasm/src/translation_utils.rs index 0da137732..3f68261b1 100644 --- a/frontend-wasm/src/translation_utils.rs +++ b/frontend-wasm/src/translation_utils.rs @@ -1,11 +1,15 @@ //! Helper functions and structures for the translation. -use miden_diagnostics::SourceSpan; -use midenc_hir::{AbiParam, CallConv, Felt, FieldElement, InstBuilder, Linkage, Signature, Value}; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Severity, SourceSpan}, + AbiParam, CallConv, Felt, FieldElement, InstBuilder, Linkage, Signature, Value, +}; use midenc_hir_type::{FunctionType, Type}; use rustc_hash::FxHasher; -use crate::{error::WasmResult, module::function_builder_ext::FunctionBuilderExt, WasmError}; +use crate::{ + error::WasmResult, module::function_builder_ext::FunctionBuilderExt, unsupported_diag, +}; pub type BuildFxHasher = std::hash::BuildHasherDefault; @@ -101,7 +105,11 @@ const fn ceiling_divide(n: usize, d: usize) -> usize { } /// Emit instructions to produce a zero value in the given type. -pub fn emit_zero(ty: &Type, builder: &mut FunctionBuilderExt) -> WasmResult { +pub fn emit_zero( + ty: &Type, + builder: &mut FunctionBuilderExt, + diagnostics: &DiagnosticsHandler, +) -> WasmResult { Ok(match ty { Type::I1 => builder.ins().i1(false, SourceSpan::default()), Type::I8 => builder.ins().i8(0, SourceSpan::default()), @@ -125,10 +133,7 @@ pub fn emit_zero(ty: &Type, builder: &mut FunctionBuilderExt) -> WasmResult { - return Err(WasmError::Unsupported(format!( - "cannot emit zero value for type: {:?}", - ty - ))); + unsupported_diag!(diagnostics, "cannot emit zero value for type: {:?}", ty); } }) } diff --git a/hir-analysis/Cargo.toml b/hir-analysis/Cargo.toml index a3ae43436..95fa99858 100644 --- a/hir-analysis/Cargo.toml +++ b/hir-analysis/Cargo.toml @@ -17,7 +17,6 @@ cranelift-entity.workspace = true cranelift-bforest.workspace = true inventory.workspace = true intrusive-collections.workspace = true -miden-diagnostics.workspace = true midenc-hir.workspace = true midenc-session.workspace = true rustc-hash.workspace = true diff --git a/hir-analysis/src/control_flow.rs b/hir-analysis/src/control_flow.rs index 59586ee82..efc80b3e6 100644 --- a/hir-analysis/src/control_flow.rs +++ b/hir-analysis/src/control_flow.rs @@ -222,30 +222,28 @@ pub(crate) fn visit_block_succs( if let Some(inst) = dfg.last_inst(block) { match &dfg[inst] { - Instruction::Br(Br { - destination: dest, .. - }) => { - visit(inst, *dest, false); + Instruction::Br(Br { successor, .. }) => { + visit(inst, successor.destination, false); } Instruction::CondBr(CondBr { - then_dest: (block_then, _), - else_dest: (block_else, _), + then_dest, + else_dest, .. }) => { - visit(inst, *block_then, false); - visit(inst, *block_else, false); + visit(inst, then_dest.destination, false); + visit(inst, else_dest.destination, false); } Instruction::Switch(Switch { ref arms, - default: default_block, + default: default_succ, .. }) => { - visit(inst, *default_block, false); + visit(inst, default_succ.destination, false); - for (_, dest) in arms.as_slice() { - visit(inst, *dest, true); + for arm in arms.as_slice() { + visit(inst, arm.successor.destination, true); } } @@ -256,11 +254,6 @@ pub(crate) fn visit_block_succs( #[cfg(test)] mod tests { - use std::sync::Arc; - - use miden_diagnostics::{ - term::termcolor::ColorChoice, CodeMap, DefaultEmitter, DiagnosticsHandler, - }; use midenc_hir::*; use super::*; @@ -294,9 +287,7 @@ mod tests { #[test] fn cfg_branches_and_jumps() { - let codemap = Arc::new(CodeMap::new()); - let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto)); - let diagnostics = DiagnosticsHandler::new(Default::default(), codemap.clone(), emitter); + let diagnostics = diagnostics::DiagnosticsHandler::default(); // Define the 'test' module let mut builder = ModuleBuilder::new("test"); diff --git a/hir-analysis/src/def_use.rs b/hir-analysis/src/def_use.rs index f958c63d9..25fad3274 100644 --- a/hir-analysis/src/def_use.rs +++ b/hir-analysis/src/def_use.rs @@ -122,9 +122,11 @@ impl DefUseGraph { args[index as usize] = replacement; } Use::BlockArgument { succ, index } => match dfg.insts[current_use.inst].as_mut() { - Instruction::Br(ref mut b) => { + Instruction::Br(midenc_hir::Br { + ref mut successor, .. + }) => { assert_eq!(succ, 0); - let args = b.args.as_mut_slice(&mut dfg.value_lists); + let args = successor.args.as_mut_slice(&mut dfg.value_lists); args[index as usize] = replacement; } Instruction::CondBr(midenc_hir::CondBr { @@ -133,16 +135,31 @@ impl DefUseGraph { .. }) => { let args = match succ { - 0 => then_dest.1.as_mut_slice(&mut dfg.value_lists), - 1 => else_dest.1.as_mut_slice(&mut dfg.value_lists), + 0 => then_dest.args.as_mut_slice(&mut dfg.value_lists), + 1 => else_dest.args.as_mut_slice(&mut dfg.value_lists), n => unreachable!( "unexpected successor index {n} for conditional branch" ), }; args[index as usize] = replacement; } - Instruction::Switch(_) => { - unimplemented!("support for switch arms with arguments is not implemented") + Instruction::Switch(midenc_hir::Switch { + ref mut arms, + default: ref mut default_succ, + .. + }) => { + let succ = succ as usize; + assert!( + succ < arms.len() + 1, + "invalid successor index {succ}: but only {} arms plus fallback", + arms.len() + ); + let args = if arms.len() == succ { + default_succ.args.as_mut_slice(&mut dfg.value_lists) + } else { + arms[succ].successor.args.as_mut_slice(&mut dfg.value_lists) + }; + args[index as usize] = replacement; } _ => unreachable!(), }, @@ -180,14 +197,14 @@ impl DefUseGraph { } graph.insert_operand_uses(inst, dfg, domtree); } - BranchInfo::SingleDest(_, args) => { + BranchInfo::SingleDest(successor) => { debug_assert_eq!( dfg.inst_results(inst), &[], "branch instructions cannot have results" ); graph.insert_operand_uses(inst, dfg, domtree); - for (index, value) in args.iter().copied().enumerate() { + for (index, value) in successor.args.iter().copied().enumerate() { debug_assert!(def_dominates_use(value, inst, dfg, domtree)); let user = Box::new(User { link: Default::default(), @@ -201,16 +218,16 @@ impl DefUseGraph { graph.insert_use(user); } } - BranchInfo::MultiDest(ref jts) => { + BranchInfo::MultiDest(ref successors) => { debug_assert_eq!( dfg.inst_results(inst), &[], "branch instructions cannot have results" ); graph.insert_operand_uses(inst, dfg, domtree); - for (succ, jt) in jts.iter().enumerate() { + for (succ, successor) in successors.iter().enumerate() { let succ = u16::try_from(succ).expect("too many successors"); - for (index, value) in jt.args.iter().copied().enumerate() { + for (index, value) in successor.args.iter().copied().enumerate() { debug_assert!(def_dominates_use(value, inst, dfg, domtree)); let user = Box::new(User { link: Default::default(), diff --git a/hir-analysis/src/dominance.rs b/hir-analysis/src/dominance.rs index db6452908..bd48a22e7 100644 --- a/hir-analysis/src/dominance.rs +++ b/hir-analysis/src/dominance.rs @@ -355,15 +355,15 @@ impl DominatorTree { // consumers of the postorder we cache here. match func.dfg.analyze_branch(inst) { BranchInfo::NotABranch => (), - BranchInfo::SingleDest(dest, _) => { - if self.nodes[dest].rpo_number == 0 { - self.stack.push((Visit::First, dest)); + BranchInfo::SingleDest(successor) => { + if self.nodes[successor.destination].rpo_number == 0 { + self.stack.push((Visit::First, successor.destination)); } } - BranchInfo::MultiDest(ref jt) => { - for dest in jt.iter().rev().map(|entry| entry.destination) { - if self.nodes[dest].rpo_number == 0 { - self.stack.push((Visit::First, dest)); + BranchInfo::MultiDest(ref successors) => { + for successor in successors.iter().rev() { + if self.nodes[successor.destination].rpo_number == 0 { + self.stack.push((Visit::First, successor.destination)); } } } diff --git a/hir-analysis/src/lib.rs b/hir-analysis/src/lib.rs index e84ea63f9..98c2af98a 100644 --- a/hir-analysis/src/lib.rs +++ b/hir-analysis/src/lib.rs @@ -1,3 +1,5 @@ +extern crate alloc; + mod control_flow; mod data; mod def_use; diff --git a/hir-analysis/src/liveness.rs b/hir-analysis/src/liveness.rs index 2d69806e7..3915dea3c 100644 --- a/hir-analysis/src/liveness.rs +++ b/hir-analysis/src/liveness.rs @@ -442,7 +442,7 @@ fn compute_liveness( BranchInfo::NotABranch => { flow_sensitive.insert(block, false); } - BranchInfo::SingleDest(succ, _) => { + BranchInfo::SingleDest(succ) => { // If the successor is flow-sensitive, by definition so must the predecessor. // // If the successor's sensitivity is not yet known, then that means control can @@ -458,18 +458,19 @@ fn compute_liveness( // Putting this all together - it must be the case that we either know that `succ` // is flow-insensitive, or we know that it is flow-sensitive, either explicitly or // by implication. - flow_sensitive.insert(block, flow_sensitive.get(&succ).copied().unwrap_or(true)); + flow_sensitive + .insert(block, flow_sensitive.get(&succ.destination).copied().unwrap_or(true)); } - BranchInfo::MultiDest(jts) => { + BranchInfo::MultiDest(succs) => { // Must like the single-successor case, we derive flow-sensitivity for predecessors // from their successors. // // The primary difference in this situation, is that the only possible way for // `block` to be flow-insensitive, is if all successors are explicitly flow- // insensitive. - let is_flow_sensitive = jts + let is_flow_sensitive = succs .iter() - .any(|jt| flow_sensitive.get(&jt.destination).copied().unwrap_or(true)); + .any(|succ| flow_sensitive.get(&succ.destination).copied().unwrap_or(true)); flow_sensitive.insert(block, is_flow_sensitive); } } @@ -618,7 +619,10 @@ fn compute_liveness( // This is a branch instruction, so get the next-use set at the entry of each // successor, increment the distances in those sets based on the distance of the // edge, and then take the join of those sets as the initial next-use set for `inst` - BranchInfo::SingleDest(succ, succ_args) => { + BranchInfo::SingleDest(SuccessorInfo { + destination: succ, + args: succ_args, + }) => { let mut inst_next_uses = liveness .live_in .get(&ProgramPoint::Block(succ)) @@ -690,7 +694,7 @@ fn compute_liveness( // graph have been split, as we cannot proceed correctly otherwise. It is expected // that either no critical edges exist, or that they have been split by a prior // transformation. - BranchInfo::MultiDest(jts) => { + BranchInfo::MultiDest(succs) => { let mut inst_next_uses = NextUseSet::default(); let mut inst_next_uses_after = NextUseSet::default(); @@ -703,10 +707,10 @@ fn compute_liveness( } let mut max_branch_operand_stack_pressure = operand_stack_pressure; - for JumpTable { + for SuccessorInfo { destination, args: succ_args, - } in jts.iter() + } in succs.iter() { let destination = *destination; // If the successor block has multiple predecessors, this is a critical diff --git a/hir-analysis/src/spill.rs b/hir-analysis/src/spill.rs index ea23a5447..00e88c70b 100644 --- a/hir-analysis/src/spill.rs +++ b/hir-analysis/src/spill.rs @@ -1004,12 +1004,12 @@ fn compute_control_flow_edge_spills_and_reloads( // parameters, with the corresponding source values in W^exit(P) (issuing reloads if the value // given as argument in the predecessor is not in W^exit(P)) let pred_args = match function.dfg.analyze_branch(pred.inst) { - BranchInfo::SingleDest(_, pred_args) => pred_args, - BranchInfo::MultiDest(jts) => jts + BranchInfo::SingleDest(successor) => successor.args, + BranchInfo::MultiDest(successors) => successors .iter() - .find_map(|jt| { - if jt.destination == block_info.block_id { - Some(jt.args) + .find_map(|successor| { + if successor.destination == block_info.block_id { + Some(successor.args) } else { None } @@ -1272,14 +1272,15 @@ fn min( w.retain(|o| liveness.is_live_after(&o.value, current_pp)); w.extend(results.iter().map(|v| Operand::new(*v, function))); } - BranchInfo::SingleDest(_, succ_args) => { + BranchInfo::SingleDest(successor) => { w.retain(|o| { - succ_args.contains(&o.value) || liveness.is_live_after(&o.value, current_pp) + successor.args.contains(&o.value) + || liveness.is_live_after(&o.value, current_pp) }); } - BranchInfo::MultiDest(jts) => { + BranchInfo::MultiDest(successors) => { w.retain(|o| { - let is_succ_arg = jts.iter().any(|jt| jt.args.contains(&o.value)); + let is_succ_arg = successors.iter().any(|s| s.args.contains(&o.value)); is_succ_arg || liveness.is_live_after(&o.value, current_pp) }); } diff --git a/hir-analysis/src/validation/block.rs b/hir-analysis/src/validation/block.rs index 010526eae..1f0234abe 100644 --- a/hir-analysis/src/validation/block.rs +++ b/hir-analysis/src/validation/block.rs @@ -1,9 +1,11 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use midenc_hir::*; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Report, Severity, Spanned}, + *, +}; use rustc_hash::FxHashSet; use smallvec::SmallVec; -use super::{Rule, ValidationError}; +use super::Rule; use crate::DominatorTree; /// This validation rule ensures that all values definitions dominate their uses. @@ -27,7 +29,7 @@ impl<'a> Rule for DefsDominateUses<'a> { &mut self, block_data: &BlockData, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { let current_block = block_data.id; let mut uses = FxHashSet::::default(); let mut defs = FxHashSet::::default(); @@ -47,27 +49,31 @@ impl<'a> Rule for DefsDominateUses<'a> { uses.extend(node.arguments(&self.dfg.value_lists).iter().copied()); match node.analyze_branch(&self.dfg.value_lists) { BranchInfo::NotABranch => (), - BranchInfo::SingleDest(_, args) => { - uses.extend(args.iter().copied()); + BranchInfo::SingleDest(info) => { + uses.extend(info.args.iter().copied()); } - BranchInfo::MultiDest(ref jts) => { - for jt in jts.iter() { - uses.extend(jt.args.iter().copied()); + BranchInfo::MultiDest(ref infos) => { + for info in infos.iter() { + uses.extend(info.args.iter().copied()); } } } // Make sure there are no uses of the instructions own results if !defs.is_disjoint(&uses) { - invalid_instruction!( - diagnostics, - node.key, - span, - "an instruction may not use its own results as arguments", - "This situation can only arise if one has manually modified the arguments of \ - an instruction, incorrectly inserting a value obtained from the set of \ - instruction results." - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + "an instruction may not use its own results as arguments", + ) + .with_help( + "This situation can only arise if one has manually modified the arguments \ + of an instruction, incorrectly inserting a value obtained from the set \ + of instruction results.", + ) + .into_report()); } // Next, ensure that all used values are dominated by their definition @@ -96,16 +102,20 @@ impl<'a> Rule for DefsDominateUses<'a> { // If we reach here, the use of `value` is not dominated by its definition, // so this use is invalid - invalid_instruction!( - diagnostics, - node.key, - span, - "an argument of this instruction, {value}, is not defined on all paths \ - leading to this point", - "All uses of a value must be dominated by its definition, i.e. all control \ - flow paths from the function entry to the point of each use must flow \ - through the point where that value is defined." - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + "an argument of this instruction, {value}, is not defined on all paths \ + leading to this point", + ) + .with_help( + "All uses of a value must be dominated by its definition, i.e. all \ + control flow paths from the function entry to the point of each use must \ + flow through the point where that value is defined.", + ) + .into_report()); } } @@ -138,7 +148,7 @@ impl<'a> Rule for BlockValidator<'a> { &mut self, block_data: &BlockData, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { // Ignore blocks which are not attached to the function body if !self.dfg.is_block_linked(block_data.id) { return Ok(()); @@ -149,85 +159,90 @@ impl<'a> Rule for BlockValidator<'a> { let terminator = block_data.insts.back().get(); if terminator.is_none() { // This block is empty - invalid_block!( - diagnostics, - id, - self.span, - "block cannot be empty", - "Empty blocks are only valid when detached from the function body" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label(self.span, "block cannot be empty") + .with_help("Empty blocks are only valid when detached from the function body") + .into_report()); } let terminator = terminator.unwrap(); let op = terminator.opcode(); if !op.is_terminator() { - invalid_block!( - diagnostics, - id, - self.span, - "invalid block terminator", - format!( + // This block is empty + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label(self.span, "invalid block terminator") + .with_help(format!( "The last instruction in a block must be a terminator, but {id} ends with \ {op} which is not a valid terminator" - ) - ); + )) + .into_report()); } match terminator.analyze_branch(&self.dfg.value_lists) { - BranchInfo::SingleDest(destination, _) => { - if !self.dfg.is_block_linked(destination) { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "invalid successor", - format!( + BranchInfo::SingleDest(info) => { + if !self.dfg.is_block_linked(info.destination) { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label(terminator.span(), "invalid successor") + .with_help(format!( "A block reference is only valid if the referenced block is present \ in the function layout. {id} references {destination}, but the \ - latter is not in the layout" - ) - ); + latter is not in the layout", + destination = info.destination + )) + .into_report()); } } - BranchInfo::MultiDest(ref jts) => { - if jts.is_empty() { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "incomplete {op} instruction", - "This instruction normally has 2 or more successors, but none were given." - ); + BranchInfo::MultiDest(ref infos) => { + if infos.is_empty() { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label( + terminator.span(), + format!("incomplete '{op}' instruction"), + ) + .with_help( + "This instruction normally has 2 or more successors, but none were \ + given.", + ) + .into_report()); } let mut seen = SmallVec::<[Block; 4]>::default(); - for jt in jts.iter() { - let destination = jt.destination; + for info in infos.iter() { + let destination = info.destination; if !self.dfg.is_block_linked(destination) { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "invalid successor", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label(terminator.span(), "invalid successor") + .with_help(format!( "A block reference is only valid if the referenced block is \ present in the function layout. {id} references {destination}, \ but the latter is not in the layout" - ) - ); + )) + .into_report()); } if seen.contains(&destination) { - invalid_instruction!( - diagnostics, - terminator.key, - terminator.span(), - "invalid {op} instruction", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label( + terminator.span(), + format!("invalid '{op}' instruction"), + ) + .with_help(format!( "A given block may only be a successor along a single control \ flow path, but {id} uses {destination} as a successor for more \ than one path" - ) - ); + )) + .into_report()); } seen.push(destination); @@ -240,16 +255,15 @@ impl<'a> Rule for BlockValidator<'a> { for node in block_data.insts.iter() { let op = node.opcode(); if op.is_terminator() && node.key != terminator.key { - invalid_block!( - diagnostics, - id, - self.span, - "terminator found in middle of block", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid block") + .with_primary_label(self.span, "terminator found in middle of block") + .with_help(format!( "A block may only have a terminator instruction as the last instruction \ in the block, but {id} uses {op} before the end of the block" - ) - ); + )) + .into_report()); } } diff --git a/hir-analysis/src/validation/function.rs b/hir-analysis/src/validation/function.rs index 141a4ea67..2ad9262f7 100644 --- a/hir-analysis/src/validation/function.rs +++ b/hir-analysis/src/validation/function.rs @@ -1,9 +1,9 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use midenc_hir::*; - -use super::{ - BlockValidator, DefsDominateUses, NamingConventions, Rule, TypeCheck, ValidationError, +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Report, Severity, Spanned}, + *, }; + +use super::{BlockValidator, DefsDominateUses, NamingConventions, Rule, TypeCheck}; use crate::{ControlFlowGraph, DominatorTree}; /// This validation rule ensures that function-local invariants are upheld: @@ -26,7 +26,7 @@ impl Rule for FunctionValidator { &mut self, function: &Function, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { // Validate the function declaration let mut rules = NamingConventions.chain(CoherentSignature::new(self.in_kernel_module)); rules.validate(function, diagnostics)?; @@ -76,18 +76,21 @@ impl Rule for CoherentSignature { &mut self, function: &Function, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { let span = function.id.span(); // 1 let linkage = function.signature.linkage; if !matches!(linkage, Linkage::External | Linkage::Internal) { - invalid_function!( - diagnostics, - function.id, - "the signature of this function specifies '{linkage}' linkage, but only \ - 'external' or 'internal' are valid" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "the signature of this function specifies '{linkage}' linkage, but only \ + 'external' or 'internal' are valid", + ) + .into_report()); } // 2 @@ -96,34 +99,48 @@ impl Rule for CoherentSignature { if self.in_kernel_module { let is_public = function.signature.is_public(); if is_public && !is_kernel_function { - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "the '{cc}' calling convention may only be used with 'internal' linkage in \ - kernel modules", - "This function is declared with 'external' linkage in a kernel module, so it \ - must use the 'kernel' calling convention" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + format!( + "the '{cc}' calling convention may only be used with 'internal' \ + linkage in kernel modules", + ), + ) + .with_help( + "This function is declared with 'external' linkage in a kernel module, so \ + it must use the 'kernel' calling convention", + ) + .into_report()); } else if !is_public && is_kernel_function { - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "the 'kernel' calling convention may only be used with 'external' linkage", - "This function has 'internal' linkage, so it must either be made 'external', \ - or a different calling convention must be used" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "the 'kernel' calling convention may only be used with 'external' linkage", + ) + .with_help( + "This function has 'internal' linkage, so it must either be made \ + 'external', or a different calling convention must be used", + ) + .into_report()); } } else if is_kernel_function { - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "the 'kernel' calling convention may only be used in kernel modules", - "Kernel functions may only be declared in kernel modules, so you must either \ - change the module type, or change the calling convention of this function" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "the 'kernel' calling convention may only be used in kernel modules", + ) + .with_help( + "Kernel functions may only be declared in kernel modules, so you must either \ + change the module type, or change the calling convention of this function", + ) + .into_report()); } // 3 @@ -147,15 +164,19 @@ impl Rule for CoherentSignature { let mut effective_stack_usage = 0; let params = function.dfg.block_args(function.dfg.entry_block()); if params.len() != function.signature.arity() { - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "function signature and entry block have different arities", - "This happens if the signature or entry block are modified without updating the \ - other, make sure the number and types of all parameters are the same in both the \ - signature and the entry block" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "function signature and entry block have different arities", + ) + .with_help( + "This happens if the signature or entry block are modified without updating \ + the other, make sure the number and types of all parameters are the same in \ + both the signature and the entry block", + ) + .into_report()); } for (i, param) in function.signature.params.iter().enumerate() { let is_first = i == 0; @@ -165,40 +186,50 @@ impl Rule for CoherentSignature { let value_ty = function.dfg.value_type(value); if param_ty != value_ty { - invalid_function!( - diagnostics, - function.id, - span, - "parameter type mismatch between signature and entry block", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "parameter type mismatch between signature and entry block", + ) + .with_help(format!( "The function declares this parameter as having type {param_ty}, but the \ actual type is {value_ty}" - ) - ); + )) + .into_report()); } let is_integer = param_ty.is_integer(); let is_signed_integer = param_ty.is_signed_integer(); match param.extension { ArgumentExtension::Zext if is_signed_integer => { - invalid_function!( - diagnostics, - function.id, - span, - "signed integer parameters may not be combined with zero-extension", - "Zero-extending a signed-integer loses the signedness, you should use \ - signed-extension instead" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "signed integer parameters may not be combined with zero-extension", + ) + .with_help( + "Zero-extending a signed-integer loses the signedness, you should use \ + signed-extension instead", + ) + .into_report()); } ArgumentExtension::Sext | ArgumentExtension::Zext if !is_integer => { - invalid_function!( - diagnostics, - function.id, - span, - "non-integer parameters may not be combined with argument extension \ - attributes", - "Argument extension has no meaning for types other than integers" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "non-integer parameters may not be combined with argument extension \ + attributes", + ) + .with_help( + "Argument extension has no meaning for types other than integers", + ) + .into_report()); } _ => (), } @@ -210,69 +241,85 @@ impl Rule for CoherentSignature { } if is_kernel_function && (is_sret || is_pointer) { - invalid_function!( - diagnostics, - function.id, - span, - "functions using the 'kernel' calling convention may not use sret or \ - pointer-typed parameters", - "Kernel functions are invoked in a different memory context, so they may not \ - pass or return values by reference" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "functions using the 'kernel' calling convention may not use sret or \ + pointer-typed parameters", + ) + .with_help( + "Kernel functions are invoked in a different memory context, so they may \ + not pass or return values by reference", + ) + .into_report()); } if !is_kernel_function { if is_sret { if sret_count > 1 || !is_first { - invalid_function!( - diagnostics, - function.id, - span, - "a function may only have a single sret parameter, and it must be the \ - first parameter", - "The sret parameter type is used to return a large value from a \ - function, but it may only be used for functions with a single return \ - value" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "a function may only have a single sret parameter, and it must be \ + the first parameter", + ) + .with_help( + "The sret parameter type is used to return a large value from a \ + function, but it may only be used for functions with a single \ + return value", + ) + .into_report()); } if !is_pointer { - invalid_function!( - diagnostics, - function.id, - span, - "sret parameters must be pointer-typed, but got {param_ty}", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "sret parameters must be pointer-typed, but got {param_ty}", + ) + .with_help(format!( "Did you mean to define this parameter with type {}?", &Type::Ptr(Box::new(param_ty.clone())) - ) - ); + )) + .into_report()); } if !function.signature.results.is_empty() { - invalid_function!( - diagnostics, - function.id, - span, - "functions with an sret parameter must have no results", - "An sret parameter is used in place of normal return values, but this \ - function uses both, which is not valid. You should remove the \ - results from the function signature." - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "functions with an sret parameter must have no results", + ) + .with_help( + "An sret parameter is used in place of normal return values, but \ + this function uses both, which is not valid. You should remove \ + the results from the function signature.", + ) + .into_report()); } } let size_in_bytes = param_ty.size_in_bytes(); if !is_pointer && size_in_bytes > 8 { - invalid_function!( - diagnostics, - function.id, - span, - "this parameter type is too large to pass by value", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "this parameter type is too large to pass by value", + ) + .with_help(format!( "This parameter has type {param_ty}, you must refactor this function \ to pass it by reference instead" - ) - ); + )) + .into_report()); } } @@ -281,49 +328,59 @@ impl Rule for CoherentSignature { } if effective_stack_usage > 16 { - invalid_function!( - diagnostics, - function.id, - span, - "this function has a signature with too many parameters", - "Due to the constraints of the Miden VM, all function parameters must fit on the \ - operand stack, which is 16 elements (each of which is effectively 4 bytes, a \ - maximum of 64 bytes). The layout of the parameter list of this function requires \ - more than this limit. You should either remove parameters, or combine some of \ - them into a struct which is then passed by reference." - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label(span, "this function has a signature with too many parameters") + .with_help( + "Due to the constraints of the Miden VM, all function parameters must fit on \ + the operand stack, which is 16 elements (each of which is effectively 4 \ + bytes, a maximum of 64 bytes). The layout of the parameter list of this \ + function requires more than this limit. You should either remove parameters, \ + or combine some of them into a struct which is then passed by reference.", + ) + .into_report()); } for (i, result) in function.signature.results.iter().enumerate() { if result.purpose == ArgumentPurpose::StructReturn { - invalid_function!( - diagnostics, - function.id, - "the sret attribute is only permitted on function parameters" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "the sret attribute is only permitted on function parameters", + ) + .into_report()); } if result.extension != ArgumentExtension::None { - invalid_function!( - diagnostics, - function.id, - "the argument extension attributes are only permitted on function parameters" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + span, + "the argument extension attributes are only permitted on function \ + parameters", + ) + .into_report()); } let size_in_bytes = result.ty.size_in_bytes(); if !result.ty.is_pointer() && size_in_bytes > 8 { - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "This function specifies a result type which is too large to pass by value", - format!( + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function signature") + .with_primary_label( + function.id.span(), + "This function specifies a result type which is too large to pass by value", + ) + .with_help(format!( "The parameter at index {} has type {}, you must refactor this function \ to pass it by reference instead", i, &result.ty - ) - ); + )) + .into_report()); } } diff --git a/hir-analysis/src/validation/mod.rs b/hir-analysis/src/validation/mod.rs index 43ab33727..59fa947f6 100644 --- a/hir-analysis/src/validation/mod.rs +++ b/hir-analysis/src/validation/mod.rs @@ -1,180 +1,15 @@ -macro_rules! bug { - ($diagnostics:ident, $msg:literal) => {{ - diagnostic!($diagnostics, Severity::Bug, $msg); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr) => {{ - diagnostic!($diagnostics, Severity::Bug, $msg, $span, $label); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr, $note:expr) => {{ - diagnostic!($diagnostics, Severity::Bug, $msg, $span, $label, $note); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr, $span2:expr, $label2:expr) => {{ - diagnostic!($diagnostics, Severity::Bug, $msg, $span, $label, $span2, $label2); - }}; -} - -macro_rules! error { - ($diagnostics:ident, $msg:literal) => {{ - diagnostic!($diagnostics, Severity::Error, $msg); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr) => {{ - diagnostic!($diagnostics, Severity::Error, $msg, $span, $label); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr, $note:expr) => {{ - diagnostic!($diagnostics, Severity::Error, $msg, $span, $label, $note); - }}; - - ($diagnostics:ident, $msg:literal, $span:expr, $label:expr, $span2:expr, $label2:expr) => {{ - diagnostic!($diagnostics, Severity::Error, $msg, $span, $label, $span2, $label2); - }}; -} - -macro_rules! invalid_instruction { - ($diagnostics:ident, $inst:expr, $span:expr, $label:expr) => {{ - let span = $span; - let reason = format!($label); - bug!($diagnostics, "invalid instruction", span, reason.as_str()); - return Err(crate::validation::ValidationError::InvalidInstruction { - span, - inst: $inst, - reason, - }); - }}; - - ($diagnostics:ident, $inst:expr, $span:expr, $label:expr, $note:expr) => {{ - let span = $span; - let reason = format!($label); - bug!($diagnostics, "invalid instruction", span, reason.as_str(), $note); - return Err(crate::validation::ValidationError::InvalidInstruction { - span, - inst: $inst, - reason, - }); - }}; -} - -macro_rules! invalid_block { - ($diagnostics:ident, $block:expr, $span:expr, $label:expr) => {{ - let reason = format!($label); - bug!($diagnostics, "invalid block", $span, reason.as_str()); - return Err(crate::validation::ValidationError::InvalidBlock { - block: $block, - reason, - }); - }}; - - ($diagnostics:ident, $block:expr, $span:expr, $label:expr, $note:expr) => {{ - let reason = format!($label); - bug!($diagnostics, "invalid block", $span, reason.as_str(), $note); - return Err(crate::validation::ValidationError::InvalidBlock { - block: $block, - reason, - }); - }}; -} - -macro_rules! invalid_module { - ($diagnostics:ident, $module:expr, $label:expr) => {{ - invalid_module!($diagnostics, $module, $module.span(), $label); - }}; - - ($diagnostics:ident, $module:expr, $span:expr, $label:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid module", span, reason.as_str()); - return Err(crate::validation::ValidationError::InvalidModule { - module: $module, - reason, - }); - }}; - - ($diagnostics:ident, $module:expr, $span:expr, $label:expr, $note:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid module", span, reason.as_str(), $note); - return Err(crate::validation::ValidationError::InvalidModule { - module: $module, - reason, - }); - }}; -} - -macro_rules! invalid_function { - ($diagnostics:ident, $function:expr, $label:expr) => {{ - invalid_function!($diagnostics, $function, $function.span(), $label); - }}; - - ($diagnostics:ident, $function:expr, $span:expr, $label:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid function", span, reason.as_str()); - return Err(crate::validation::ValidationError::InvalidFunction { - function: $function, - reason, - }); - }}; - - ($diagnostics:ident, $function:expr, $span:expr, $label:expr, $note:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid function", span, reason.as_str(), $note); - return Err(crate::validation::ValidationError::InvalidFunction { - function: $function, - reason, - }); - }}; - - ($diagnostics:ident, $function:expr, $span:expr, $label:expr, $span2:expr, $label2:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid function", span, reason.as_str()); - $diagnostics - .diagnostic(miden_diagnostics::Severity::Error) - .with_message("invalid function") - .with_primary_label(span, reason.as_str()) - .with_secondary_label($span2, $label2) - .emit(); - return Err(crate::validation::ValidationError::InvalidFunction { - function: $function, - reason, - }); - }}; -} - -macro_rules! invalid_global { - ($diagnostics:ident, $name:expr, $label:expr) => {{ - invalid_global!($diagnostics, $name, $name.span(), $label); - }}; - - ($diagnostics:ident, $name:expr, $span:expr, $label:expr) => {{ - let span = $span; - let reason = format!($label); - error!($diagnostics, "invalid global variable", span, reason.as_str()); - return Err(crate::validation::ValidationError::InvalidGlobalVariable { - name: $name, - reason, - }); - }}; -} - mod block; mod function; mod naming; mod typecheck; -use miden_diagnostics::DiagnosticsHandler; use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Report}, pass::{Analysis, AnalysisManager, AnalysisResult}, *, }; use midenc_session::Session; -pub use self::typecheck::TypeError; use self::{ block::{BlockValidator, DefsDominateUses}, function::FunctionValidator, @@ -182,101 +17,6 @@ use self::{ typecheck::TypeCheck, }; -/// This error is produced by validation rules run against the IR -#[derive(Debug, thiserror::Error)] -pub enum ValidationError { - /// A validation rule indicates a module is invalid - #[error("invalid module '{module}': {reason}")] - InvalidModule { module: Ident, reason: String }, - /// A validation rule indicates a global variable is invalid - #[error("invalid global variable '{name}': {reason}")] - InvalidGlobalVariable { name: Ident, reason: String }, - /// A validation rule indicates a function is invalid - #[error("invalid function '{function}': {reason}")] - InvalidFunction { - function: FunctionIdent, - reason: String, - }, - /// A validation rule indicates a block is invalid - #[error("invalid block '{block}': {reason}")] - InvalidBlock { block: Block, reason: String }, - /// A validation rule indicates an instruction is invalid - #[error("invalid instruction '{inst}': {reason}")] - InvalidInstruction { - span: SourceSpan, - inst: Inst, - reason: String, - }, - /// A type error was found - #[error("type error: {0}")] - TypeError(#[from] TypeError), - /// An unknown validation error occurred - #[error(transparent)] - Failed(#[from] anyhow::Error), -} -#[cfg(test)] -impl PartialEq for ValidationError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - ( - Self::InvalidModule { - module: am, - reason: ar, - }, - Self::InvalidModule { - module: bm, - reason: br, - }, - ) => am == bm && ar == br, - ( - Self::InvalidGlobalVariable { - name: an, - reason: ar, - }, - Self::InvalidGlobalVariable { - name: bn, - reason: br, - }, - ) => an == bn && ar == br, - ( - Self::InvalidFunction { - function: af, - reason: ar, - }, - Self::InvalidFunction { - function: bf, - reason: br, - }, - ) => af == bf && ar == br, - ( - Self::InvalidBlock { - block: ab, - reason: ar, - }, - Self::InvalidBlock { - block: bb, - reason: br, - }, - ) => ab == bb && ar == br, - ( - Self::InvalidInstruction { - inst: ai, - reason: ar, - .. - }, - Self::InvalidInstruction { - inst: bi, - reason: br, - .. - }, - ) => ai == bi && ar == br, - (Self::TypeError(a), Self::TypeError(b)) => a == b, - (Self::Failed(a), Self::Failed(b)) => a.to_string() == b.to_string(), - (..) => false, - } - } -} - inventory::submit! { midenc_session::CompileFlag::new("validate") .long("no-validate") @@ -288,11 +28,7 @@ inventory::submit! { /// A [Rule] validates some specific type of behavior on an item of type `T` pub trait Rule { /// Validate `item`, using `diagnostics` to emit relevant diagnostics. - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError>; + fn validate(&mut self, item: &T, diagnostics: &DiagnosticsHandler) -> Result<(), Report>; /// Combine two rules into one rule fn chain(self, rule: R) -> RuleSet @@ -307,11 +43,7 @@ impl Rule for &mut R where R: Rule, { - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + fn validate(&mut self, item: &T, diagnostics: &DiagnosticsHandler) -> Result<(), Report> { (*self).validate(item, diagnostics) } } @@ -319,21 +51,13 @@ impl Rule for Box where R: Rule, { - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + fn validate(&mut self, item: &T, diagnostics: &DiagnosticsHandler) -> Result<(), Report> { (**self).validate(item, diagnostics) } } -impl Rule for dyn FnMut(&T, &DiagnosticsHandler) -> Result<(), ValidationError> { +impl Rule for dyn FnMut(&T, &DiagnosticsHandler) -> Result<(), Report> { #[inline] - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + fn validate(&mut self, item: &T, diagnostics: &DiagnosticsHandler) -> Result<(), Report> { self(item, diagnostics) } } @@ -369,11 +93,7 @@ where A: Rule, B: Rule, { - fn validate( - &mut self, - item: &T, - diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + fn validate(&mut self, item: &T, diagnostics: &DiagnosticsHandler) -> Result<(), Report> { self.a .validate(item, diagnostics) .and_then(|_| self.b.validate(item, diagnostics)) @@ -384,7 +104,7 @@ where /// /// This validates all rules which apply to items at/within module scope. #[derive(PassInfo)] -pub struct ModuleValidationAnalysis(Result<(), ValidationError>); +pub struct ModuleValidationAnalysis(Result<(), Report>); impl Analysis for ModuleValidationAnalysis { type Entity = Module; @@ -397,15 +117,11 @@ impl Analysis for ModuleValidationAnalysis { return Ok(Self(Ok(()))); } - match Self::validate(module, session) { - // If an unexpected error occurs, treat it as a failure of the pass itself - Err(ValidationError::Failed(err)) => Err(err.into()), - result => Ok(Self(result)), - } + Ok(Self(Self::validate(module, session))) } } impl ModuleValidationAnalysis { - fn validate(module: &Module, session: &Session) -> Result<(), ValidationError> { + fn validate(module: &Module, session: &Session) -> Result<(), Report> { // Apply module-scoped rules let mut rules = NamingConventions; rules.validate(module, &session.diagnostics)?; @@ -425,7 +141,7 @@ impl ModuleValidationAnalysis { Ok(()) } } -impl From for Result<(), ValidationError> { +impl From for Result<(), Report> { fn from(analysis: ModuleValidationAnalysis) -> Self { analysis.0 } diff --git a/hir-analysis/src/validation/naming.rs b/hir-analysis/src/validation/naming.rs index b1078e041..66800a71d 100644 --- a/hir-analysis/src/validation/naming.rs +++ b/hir-analysis/src/validation/naming.rs @@ -1,7 +1,9 @@ -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use midenc_hir::*; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Report, Severity, Spanned}, + *, +}; -use super::{Rule, ValidationError}; +use super::Rule; /// This validation rule ensures that all identifiers adhere to the rules of their respective items. pub struct NamingConventions; @@ -10,7 +12,7 @@ impl Rule for NamingConventions { &mut self, module: &Module, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { // Make sure all functions in this module have the same module name in their id for function in module.functions() { let id = function.id; @@ -19,30 +21,43 @@ impl Rule for NamingConventions { module: module.name, function: id.function, }; - invalid_function!( - diagnostics, - function.id, - function.id.span(), - "the fully-qualified name of this function is '{id}'", - module.name.span(), - format!("but we expected '{expected_name}' because it belongs to this module") - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function name") + .with_primary_label( + function.id.span(), + format!("the fully-qualified name of this function is '{id}'"), + ) + .with_secondary_label( + module.name.span(), + format!( + "but we expected '{expected_name}' because it belongs to this module" + ), + ) + .into_report()); } } // 1. Must not be empty let name = module.name.as_str(); if name.is_empty() { - invalid_module!(diagnostics, module.name, "module name cannot be empty"); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module name") + .with_primary_label(module.name.span, "module name cannot be empty") + .into_report()); } // 2. Must begin with a lowercase ASCII alphabetic character if !name.starts_with(is_lower_ascii_alphabetic) { - invalid_module!( - diagnostics, - module.name, - "module name must start with a lowercase, ascii-alphabetic character" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module name") + .with_primary_label( + module.name.span(), + "module name must start with a lowercase, ascii-alphabetic character", + ) + .into_report()); } // 3. May otherwise consist of any number of characters of the following classes: @@ -54,6 +69,7 @@ impl Rule for NamingConventions { let mut char_indices = name.char_indices().peekable(); let mut is_namespaced = false; while let Some((offset, c)) = char_indices.next() { + let offset = offset as u32; match c { c if c.is_ascii_alphanumeric() => continue, '_' | '-' | '+' | '$' | '@' => continue, @@ -64,33 +80,39 @@ impl Rule for NamingConventions { continue; } _ => { - let pos = module.name.span().start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_module!( - diagnostics, - module.name, - span, - "module name contains invalid character ':'", - "Did you mean to use the namespacing operator '::'?" - ); + let module_name_span = module.name.span(); + let source_id = module_name_span.source_id(); + let pos = module_name_span.start() + offset; + let span = SourceSpan::at(source_id, pos); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module name") + .with_primary_label(span, "module name contains invalid character ':'") + .with_help("Did you mean to use the namespacing operator '::'?") + .into_report()); } }, c if c.is_whitespace() => { - invalid_module!( - diagnostics, - module.name, - "module names may not contain whitespace" - ); + let module_name_span = module.name.span(); + let source_id = module_name_span.source_id(); + let pos = module_name_span.start() + offset; + let span = SourceSpan::at(source_id, pos); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module name") + .with_primary_label(span, "module names may not contain whitespace") + .into_report()); } c => { - let pos = module.name.span().start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_module!( - diagnostics, - module.name, - span, - "{c} is not valid in module names" - ); + let module_name_span = module.name.span(); + let source_id = module_name_span.source_id(); + let pos = module_name_span.start() + offset; + let span = SourceSpan::at(source_id, pos); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module name") + .with_primary_label(span, format!("'{c}' is not valid in module names")) + .into_report()); } } } @@ -98,27 +120,31 @@ impl Rule for NamingConventions { // 5. The namespacing operator may only appear between two valid module identifiers // 6. Namespaced module names must adhere to the above rules in each submodule identifier if is_namespaced { - let mut offset = 0; + let mut offset = 0u32; for component in name.split("::") { - let len = component.as_bytes().len(); - let start = module.name.span().start() + offset; - let span = SourceSpan::new(start, start + len); + let len = component.as_bytes().len() as u32; + let module_name_span = module.name.span(); + let source_id = module_name_span.source_id(); + let start = module_name_span.start() + offset; + let span = SourceSpan::new(source_id, start..(start + len)); if component.is_empty() { - invalid_module!( - diagnostics, - module.name, - span, - "submodule names cannot be empty" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module namespace") + .with_primary_label(span, "submodule names cannot be empty") + .into_report()); } if !name.starts_with(is_lower_ascii_alphabetic) { - invalid_module!( - diagnostics, - module.name, - span, - "submodule name must start with a lowercase, ascii-alphabetic character" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid module namespace") + .with_primary_label( + span, + "submodule name must start with a lowercase, ascii-alphabetic \ + character", + ) + .into_report()); } offset += len + 2; @@ -133,13 +159,17 @@ impl Rule for NamingConventions { &mut self, function: &Function, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { let name = function.id.function.as_str(); let span = function.id.function.span(); // 1. Must not be empty if name.is_empty() { - invalid_function!(diagnostics, function.id, "function names cannot be empty"); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function name") + .with_primary_label(span, "function names cannot be empty") + .into_report()); } // 2. Must start with an ASCII-alphabetic character, underscore, `$` or `@` @@ -149,25 +179,29 @@ impl Rule for NamingConventions { // 3. Otherwise, no restrictions, but may not contain whitespace if let Err((offset, c)) = is_valid_identifier(name, name_starts_with, char::is_whitespace) { + let offset = offset as u32; if c.is_whitespace() { + let source_id = span.source_id(); let pos = span.start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_function!( - diagnostics, - function.id, - span, - "function names may not contain whitespace" - ); + let span = SourceSpan::at(source_id, pos); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function name") + .with_primary_label(span, "function names may not contain whitespace") + .into_report()); } else { debug_assert_eq!(offset, 0); - let span = SourceSpan::new(span.start(), span.start()); - invalid_function!( - diagnostics, - function.id, - span, - "function names must start with an ascii-alphabetic character, '_', '$', or \ - '@'" - ); + let source_id = span.source_id(); + let span = SourceSpan::at(source_id, span.start()); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid function name") + .with_primary_label( + span, + "function names must start with an ascii-alphabetic character, '_', '$', \ + or '@'", + ) + .into_report()); } } @@ -179,13 +213,17 @@ impl Rule for NamingConventions { &mut self, global: &GlobalVariableData, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { let span = global.name.span(); let name = global.name.as_str(); // 1. Must not be empty if name.is_empty() { - invalid_global!(diagnostics, global.name, "global variable names cannot be empty"); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid global variable name") + .with_primary_label(span, "global variable names cannot be empty") + .into_report()); } // 2. Must start with an ASCII-alphabetic character, underscore, `.`, `$` or `@` @@ -195,25 +233,29 @@ impl Rule for NamingConventions { // 3. Otherwise, no restrictions, but may not contain whitespace if let Err((offset, c)) = is_valid_identifier(name, name_starts_with, char::is_whitespace) { + let offset = offset as u32; if c.is_whitespace() { + let source_id = span.source_id(); let pos = span.start() + offset; - let span = SourceSpan::new(pos, pos); - invalid_global!( - diagnostics, - global.name, - span, - "global variable names may not contain whitespace" - ); + let span = SourceSpan::at(source_id, pos); + + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid global variable name") + .with_primary_label(span, "global variable names may not contain whitespace") + .into_report()); } else { debug_assert_eq!(offset, 0); - let span = SourceSpan::new(span.start(), span.start()); - invalid_global!( - diagnostics, - global.name, - span, - "global variable names must start with an ascii-alphabetic character, '_', \ - '.', '$', or '@'" - ); + let span = SourceSpan::at(span.source_id(), span.start()); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid global variable name") + .with_primary_label( + span, + "global variable names must start with an ascii-alphabetic character, \ + '_', '.', '$', or '@'", + ) + .into_report()); } } diff --git a/hir-analysis/src/validation/typecheck.rs b/hir-analysis/src/validation/typecheck.rs index 89a08f8eb..b31e55808 100644 --- a/hir-analysis/src/validation/typecheck.rs +++ b/hir-analysis/src/validation/typecheck.rs @@ -1,13 +1,15 @@ +use alloc::collections::BTreeMap; use core::fmt; -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; -use midenc_hir::*; -use rustc_hash::FxHashMap; +use midenc_hir::{ + diagnostics::{DiagnosticsHandler, Report, Severity, Spanned}, + *, +}; -use super::{Rule, ValidationError}; +use super::Rule; /// This error is produced when type checking the IR for function or module -#[derive(Debug, thiserror::Error, PartialEq, Eq)] +#[derive(Debug, thiserror::Error)] pub enum TypeError { /// The number of arguments given does not match what is expected by the instruction #[error("expected {expected} arguments, but {actual} are given")] @@ -29,22 +31,6 @@ pub enum TypeError { actual: Type, index: usize, }, - /// The number of arguments given to a successor block does not match what is expected by the - /// block - #[error("{successor} expected {expected} arguments, but {actual} are given")] - IncorrectSuccessorArgumentCount { - successor: Block, - expected: usize, - actual: usize, - }, - /// One of the arguments to a successor block is not of the correct type - #[error("{successor} expected argument of {expected} type at index {index}, got {actual}")] - IncorrectSuccessorArgumentType { - successor: Block, - expected: Type, - actual: Type, - index: usize, - }, /// An attempt was made to cast from a larger integer type to a smaller one via widening cast, /// e.g. `zext` #[error("expected result to be an integral type larger than {expected}, but got {actual}")] @@ -86,7 +72,7 @@ impl<'a> Rule for TypeCheck<'a> { &mut self, block_data: &BlockData, diagnostics: &DiagnosticsHandler, - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { // Traverse the block, checking each instruction in turn for node in block_data.insts.iter() { let span = node.span(); @@ -106,36 +92,51 @@ impl<'a> Rule for TypeCheck<'a> { | Opcode::ImmU64 | Opcode::ImmI64 | Opcode::ImmFelt - | Opcode::ImmF64 => invalid_instruction!( - diagnostics, - node.key, - span, - "immediate opcode '{opcode}' cannot be used with non-immediate argument" - ), + | Opcode::ImmF64 => { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "immediate opcode '{opcode}' cannot be used with \ + non-immediate argument" + ), + ) + .into_report()); + } _ => { typechecker.check(&[*arg], results)?; } }, Instruction::UnaryOpImm(UnaryOpImm { imm, .. }) => match opcode { - Opcode::PtrToInt => invalid_instruction!( - diagnostics, - node.key, - span, - "'{opcode}' cannot be used with an immediate value" - ), + Opcode::PtrToInt => { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!("'{opcode}' cannot be used with an immediate value"), + ) + .into_report()); + } _ => { typechecker.check_immediate(&[], imm, results)?; } }, Instruction::Load(LoadOp { ref ty, addr, .. }) => { if ty.size_in_felts() > 4 { - invalid_instruction!( - diagnostics, - node.key, - span, - "cannot load a value of type {ty} on the stack, as it is larger than \ - 16 bytes" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "cannot load a value of type {ty} on the stack, as it is \ + larger than 16 bytes" + ), + ) + .into_report()); } typechecker.check(&[*addr], results)?; } @@ -153,60 +154,80 @@ impl<'a> Rule for TypeCheck<'a> { let expected_ty = self.dfg.local_type(*local); let actual_ty = self.dfg.value_type(args[0]); if actual_ty != expected_ty { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentType { - expected: expected_ty.clone().into(), - actual: actual_ty.clone(), - index: 0, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "local type is {expected_ty}, but argument is \ + {actual_ty}" + ), + ) + .into_report()); } typechecker.check(args, results)?; } Opcode::Load => { if !args.is_empty() { - invalid_instruction!( - diagnostics, - node.key, - span, - "local.load does not accept any arguments" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + "local.load does not accept any arguments", + ) + .into_report()); } if results.len() != 1 { - invalid_instruction!( - diagnostics, - node.key, - span, - "local.load should have exactly one result" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + "local.load should have exactly one result", + ) + .into_report()); } let local_ty = self.dfg.local_type(*local); if local_ty.size_in_felts() > 4 { - invalid_instruction!( - diagnostics, - node.key, - span, - "cannot load a value of type {local_ty} on the stack, as it \ - is larger than 16 bytes" - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + "cannot load a value of type {local_ty} on the stack, as \ + it is larger than 16 bytes", + ) + .into_report()); } let result_ty = self.dfg.value_type(results[0]); if local_ty != result_ty { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentType { - expected: local_ty.clone().into(), - actual: result_ty.clone(), - index: 0, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "local type is {local_ty}, but result of load is \ + {result_ty}" + ), + ) + .into_report()); } } - opcode => invalid_instruction!( - diagnostics, - node.key, - span, - "opcode '{opcode}' cannot be used with local variables" - ), + opcode => { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "opcode '{opcode}' cannot be used with local variables" + ), + ) + .into_report()); + } } } Instruction::GlobalValue(_) @@ -221,63 +242,95 @@ impl<'a> Rule for TypeCheck<'a> { Instruction::Ret(Ret { ref args, .. }) => { let args = args.as_slice(&self.dfg.value_lists); if args.len() != self.signature.results.len() { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentCount { - expected: self.signature.results.len(), - actual: args.len(), - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "the function signature states that {} results should be \ + returned, but {} were given", + self.signature.results.len(), + args.len() + ), + ) + .into_report()); } for (index, (expected, arg)) in self.signature.results.iter().zip(args.iter().copied()).enumerate() { let actual = self.dfg.value_type(arg); if actual != &expected.ty { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentType { - expected: expected.ty.clone().into(), - actual: actual.clone(), - index, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "result at index {index} is {actual}, but function \ + signature expects {}", + &expected.ty + ), + ) + .into_report()); } } } Instruction::RetImm(RetImm { ref arg, .. }) => { if self.signature.results.len() != 1 { - return Err(ValidationError::TypeError( - TypeError::IncorrectArgumentCount { - expected: self.signature.results.len(), - actual: 1, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "the function signature states that {} results should be \ + returned, but {} were given", + self.signature.results.len(), + 1 + ), + ) + .into_report()); } let expected = &self.signature.results[0].ty; let actual = arg.ty(); if &actual != expected { - return Err(ValidationError::TypeError(TypeError::IncorrectArgumentType { - expected: expected.clone().into(), - actual, - index: 0, - })); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "result is {actual}, but function signature expects {expected}" + ), + ) + .into_report()); } } Instruction::Br(Br { - ref args, - destination, + successor: + Successor { + destination, + ref args, + }, .. }) => { let successor = *destination; let expected = self.dfg.block_args(successor); let args = args.as_slice(&self.dfg.value_lists); if args.len() != expected.len() { - return Err(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentCount { - successor, - expected: expected.len(), - actual: args.len(), - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "{successor} expects {} arguments, but is being given {}", + expected.len(), + args.len() + ), + ) + .into_report()); } for (index, (param, arg)) in expected.iter().copied().zip(args.iter().copied()).enumerate() @@ -285,40 +338,45 @@ impl<'a> Rule for TypeCheck<'a> { let expected = self.dfg.value_type(param); let actual = self.dfg.value_type(arg); if actual != expected { - return Err(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentType { - successor, - expected: expected.clone(), - actual: actual.clone(), - index, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "{successor} argument at index {index} is expected to be \ + {expected}, but got {actual}" + ), + ) + .into_report()); } } } Instruction::CondBr(CondBr { cond, - then_dest: (then_dest, then_args), - else_dest: (else_dest, else_args), + ref then_dest, + ref else_dest, .. }) => { typechecker.check(&[*cond], results)?; - let then_dest = *then_dest; - let else_dest = *else_dest; - for (successor, dest_args) in - [(then_dest, then_args), (else_dest, else_args)].into_iter() - { - let expected = self.dfg.block_args(successor); - let args = dest_args.as_slice(&self.dfg.value_lists); + for successor in [then_dest, else_dest].into_iter() { + let expected = self.dfg.block_args(successor.destination); + let args = successor.args.as_slice(&self.dfg.value_lists); if args.len() != expected.len() { - return Err(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentCount { - successor, - expected: expected.len(), - actual: args.len(), - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "{successor} expects {} arguments, but is being given {}", + expected.len(), + args.len(), + successor = successor.destination, + ), + ) + .into_report()); } for (index, (param, arg)) in expected.iter().copied().zip(args.iter().copied()).enumerate() @@ -326,14 +384,18 @@ impl<'a> Rule for TypeCheck<'a> { let expected = self.dfg.value_type(param); let actual = self.dfg.value_type(arg); if actual != expected { - return Err(ValidationError::TypeError( - TypeError::IncorrectSuccessorArgumentType { - successor, - expected: expected.clone(), - actual: actual.clone(), - index, - }, - )); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "{successor} argument at index {index} is expected to \ + be {expected}, but got {actual}", + successor = successor.destination + ), + ) + .into_report()); } } } @@ -346,45 +408,69 @@ impl<'a> Rule for TypeCheck<'a> { }) => { typechecker.check(&[*arg], results)?; - let mut seen = FxHashMap::::default(); - for (i, (key, successor)) in arms.iter().enumerate() { - if let Some(prev) = seen.insert(*key, i) { - return Err(ValidationError::InvalidInstruction { - span, - inst: node.key, - reason: format!( - "all arms of a 'switch' must have a unique discriminant, but \ - the arm at index {i} has the same discriminant as the arm at \ - {prev}" - ), - }); + let mut seen = BTreeMap::::default(); + for (i, arm) in arms.iter().enumerate() { + if let Some(prev) = seen.insert(arm.value, i) { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "all arms of a 'switch' must have a unique discriminant, \ + but the arm at index {i} has the same discriminant as \ + the arm at {prev}" + ), + ) + .into_report()); } + } - let expected = self.dfg.block_args(*successor); - if !expected.is_empty() { - return Err(ValidationError::InvalidInstruction { - span, - inst: node.key, - reason: format!( - "all successors of a 'switch' must not have block parameters, \ - but {successor}, the successor for discriminant {key}, has \ - {} arguments", - expected.len() - ), - }); + for (i, successor) in arms + .iter() + .map(|arm| &arm.successor) + .chain(core::iter::once(fallback)) + .enumerate() + { + let expected = self.dfg.block_args(successor.destination); + let args = successor.args.as_slice(&self.dfg.value_lists); + if args.len() != expected.len() { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!( + "the destination for the arm at index {i}, {successor}, \ + expects {} arguments, but is being given {}", + expected.len(), + args.len(), + successor = successor.destination, + ), + ) + .into_report()); + } + for (index, (param, arg)) in + expected.iter().copied().zip(args.iter().copied()).enumerate() + { + let expected = self.dfg.value_type(param); + let actual = self.dfg.value_type(arg); + if actual != expected { + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label( + span, + format!( + "invalid switch arm at index {i}: {successor} \ + argument at index {index} is expected to be \ + {expected}, but got {actual}", + successor = successor.destination + ), + ) + .into_report()); + } } - } - let expected = self.dfg.block_args(*fallback); - if !expected.is_empty() { - return Err(ValidationError::InvalidInstruction { - span, - inst: node.key, - reason: format!( - "all successors of a 'switch' must not have block parameters, but \ - {fallback}, the default successor, has {} arguments", - expected.len() - ), - }); } } } @@ -407,6 +493,7 @@ pub enum TypePattern { /// Matches any unsigned integer type Uint, /// Matches any signed integer type + #[allow(dead_code)] Sint, /// Matches any pointer type Pointer, @@ -1048,7 +1135,6 @@ struct InstTypeChecker<'a> { diagnostics: &'a DiagnosticsHandler, dfg: &'a DataFlowGraph, span: SourceSpan, - opcode: Opcode, pattern: InstPattern, } impl<'a> InstTypeChecker<'a> { @@ -1057,10 +1143,10 @@ impl<'a> InstTypeChecker<'a> { diagnostics: &'a DiagnosticsHandler, dfg: &'a DataFlowGraph, node: &InstNode, - ) -> Result { + ) -> Result { let span = node.span(); let opcode = node.opcode(); - let is_local_op = matches!(node.data.as_ref(), Instruction::LocalVar(_)); + let is_local_op = matches!(&*node.data, Instruction::LocalVar(_)); let pattern = match opcode { Opcode::Assert | Opcode::Assertz => InstPattern::UnaryNoResult(Type::I1.into()), Opcode::AssertEq => InstPattern::BinaryMatchingNoResult(Type::I1.into()), @@ -1164,13 +1250,17 @@ impl<'a> InstTypeChecker<'a> { .collect(); InstPattern::Exact(args, results) } else { - invalid_instruction!( - diagnostics, - node.key, - span, - "no signature is available for {callee}", - "Make sure you import functions before building calls to them." - ); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("invalid instruction") + .with_primary_label( + span, + format!("no signature is available for {callee}"), + ) + .with_help( + "Make sure you import functions before building calls to them.", + ) + .into_report()); } } inst => panic!("invalid opcode '{opcode}' for {inst:#?}"), @@ -1187,28 +1277,22 @@ impl<'a> InstTypeChecker<'a> { diagnostics, dfg, span: node.span(), - opcode, pattern, }) } /// Checks that the given `operands` and `results` match the types represented by this /// [InstTypeChecker] - pub fn check(self, operands: &[Value], results: &[Value]) -> Result<(), ValidationError> { + pub fn check(self, operands: &[Value], results: &[Value]) -> Result<(), Report> { let diagnostics = self.diagnostics; let dfg = self.dfg; match self.pattern.into_match(dfg, operands, results) { Ok(_) => Ok(()), - Err(err) => { - let opcode = self.opcode; - let message = format!("validation failed for {opcode} instruction"); - diagnostics - .diagnostic(Severity::Error) - .with_message(message.as_str()) - .with_primary_label(self.span, format!("{err}")) - .emit(); - Err(ValidationError::TypeError(err)) - } + Err(err) => Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label(self.span, err) + .into_report()), } } @@ -1219,21 +1303,16 @@ impl<'a> InstTypeChecker<'a> { operands: &[Value], imm: &Immediate, results: &[Value], - ) -> Result<(), ValidationError> { + ) -> Result<(), Report> { let diagnostics = self.diagnostics; let dfg = self.dfg; match self.pattern.into_match_with_immediate(dfg, operands, imm, results) { Ok(_) => Ok(()), - Err(err) => { - let opcode = self.opcode; - let message = format!("validation failed for {opcode} instruction"); - diagnostics - .diagnostic(Severity::Error) - .with_message(message.as_str()) - .with_primary_label(self.span, format!("{err}")) - .emit(); - Err(ValidationError::TypeError(err)) - } + Err(err) => Err(diagnostics + .diagnostic(Severity::Error) + .with_message("type error") + .with_primary_label(self.span, err) + .into_report()), } } } diff --git a/hir-macros/Cargo.toml b/hir-macros/Cargo.toml index 14e728b23..0044e1409 100644 --- a/hir-macros/Cargo.toml +++ b/hir-macros/Cargo.toml @@ -24,4 +24,4 @@ quote = "1.0" [dependencies.syn] version = "2.0" -features = ["full", "parsing", "derive"] +features = ["full", "parsing", "derive", "extra-traits", "printing"] diff --git a/hir-macros/src/lib.rs b/hir-macros/src/lib.rs index 0d51d9d61..f1f1491fc 100644 --- a/hir-macros/src/lib.rs +++ b/hir-macros/src/lib.rs @@ -1,6 +1,29 @@ +extern crate proc_macro; + +mod spanned; + use inflector::cases::kebabcase::to_kebab_case; use quote::quote; -use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident, Token}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Error, Ident, Token}; + +#[proc_macro_derive(Spanned, attributes(span))] +pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + // Parse into syntax tree + let derive = parse_macro_input!(input as DeriveInput); + // Structure name + let name = derive.ident; + let result = match derive.data { + Data::Struct(data) => spanned::derive_spanned_struct(name, data, derive.generics), + Data::Enum(data) => spanned::derive_spanned_enum(name, data, derive.generics), + Data::Union(_) => { + Err(Error::new(name.span(), "deriving Spanned on unions is not currently supported")) + } + }; + match result { + Ok(ts) => ts, + Err(err) => err.into_compile_error().into(), + } +} #[proc_macro_derive(PassInfo)] pub fn derive_pass_info(item: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -141,9 +164,71 @@ pub fn derive_analysis_key(item: proc_macro::TokenStream) -> proc_macro::TokenSt pub fn derive_rewrite_pass_registration(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let derive_input = parse_macro_input!(item as DeriveInput); let id = derive_input.ident.clone(); + let generics = derive_input.generics; + let mut params = syn::punctuated::Punctuated::<_, Token![,]>::new(); + for gp in generics.params.iter() { + match gp { + syn::GenericParam::Lifetime(ref lt) => { + if !lt.bounds.empty_or_trailing() { + return syn::Error::new( + gp.span(), + "cannot derive RewritePassRegistration on a type with lifetime bounds", + ) + .into_compile_error() + .into(); + } + params.push(syn::GenericArgument::Lifetime(syn::Lifetime { + apostrophe: lt.span(), + ident: Ident::new("_", lt.span()), + })); + } + syn::GenericParam::Type(ref ty) => { + if !ty.bounds.empty_or_trailing() { + return syn::Error::new( + gp.span(), + "cannot derive RewritePassRegistration on a generic type with type bounds", + ) + .into_compile_error() + .into(); + } + let param_ty: syn::Type = syn::parse_quote_spanned! { ty.span() => () }; + params.push(syn::GenericArgument::Type(param_ty)); + } + syn::GenericParam::Const(_) => { + return syn::Error::new( + gp.span(), + "cannot derive RewritePassRegistration on a generic type with const arguments", + ) + .into_compile_error() + .into(); + } + } + } - let quoted = quote! { - inventory::submit!(midenc_hir::pass::RewritePassRegistration::new::<#id>()); + let quoted = if params.empty_or_trailing() { + quote! { + inventory::submit!(midenc_hir::pass::RewritePassRegistration::new::<#id>()); + inventory::submit! { + midenc_session::CompileFlag::new(<#id as PassInfo>::FLAG) + .long(<#id as PassInfo>::FLAG) + .help(<#id as PassInfo>::SUMMARY) + .help_heading("Rewrites") + .action(midenc_session::FlagAction::SetTrue) + .hide(true) + } + } + } else { + quote! { + inventory::submit!(midenc_hir::pass::RewritePassRegistration::new::<#id<#params>>()); + inventory::submit! { + midenc_session::CompileFlag::new(<#id<#params> as PassInfo>::FLAG) + .long(<#id<#params> as PassInfo>::FLAG) + .help(<#id<#params> as PassInfo>::SUMMARY) + .help_heading("Rewrites") + .action(midenc_session::FlagAction::SetTrue) + .hide(true) + } + } }; proc_macro::TokenStream::from(quoted) @@ -220,6 +305,7 @@ pub fn derive_conversion_pass_registration( .help(<#id as PassInfo>::SUMMARY) .help_heading("Conversions") .action(midenc_session::FlagAction::SetTrue) + .hide(true) } } } else { @@ -230,6 +316,7 @@ pub fn derive_conversion_pass_registration( .help(<#id<#params> as PassInfo>::SUMMARY) .help_heading("Conversions") .action(midenc_session::FlagAction::SetTrue) + .hide(true) } } }; diff --git a/hir-macros/src/spanned.rs b/hir-macros/src/spanned.rs new file mode 100644 index 000000000..e88a6fced --- /dev/null +++ b/hir-macros/src/spanned.rs @@ -0,0 +1,413 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::{ + punctuated::Punctuated, spanned::Spanned, Arm, Attribute, Error, Expr, ExprField, ExprMatch, + ExprMethodCall, ExprPath, ExprUnary, FieldPat, Ident, Index, Member, Pat, PatIdent, PatRest, + PatStruct, PatTupleStruct, PatWild, Path, PathArguments, PathSegment, Token, UnOp, +}; + +pub fn derive_spanned_struct( + name: Ident, + data: syn::DataStruct, + generics: syn::Generics, +) -> Result { + let access = extract_span_fields(name.span(), None, &data.fields)?; + let span_expr = match access { + SpanAccess::Field(member) => make_member_access(make_self_expr(member.span()), member), + SpanAccess::Delegated(member) => { + let base = make_member_access(make_self_expr(member.span()), member); + make_delegated_member_access(base) + } + _ => unreachable!(), + }; + + let (impl_gen, ty_gen, where_clause) = generics.split_for_impl(); + let quoted = quote! { + impl #impl_gen Spanned for #name #ty_gen #where_clause { + #[inline] + fn span(&self) -> SourceSpan { + #span_expr + } + } + }; + + Ok(TokenStream::from(quoted)) +} + +pub fn derive_spanned_enum( + name: Ident, + data: syn::DataEnum, + generics: syn::Generics, +) -> Result { + let mut variants = Vec::with_capacity(data.variants.len()); + for variant in data.variants.iter() { + let span = variant.span(); + let access = extract_span_fields(span, Some(variant.ident.clone()), &variant.fields)?; + variants.push(access); + } + + // Generate match patterns for each variant, where the body of the match arm returns the span + let arms = variants + .drain(..) + .map(|access| match access { + SpanAccess::Variant(variant, Member::Named(id)) => { + let span = variant.span(); + let pat = Pat::Struct(PatStruct { + attrs: vec![], + path: make_path(&[Ident::new("Self", span), variant]), + brace_token: Default::default(), + fields: Punctuated::from_iter(core::iter::once(FieldPat { + attrs: vec![], + member: Member::Named(id.clone()), + colon_token: None, + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: id.clone(), + subpat: None, + })), + })), + rest: Some(syn::PatRest { + attrs: vec![], + dot2_token: Token![..](span), + }), + qself: None, + }); + Arm { + attrs: vec![], + pat, + guard: None, + fat_arrow_token: Token![=>](span), + body: Box::new(make_deref_expr(make_var_expr(id))), + comma: Some(Token![,](span)), + } + } + SpanAccess::Variant(variant, Member::Unnamed(idx)) => { + let span = variant.span(); + let mut elems = Punctuated::new(); + let index = idx.index; + for i in 0..=index { + if i == index { + elems.push(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new("span", span), + subpat: None, + })) + } else { + elems.push(Pat::Wild(PatWild { + attrs: vec![], + underscore_token: Token![_](span), + })); + } + } + elems.push(Pat::Rest(PatRest { + attrs: vec![], + dot2_token: Token![..](span), + })); + let pat = Pat::TupleStruct(PatTupleStruct { + attrs: vec![], + path: make_path(&[Ident::new("Self", span), variant]), + paren_token: Default::default(), + elems, + qself: None, + }); + Arm { + attrs: vec![], + pat, + guard: None, + fat_arrow_token: Token![=>](span), + body: Box::new(make_deref_expr(make_var_expr(Ident::new("span", span)))), + comma: Some(Token![,](span)), + } + } + SpanAccess::DelegatedVariant(variant, Member::Named(id)) => { + let span = variant.span(); + let pat = Pat::Struct(PatStruct { + attrs: vec![], + path: make_path(&[Ident::new("Self", span), variant]), + brace_token: Default::default(), + fields: Punctuated::from_iter(core::iter::once(FieldPat { + attrs: vec![], + member: Member::Named(id.clone()), + colon_token: None, + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: id.clone(), + subpat: None, + })), + })), + rest: Some(syn::PatRest { + attrs: vec![], + dot2_token: Token![..](span), + }), + qself: None, + }); + let body = make_delegated_member_access(make_var_expr(id)); + Arm { + attrs: vec![], + pat, + guard: None, + fat_arrow_token: Token![=>](span), + body: Box::new(body), + comma: Some(Token![,](span)), + } + } + SpanAccess::DelegatedVariant(variant, Member::Unnamed(idx)) => { + let span = variant.span(); + let mut elems = Punctuated::new(); + let index = idx.index; + for i in 0..=index { + if i == index { + elems.push(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new("span", span), + subpat: None, + })) + } else { + elems.push(Pat::Wild(PatWild { + attrs: vec![], + underscore_token: Token![_](span), + })); + } + } + elems.push(Pat::Rest(PatRest { + attrs: vec![], + dot2_token: Token![..](span), + })); + let pat = Pat::TupleStruct(PatTupleStruct { + attrs: vec![], + path: make_path(&[Ident::new("Self", span), variant]), + paren_token: Default::default(), + elems, + qself: None, + }); + let body = make_delegated_member_access(make_var_expr(Ident::new("span", span))); + Arm { + attrs: vec![], + pat, + guard: None, + fat_arrow_token: Token![=>](span), + body: Box::new(body), + comma: Some(Token![,](span)), + } + } + _ => unreachable!(), + }) + .collect(); + + let span = name.span(); + let span_expr = Expr::Match(ExprMatch { + attrs: vec![], + match_token: Token![match](span), + expr: Box::new(make_self_expr(span)), + brace_token: Default::default(), + arms, + }); + + let (impl_gen, ty_gen, where_clause) = generics.split_for_impl(); + let quoted = quote! { + impl #impl_gen Spanned for #name #ty_gen #where_clause { + fn span(&self) -> SourceSpan { + #span_expr + } + } + }; + + Ok(TokenStream::from(quoted)) +} + +enum SpanAccess { + Field(Member), + Variant(Ident, Member), + Delegated(Member), + DelegatedVariant(Ident, Member), +} + +fn make_path(idents: &[Ident]) -> Path { + let mut segments = Punctuated::new(); + for ident in idents.iter().cloned() { + segments.push(PathSegment { + ident, + arguments: PathArguments::None, + }); + } + Path { + leading_colon: None, + segments, + } +} + +fn make_self_expr(span: Span) -> Expr { + make_var_expr(Ident::new("self", span)) +} + +fn make_var_expr(ident: Ident) -> Expr { + Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: make_path(&[ident]), + }) +} + +fn make_deref_expr(expr: Expr) -> Expr { + let span = expr.span(); + Expr::Unary(ExprUnary { + attrs: vec![], + op: UnOp::Deref(Token![*](span)), + expr: Box::new(expr), + }) +} + +fn make_member_access(base: Expr, member: Member) -> Expr { + let span = member.span(); + Expr::Field(ExprField { + attrs: vec![], + base: Box::new(base), + dot_token: Token![.](span), + member, + }) +} + +fn make_delegated_member_access(receiver: Expr) -> Expr { + let span = receiver.span(); + Expr::MethodCall(ExprMethodCall { + attrs: vec![], + receiver: Box::new(receiver), + dot_token: Token![.](span), + method: Ident::new("span", span), + turbofish: None, + paren_token: Default::default(), + args: Punctuated::new(), + }) +} + +fn has_span_attr(attrs: &[Attribute]) -> bool { + attrs.iter().any(|attr| attr.path().is_ident("span")) +} + +fn extract_span_fields( + span: Span, + variant: Option, + fields: &syn::Fields, +) -> Result { + match fields { + syn::Fields::Named(fields) => { + let mut spanned = fields + .named + .iter() + .filter_map(|f| { + if has_span_attr(f.attrs.as_slice()) { + let delegated = !is_source_span(&f.ty); + Some((delegated, Member::Named(f.ident.clone().unwrap()))) + } else { + None + } + }) + .collect::>(); + if spanned.is_empty() { + Err(Error::new( + span, + "Spanned requires at least one field to be marked with the #[span] attribute", + )) + } else if spanned.len() > 1 { + Err(Error::new( + span, + "Spanned requires one field tagged with #[span], but multiple were found", + )) + } else { + let (delegated, member) = spanned.pop().unwrap(); + match variant { + None if delegated => Ok(SpanAccess::Delegated(member)), + None => Ok(SpanAccess::Field(member)), + Some(variant) if delegated => Ok(SpanAccess::DelegatedVariant(variant, member)), + Some(variant) => Ok(SpanAccess::Variant(variant, member)), + } + } + } + syn::Fields::Unnamed(fields) => { + let mut spanned = fields + .unnamed + .iter() + .enumerate() + .filter_map(|(i, f)| { + if has_span_attr(f.attrs.as_slice()) { + let delegated = !is_source_span(&f.ty); + Some(( + delegated, + Member::Unnamed(Index { + index: i as u32, + span: f.span(), + }), + )) + } else { + None + } + }) + .collect::>(); + if spanned.is_empty() { + // If there are multiple fields, then we can't make a selection, so raise an error + if fields.unnamed.len() > 1 { + return Err(Error::new( + span, + "Spanned requires at least one field to be marked with the #[span] \ + attribute", + )); + } + // Otherwise, we can infer the only field to contain a Spanned impl + match variant { + None => Ok(SpanAccess::Delegated(Member::Unnamed(Index { index: 0, span }))), + Some(variant) => Ok(SpanAccess::DelegatedVariant( + variant, + Member::Unnamed(Index { index: 0, span }), + )), + } + } else if spanned.len() > 1 { + Err(Error::new( + span, + "Spanned requires one field tagged with #[span], but multiple were found", + )) + } else { + let (delegated, member) = spanned.pop().unwrap(); + match variant { + None if delegated => Ok(SpanAccess::Delegated(member)), + None => Ok(SpanAccess::Field(member)), + Some(variant) if delegated => Ok(SpanAccess::DelegatedVariant(variant, member)), + Some(variant) => Ok(SpanAccess::Variant(variant, member)), + } + } + } + syn::Fields::Unit if variant.is_some() => { + Err(Error::new(span, "Spanned cannot be derived on enums with unit variants")) + } + syn::Fields::Unit => { + Err(Error::new(span, "Spanned requires a struct with at least one SourceSpan field")) + } + } +} + +fn is_source_span(ty: &syn::Type) -> bool { + match ty { + syn::Type::Path(tpath) => { + if tpath.path.is_ident("SourceSpan") { + return true; + } + match tpath.path.segments.len() { + 1 if tpath.path.leading_colon.is_none() => false, + 1 => { + let first = tpath.path.segments.first().unwrap(); + first.ident == "SourceSpan" && first.arguments == PathArguments::None + } + _ => false, + } + } + _ => false, + } +} diff --git a/hir-transform/src/inline_blocks.rs b/hir-transform/src/inline_blocks.rs index fd6d87652..5191eb548 100644 --- a/hir-transform/src/inline_blocks.rs +++ b/hir-transform/src/inline_blocks.rs @@ -87,13 +87,18 @@ impl RewritePass for InlineBlocks { // If inlining can proceed, do so until we reach a point where the inlined terminator // returns from the function, has multiple successors, or branches to a block with // multiple predecessors. - while let BranchInfo::SingleDest(b, args) = - function.dfg.analyze_branch(function.dfg.last_inst(p).unwrap()) - { + loop { + let succ = match function.dfg.analyze_branch(function.dfg.last_inst(p).unwrap()) { + BranchInfo::SingleDest(succ) => succ, + _ => break, + }; + + let destination = succ.destination; + // If this successor has other predecessors, it can't be inlined, so // add it to the work list and move on - if cfg.num_predecessors(b) > 1 { - worklist.push_back(b); + if cfg.num_predecessors(destination) > 1 { + worklist.push_back(destination); break; } @@ -103,16 +108,20 @@ impl RewritePass for InlineBlocks { // as we must visit all uses of the block arguments and update them. This // is left as a future extension of this pass should we find that it is // valuable as an optimization. - if !args.is_empty() { + if !succ.args.is_empty() { // Compute the set of values to rewrite - for (from, to) in - function.dfg.block_params(b).iter().copied().zip(args.iter().copied()) + for (from, to) in function + .dfg + .block_params(destination) + .iter() + .copied() + .zip(succ.args.iter().copied()) { rewrites.insert(from, to); } } - inline(b, p, function, &mut worklist, &rewrites, &mut cfg); + inline(destination, p, function, &mut worklist, &rewrites, &mut cfg); // Mark that the control flow graph as modified changed = true; @@ -167,16 +176,24 @@ fn inline( // Append the cloned terminator back to the inlined block before we detach it let from_terminator = from_terminator.expect("a block must have a terminator"); match (*from_terminator).as_ref() { - Instruction::Br(Br { destination, .. }) => { - worklist.push_back(*destination); + Instruction::Br(Br { successor, .. }) => { + worklist.push_back(successor.destination); } Instruction::CondBr(CondBr { - then_dest: (then_blk, _), - else_dest: (else_blk, _), + then_dest, + else_dest, .. }) => { - worklist.push_back(*then_blk); - worklist.push_back(*else_blk); + worklist.push_back(then_dest.destination); + worklist.push_back(else_dest.destination); + } + Instruction::Switch(hir::Switch { + arms, + default: default_dest, + .. + }) => { + worklist.extend(arms.iter().map(|arm| arm.successor.destination)); + worklist.push_back(default_dest.destination); } _ => (), } @@ -226,12 +243,10 @@ fn rewrite_use( let mut worklist = SmallVec::<[Block; 2]>::default(); match inst { Instruction::Br(Br { - destination, - ref mut args, - .. + ref mut successor, .. }) => { - worklist.push(*destination); - for arg in args.as_mut_slice(pool) { + worklist.push(successor.destination); + for arg in successor.args.as_mut_slice(pool) { if let Some(replacement) = rewrites.get(arg).copied() { *arg = replacement; } @@ -239,25 +254,49 @@ fn rewrite_use( } Instruction::CondBr(CondBr { ref mut cond, - then_dest: (then_dest, ref mut then_args), - else_dest: (else_dest, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { - worklist.push(*then_dest); - worklist.push(*else_dest); + worklist.push(then_dest.destination); + worklist.push(else_dest.destination); if let Some(replacement) = rewrites.get(cond).copied() { *cond = replacement; } - for arg in then_args.as_mut_slice(pool) { + for arg in then_dest.args.as_mut_slice(pool) { + if let Some(replacement) = rewrites.get(arg).copied() { + *arg = replacement; + } + } + for arg in else_dest.args.as_mut_slice(pool) { if let Some(replacement) = rewrites.get(arg).copied() { *arg = replacement; } } - for arg in else_args.as_mut_slice(pool) { + } + Instruction::Switch(hir::Switch { + ref mut arg, + ref mut arms, + default: ref mut default_dest, + .. + }) => { + worklist.extend(arms.iter().map(|arm| arm.successor.destination)); + worklist.push(default_dest.destination); + if let Some(replacement) = rewrites.get(arg).copied() { + *arg = replacement; + } + for arg in default_dest.args.as_mut_slice(pool) { if let Some(replacement) = rewrites.get(arg).copied() { *arg = replacement; } } + for arm in arms.iter_mut() { + for arg in arm.successor.args.as_mut_slice(pool) { + if let Some(replacement) = rewrites.get(arg).copied() { + *arg = replacement; + } + } + } } op => { for arg in op.arguments_mut(pool) { diff --git a/hir-transform/src/spill.rs b/hir-transform/src/spill.rs index 73579f24c..bd98c0728 100644 --- a/hir-transform/src/spill.rs +++ b/hir-transform/src/spill.rs @@ -13,9 +13,11 @@ use midenc_hir_analysis::{ use midenc_session::Session; use rustc_hash::FxHashSet; -/// This pass handles orchestrating the [InsertSpills] and [RewriteSpills] passes, and should be -/// preferred over using those two passes directly. See their respective documentation to better -/// understand what this pass does. +/// This pass places spills of SSA values to temporaries to cap the depth of the operand stack. +/// +/// Internally it handles orchestrating the [InsertSpills] and [RewriteSpills] passes, and should +/// be preferred over using those two passes directly. See their respective documentation to better +/// understand what this pass does as a whole. /// /// In addition to running the two passes, and maintaining the [AnalysisManager] state between them, /// this pass also handles applying an additional run of [crate::InlineBlocks] if spills were @@ -111,7 +113,7 @@ impl RewritePass for ApplySpills { /// /// **TL;DR:** Unless testing or debugging, always apply [InsertSpills] and [RewriteSpills] /// consecutively! -#[derive(Default, PassInfo, ModuleRewritePassAdapter)] +#[derive(Default)] pub struct InsertSpills; impl RewritePass for InsertSpills { type Entity = hir::Function; @@ -156,40 +158,28 @@ impl RewritePass for InsertSpills { let ix = builder.func.dfg.inst_mut(split_info.predecessor.inst); let args = match ix { Instruction::Br(Br { - ref mut destination, - args, - .. + ref mut successor, .. }) => { - assert_eq!(*destination, split_info.block); - *destination = split; - args.take() + assert_eq!(successor.destination, split_info.block); + successor.destination = split; + successor.args.take() } Instruction::CondBr(CondBr { - then_dest, - else_dest, + ref mut then_dest, + ref mut else_dest, .. }) => { - if then_dest.0 == split_info.block { - then_dest.0 = split; - then_dest.1.take() + if then_dest.destination == split_info.block { + then_dest.destination = split; + then_dest.args.take() } else { - assert_eq!(else_dest.0, split_info.block); - else_dest.0 = split; - else_dest.1.take() + assert_eq!(else_dest.destination, split_info.block); + else_dest.destination = split; + else_dest.args.take() } } - Instruction::Switch(Switch { - arms, r#default, .. - }) => { - if r#default == &split_info.block { - *r#default = split; - } - for (_, arm) in arms.iter_mut() { - if arm == &split_info.block { - *arm = split; - } - } - ValueList::default() + Instruction::Switch(_) => { + panic!("expected switch instructions to have been rewritten prior to this pass") } ix => unimplemented!("unhandled branch instruction: {}", ix.opcode()), }; @@ -309,7 +299,7 @@ impl RewritePass for InsertSpills { /// only place greater constraints on backend scheduling, but also ensure that more live ranges /// are split, and thus operands will spend less time on the operand stack overall. Time will /// tell whether this holds true or not. -#[derive(Default, PassInfo, ModuleRewritePassAdapter)] +#[derive(Default)] pub struct RewriteSpills; impl RewritePass for RewriteSpills { type Entity = hir::Function; @@ -485,7 +475,7 @@ fn find_inst_uses( // If `current_inst` is a branch or terminator, it cannot define a value, so // we simply record any uses, and move on. match function.dfg.analyze_branch(current_inst) { - BranchInfo::SingleDest(_, args) => { + BranchInfo::SingleDest(SuccessorInfo { args, .. }) => { for (index, arg) in args.iter().enumerate() { if spills.is_spilled(arg) { used.entry(*arg).or_default().insert(User::new( @@ -499,9 +489,9 @@ fn find_inst_uses( } } } - BranchInfo::MultiDest(ref jts) => { - for (succ_index, jt) in jts.iter().enumerate() { - for (index, arg) in jt.args.iter().enumerate() { + BranchInfo::MultiDest(infos) => { + for (succ_index, info) in infos.into_iter().enumerate() { + for (index, arg) in info.args.iter().enumerate() { if spills.is_spilled(arg) { used.entry(*arg).or_default().insert(User::new( current_inst, diff --git a/hir-transform/src/split_critical_edges.rs b/hir-transform/src/split_critical_edges.rs index 7a046c735..7db009d19 100644 --- a/hir-transform/src/split_critical_edges.rs +++ b/hir-transform/src/split_critical_edges.rs @@ -90,24 +90,22 @@ impl RewritePass for SplitCriticalEdges { let args: ValueList; match ix { Instruction::Br(hir::Br { - ref mut destination, - args: ref mut orig_args, - .. + ref mut successor, .. }) => { - args = orig_args.take(); - *destination = split; + args = successor.args.take(); + successor.destination = split; } Instruction::CondBr(hir::CondBr { - then_dest: (ref mut then_dest, ref mut then_args), - else_dest: (ref mut else_dest, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { - if *then_dest == b { - *then_dest = split; - args = then_args.take(); + if then_dest.destination == b { + then_dest.destination = split; + args = then_dest.args.take(); } else { - *else_dest = split; - args = else_args.take(); + else_dest.destination = split; + args = else_dest.args.take(); } } Instruction::Switch(_) => unimplemented!(), @@ -120,8 +118,10 @@ impl RewritePass for SplitCriticalEdges { }, Instruction::Br(hir::Br { op: hir::Opcode::Br, - destination: b, - args, + successor: hir::Successor { + destination: b, + args, + }, }), Type::Unknown, span, diff --git a/hir-transform/src/treeify.rs b/hir-transform/src/treeify.rs index 7368b903f..b51a94e4f 100644 --- a/hir-transform/src/treeify.rs +++ b/hir-transform/src/treeify.rs @@ -5,6 +5,7 @@ use std::{ use midenc_hir::{ self as hir, + diagnostics::Report, pass::{AnalysisManager, RewritePass, RewriteResult}, Block as BlockId, Value as ValueId, *, }; @@ -443,7 +444,7 @@ fn treeify( block_q: &mut VecDeque, mut value_map: ScopedMap, mut block_map: ScopedMap, -) -> anyhow::Result<()> { +) -> Result<(), Report> { // Check if we're dealing with a loop header let is_loop = block_infos.is_loop_header(b).is_some(); @@ -472,14 +473,15 @@ fn treeify( } } else { match function.dfg.analyze_branch(p.inst) { - BranchInfo::SingleDest(_, args) => { - value_map - .extend(function.dfg.block_args(b).iter().copied().zip(args.iter().copied())); + BranchInfo::SingleDest(info) => { + value_map.extend( + function.dfg.block_args(b).iter().copied().zip(info.args.iter().copied()), + ); } - BranchInfo::MultiDest(ref jts) => { - let jt = jts.iter().find(|jt| jt.destination == b).unwrap(); + BranchInfo::MultiDest(ref infos) => { + let info = infos.iter().find(|info| info.destination == b).unwrap(); value_map.extend( - function.dfg.block_args(b).iter().copied().zip(jt.args.iter().copied()), + function.dfg.block_args(b).iter().copied().zip(info.args.iter().copied()), ); } BranchInfo::NotABranch => unreachable!(), @@ -489,12 +491,12 @@ fn treeify( // 3. Update the predecessor instruction to reference the new block, remove block arguments if // this is not a loop header. let mut seen = false; // Only update the first occurrance of this predecessor - update_predecessor(function, p, |dest, dest_args, pool| { - if *dest == b && !seen { + update_predecessor(function, p, |successor, pool| { + if successor.destination == b && !seen { seen = true; - *dest = b_prime; + successor.destination = b_prime; if !is_loop { - dest_args.clear(pool); + successor.args.clear(pool); } } }); @@ -517,16 +519,16 @@ fn copy_children( block_q: &mut VecDeque, value_map: ScopedMap, block_map: ScopedMap, -) -> anyhow::Result<()> { +) -> Result<(), Report> { let pred = BlockPredecessor { inst: function.dfg.last_inst(b_prime).expect("expected non-empty block"), block: b_prime, }; let successors = match function.dfg.analyze_branch(function.dfg.last_inst(b).unwrap()) { BranchInfo::NotABranch => return Ok(()), - BranchInfo::SingleDest(dest, _) => smallvec![dest], - BranchInfo::MultiDest(ref jts) => { - SmallVec::<[_; 2]>::from_iter(jts.iter().map(|jt| jt.destination)) + BranchInfo::SingleDest(info) => smallvec![info.destination], + BranchInfo::MultiDest(infos) => { + SmallVec::<[_; 2]>::from_iter(infos.into_iter().map(|info| info.destination)) } }; let value_map = Rc::new(value_map); @@ -534,9 +536,9 @@ fn copy_children( for succ in successors { if let Some(succ_prime) = block_map.get(&succ) { - update_predecessor(function, &pred, |dest, _, _| { - if dest == &succ { - *dest = *succ_prime; + update_predecessor(function, &pred, |successor, _| { + if successor.destination == succ { + successor.destination = *succ_prime; } }); } @@ -585,14 +587,12 @@ fn copy_instructions( // Second, we need to rewrite value/block references contained in the instruction match data.as_mut() { Instruction::Br(hir::Br { - ref mut destination, - ref mut args, - .. + ref mut successor, .. }) => { - if let Some(new_dest) = block_map.get(destination) { - *destination = *new_dest; + if let Some(new_dest) = block_map.get(&successor.destination) { + successor.destination = *new_dest; } - let args = args.as_mut_slice(&mut function.dfg.value_lists); + let args = successor.args.as_mut_slice(&mut function.dfg.value_lists); for arg in args.iter_mut() { if let Some(arg_prime) = value_map.get(arg) { *arg = *arg_prime; @@ -601,32 +601,62 @@ fn copy_instructions( } Instruction::CondBr(hir::CondBr { ref mut cond, - then_dest: (ref mut then_dest, ref mut then_args), - else_dest: (ref mut else_dest, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { if let Some(cond_prime) = value_map.get(cond) { *cond = *cond_prime; } - if let Some(new_dest) = block_map.get(then_dest) { - *then_dest = *new_dest; + if let Some(new_dest) = block_map.get(&then_dest.destination) { + then_dest.destination = *new_dest; } - let then_args = then_args.as_mut_slice(&mut function.dfg.value_lists); + let then_args = then_dest.args.as_mut_slice(&mut function.dfg.value_lists); for arg in then_args.iter_mut() { if let Some(arg_prime) = value_map.get(arg) { *arg = *arg_prime; } } - if let Some(new_dest) = block_map.get(else_dest) { - *else_dest = *new_dest; + if let Some(new_dest) = block_map.get(&else_dest.destination) { + else_dest.destination = *new_dest; } - let else_args = else_args.as_mut_slice(&mut function.dfg.value_lists); + let else_args = else_dest.args.as_mut_slice(&mut function.dfg.value_lists); for arg in else_args.iter_mut() { if let Some(arg_prime) = value_map.get(arg) { *arg = *arg_prime; } } } + Instruction::Switch(hir::Switch { + ref mut arg, + ref mut arms, + default: ref mut default_succ, + .. + }) => { + if let Some(arg_prime) = value_map.get(arg) { + *arg = *arg_prime; + } + if let Some(new_default_dest) = block_map.get(&default_succ.destination) { + default_succ.destination = *new_default_dest; + } + let default_args = default_succ.args.as_mut_slice(&mut function.dfg.value_lists); + for arg in default_args.iter_mut() { + if let Some(arg_prime) = value_map.get(arg) { + *arg = *arg_prime; + } + } + for arm in arms.iter_mut() { + if let Some(new_dest) = block_map.get(&arm.successor.destination) { + arm.successor.destination = *new_dest; + } + let args = arm.successor.args.as_mut_slice(&mut function.dfg.value_lists); + for arg in args.iter_mut() { + if let Some(arg_prime) = value_map.get(arg) { + *arg = *arg_prime; + } + } + } + } other => { for arg in other.arguments_mut(&mut function.dfg.value_lists).iter_mut() { if let Some(arg_prime) = value_map.get(arg) { @@ -669,25 +699,23 @@ impl CopyBlock { #[inline] fn update_predecessor(function: &mut hir::Function, p: &BlockPredecessor, mut callback: F) where - F: FnMut(&mut BlockId, &mut ValueList, &mut ValueListPool), + F: FnMut(&mut hir::Successor, &mut ValueListPool), { - match &mut function.dfg.insts[p.inst].data.item { + match &mut *function.dfg.insts[p.inst].data { Instruction::Br(hir::Br { - ref mut destination, - ref mut args, - .. + ref mut successor, .. }) => { - callback(destination, args, &mut function.dfg.value_lists); + callback(successor, &mut function.dfg.value_lists); } Instruction::CondBr(hir::CondBr { - then_dest: (ref mut then_dest, ref mut then_args), - else_dest: (ref mut else_dest, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { - assert_ne!(then_dest, else_dest, "unexpected critical edge"); + assert_ne!(then_dest.destination, else_dest.destination, "unexpected critical edge"); let value_lists = &mut function.dfg.value_lists; - callback(then_dest, then_args, value_lists); - callback(else_dest, else_args, value_lists); + callback(then_dest, value_lists); + callback(else_dest, value_lists); } Instruction::Switch(_) => { panic!("expected switch instructions to have been simplified prior to treeification") diff --git a/hir/Cargo.toml b/hir/Cargo.toml index 4d154c204..e96040c3a 100644 --- a/hir/Cargo.toml +++ b/hir/Cargo.toml @@ -25,9 +25,9 @@ cranelift-entity.workspace = true intrusive-collections.workspace = true inventory.workspace = true lalrpop-util = "0.20" +log.workspace = true miden-core.workspace = true miden-assembly.workspace = true -miden-diagnostics.workspace = true midenc-hir-symbol.workspace = true midenc-hir-type.workspace = true midenc-hir-macros.workspace = true diff --git a/hir/src/asm/builder.rs b/hir/src/asm/builder.rs index 961aea702..b8e519a4e 100644 --- a/hir/src/asm/builder.rs +++ b/hir/src/asm/builder.rs @@ -2,7 +2,8 @@ use smallvec::smallvec; use super::*; use crate::{ - CallConv, Felt, FunctionIdent, Inst, InstBuilder, Instruction, Overflow, SourceSpan, Value, + diagnostics::{SourceSpan, Span}, + CallConv, Felt, FunctionIdent, Inst, InstBuilder, Instruction, Overflow, Value, }; /// Used to construct an [InlineAsm] instruction, while checking the input/output types, @@ -137,43 +138,43 @@ pub struct MasmOpBuilder<'a> { } impl<'a> MasmOpBuilder<'a> { /// Pads the stack with four zero elements - pub fn padw(mut self) { - self.build(self.ip, MasmOp::Padw); + pub fn padw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Padw, span); } /// Pushes an element on the stack - pub fn push(mut self, imm: Felt) { - self.build(self.ip, MasmOp::Push(imm)); + pub fn push(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::Push(imm), span); } /// Pushes a word on the stack - pub fn pushw(mut self, word: [Felt; 4]) { - self.build(self.ip, MasmOp::Pushw(word)); + pub fn pushw(mut self, word: [Felt; 4], span: SourceSpan) { + self.build(self.ip, MasmOp::Pushw(word), span); } /// Pushes an element representing an unsigned 8-bit integer on the stack - pub fn push_u8(mut self, imm: u8) { - self.build(self.ip, MasmOp::PushU8(imm)); + pub fn push_u8(mut self, imm: u8, span: SourceSpan) { + self.build(self.ip, MasmOp::PushU8(imm), span); } /// Pushes an element representing an unsigned 16-bit integer on the stack - pub fn push_u16(mut self, imm: u16) { - self.build(self.ip, MasmOp::PushU16(imm)); + pub fn push_u16(mut self, imm: u16, span: SourceSpan) { + self.build(self.ip, MasmOp::PushU16(imm), span); } /// Pushes an element representing an unsigned 32-bit integer on the stack - pub fn push_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::PushU32(imm)); + pub fn push_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::PushU32(imm), span); } /// Drops the element on the top of the stack - pub fn drop(mut self) { - self.build(self.ip, MasmOp::Drop); + pub fn drop(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Drop, span); } /// Drops the word (first four elements) on the top of the stack - pub fn dropw(mut self) { - self.build(self.ip, MasmOp::Dropw); + pub fn dropw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Dropw, span); } /// Duplicates the `n`th element from the top of the stack, to the top of the stack @@ -181,8 +182,8 @@ impl<'a> MasmOpBuilder<'a> { /// A `n` of zero, duplicates the element on top of the stack /// /// The valid range for `n` is 0..=15 - pub fn dup(mut self, n: usize) { - self.build(self.ip, MasmOp::Dup(n as u8)); + pub fn dup(mut self, n: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Dup(n as u8), span); } /// Duplicates the `n`th word from the top of the stack, to the top of the stack @@ -190,156 +191,156 @@ impl<'a> MasmOpBuilder<'a> { /// A `n` of zero, duplicates the word on top of the stack /// /// The valid range for `n` is 0..=3 - pub fn dupw(mut self, n: usize) { - self.build(self.ip, MasmOp::Dupw(n as u8)); + pub fn dupw(mut self, n: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Dupw(n as u8), span); } /// Swaps the `n`th element and the element on top of the stack /// /// The valid range for `n` is 1..=15 - pub fn swap(mut self, n: usize) { - self.build(self.ip, MasmOp::Swap(n as u8)); + pub fn swap(mut self, n: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Swap(n as u8), span); } /// Swaps the `n`th word and the word on top of the stack /// /// The valid range for `n` is 1..=3 - pub fn swapw(mut self, n: usize) { - self.build(self.ip, MasmOp::Swapw(n as u8)); + pub fn swapw(mut self, n: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Swapw(n as u8), span); } /// Swaps the top 2 and bottom 2 words on the stack - pub fn swapdw(mut self) { - self.build(self.ip, MasmOp::Swapdw); + pub fn swapdw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Swapdw, span); } /// Moves the `n`th element to the top of the stack /// /// The valid range for `n` is 2..=15 - pub fn movup(mut self, idx: usize) { - self.build(self.ip, MasmOp::Movup(idx as u8)); + pub fn movup(mut self, idx: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Movup(idx as u8), span); } /// Moves the `n`th word to the top of the stack /// /// The valid range for `n` is 2..=3 - pub fn movupw(mut self, idx: usize) { - self.build(self.ip, MasmOp::Movupw(idx as u8)); + pub fn movupw(mut self, idx: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Movupw(idx as u8), span); } /// Moves the element on top of the stack, making it the `n`th element /// /// The valid range for `n` is 2..=15 - pub fn movdn(mut self, idx: usize) { - self.build(self.ip, MasmOp::Movdn(idx as u8)); + pub fn movdn(mut self, idx: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Movdn(idx as u8), span); } /// Moves the word on top of the stack, making it the `n`th word /// /// The valid range for `n` is 2..=3 - pub fn movdnw(mut self, idx: usize) { - self.build(self.ip, MasmOp::Movdnw(idx as u8)); + pub fn movdnw(mut self, idx: usize, span: SourceSpan) { + self.build(self.ip, MasmOp::Movdnw(idx as u8), span); } /// Pops a boolean element off the stack, and swaps the top two elements /// on the stack if that boolean is true. /// /// Traps if the conditional is not 0 or 1. - pub fn cswap(mut self) { - self.build(self.ip, MasmOp::Cswap); + pub fn cswap(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Cswap, span); } /// Pops a boolean element off the stack, and swaps the top two words /// on the stack if that boolean is true. /// /// Traps if the conditional is not 0 or 1. - pub fn cswapw(mut self) { - self.build(self.ip, MasmOp::Cswapw); + pub fn cswapw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Cswapw, span); } /// Pops a boolean element off the stack, and drops the top element on the /// stack if the boolean is true, otherwise it drops the next element down. /// /// Traps if the conditional is not 0 or 1. - pub fn cdrop(mut self) { - self.build(self.ip, MasmOp::Cdrop); + pub fn cdrop(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Cdrop, span); } /// Pops a boolean element off the stack, and drops the top word on the /// stack if the boolean is true, otherwise it drops the next word down. /// /// Traps if the conditional is not 0 or 1. - pub fn cdropw(mut self) { - self.build(self.ip, MasmOp::Cdropw); + pub fn cdropw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Cdropw, span); } /// Pops the top element on the stack, and traps if that element is != 1. - pub fn assert(mut self, error_code: Option) { + pub fn assert(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::AssertWithError).unwrap_or(MasmOp::Assert); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops the top element on the stack, and traps if that element is != 0. - pub fn assertz(mut self, error_code: Option) { + pub fn assertz(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::AssertzWithError).unwrap_or(MasmOp::Assertz); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops the top two elements on the stack, and traps if they are not equal. - pub fn assert_eq(mut self, error_code: Option) { + pub fn assert_eq(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::AssertEqWithError).unwrap_or(MasmOp::AssertEq); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops the top two words on the stack, and traps if they are not equal. - pub fn assert_eqw(mut self, error_code: Option) { + pub fn assert_eqw(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::AssertEqwWithError).unwrap_or(MasmOp::AssertEqw); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops an element containing a memory address from the top of the stack, /// and loads the first element of the word at that address to the top of the stack. - pub fn load(mut self) { - self.build(self.ip, MasmOp::MemLoad); + pub fn load(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::MemLoad, span); } /// Loads the first element of the word at the given address to the top of the stack. - pub fn load_imm(mut self, addr: u32) { - self.build(self.ip, MasmOp::MemLoadImm(addr)); + pub fn load_imm(mut self, addr: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::MemLoadImm(addr), span); } /// Pops an element containing a memory address from the top of the stack, /// and loads the word at that address to the top of the stack. - pub fn loadw(mut self) { - self.build(self.ip, MasmOp::MemLoadw); + pub fn loadw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::MemLoadw, span); } /// Loads the word at the given address to the top of the stack. - pub fn loadw_imm(mut self, addr: u32) { - self.build(self.ip, MasmOp::MemLoadwImm(addr)); + pub fn loadw_imm(mut self, addr: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::MemLoadwImm(addr), span); } /// Pops two elements, the first containing a memory address from the top of the stack, /// the second the value to be stored as the first element of the word at that address. - pub fn store(mut self) { - self.build(self.ip, MasmOp::MemStore); + pub fn store(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::MemStore, span); } /// Pops an element from the top of the stack, and stores it as the first element of /// the word at the given address. - pub fn store_imm(mut self, addr: u32) { - self.build(self.ip, MasmOp::MemStoreImm(addr)); + pub fn store_imm(mut self, addr: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::MemStoreImm(addr), span); } /// Pops an element containing a memory address from the top of the stack, /// and then pops a word from the stack and stores it as the word at that address. - pub fn storew(mut self) { - self.build(self.ip, MasmOp::MemStorew); + pub fn storew(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::MemStorew, span); } /// Pops a word from the stack and stores it as the word at the given address. - pub fn storew_imm(mut self, addr: u32) { - self.build(self.ip, MasmOp::MemStorewImm(addr)); + pub fn storew_imm(mut self, addr: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::MemStorewImm(addr), span); } /// Begins construction of a `if.true` statement. @@ -355,7 +356,7 @@ impl<'a> MasmOpBuilder<'a> { /// in the same abstract state, so that when control resumes after the `if.true`, the remaining /// program is well-formed. This will be validated automatically for you, but if validation /// fails, the builder will panic. - pub fn if_true(self) -> IfTrueBuilder<'a> { + pub fn if_true(self, span: SourceSpan) -> IfTrueBuilder<'a> { let cond = self.stack.pop().expect("operand stack is empty"); assert_eq!(cond, Type::I1, "expected while.true condition to be a boolean value"); let out_stack = self.stack.clone(); @@ -364,6 +365,7 @@ impl<'a> MasmOpBuilder<'a> { asm: self.asm, in_stack: self.stack, out_stack, + span, ip: self.ip, then_blk: None, else_blk: None, @@ -390,7 +392,7 @@ impl<'a> MasmOpBuilder<'a> { /// on the operand stack. /// /// Both of these are validated by [LoopBuilder], and a panic is raised if validation fails. - pub fn while_true(self) -> LoopBuilder<'a> { + pub fn while_true(self, span: SourceSpan) -> LoopBuilder<'a> { let cond = self.stack.pop().expect("operand stack is empty"); assert_eq!(cond, Type::I1, "expected while.true condition to be a boolean value"); let out_stack = self.stack.clone(); @@ -400,6 +402,7 @@ impl<'a> MasmOpBuilder<'a> { asm: self.asm, in_stack: self.stack, out_stack, + span, ip: self.ip, body, style: LoopType::While, @@ -412,7 +415,7 @@ impl<'a> MasmOpBuilder<'a> { /// times. /// /// NOTE: The iteration count must be non-zero, or this function will panic. - pub fn repeat(self, n: u16) -> LoopBuilder<'a> { + pub fn repeat(self, n: u16, span: SourceSpan) -> LoopBuilder<'a> { assert!(n > 0, "invalid iteration count for `repeat.n`, must be non-zero"); let out_stack = self.stack.clone(); let body = self.asm.create_block(); @@ -421,6 +424,7 @@ impl<'a> MasmOpBuilder<'a> { asm: self.asm, in_stack: self.stack, out_stack, + span, ip: self.ip, body, style: LoopType::Repeat(n), @@ -428,315 +432,315 @@ impl<'a> MasmOpBuilder<'a> { } /// Executes the named procedure as a regular function. - pub fn exec(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::Exec(id)); + pub fn exec(mut self, id: FunctionIdent, span: SourceSpan) { + self.build(self.ip, MasmOp::Exec(id), span); } /// Execute a procedure indirectly. /// /// Expects the hash of a function's MAST root on the stack, see `procref` - pub fn dynexec(mut self) { - self.build(self.ip, MasmOp::DynExec) + pub fn dynexec(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::DynExec, span) } /// Call a procedure indirectly. /// /// Expects the hash of a function's MAST root on the stack, see `procref` - pub fn dyncall(mut self) { - self.build(self.ip, MasmOp::DynCall) + pub fn dyncall(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::DynCall, span) } /// Executes the named procedure as a call. - pub fn call(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::Call(id)); + pub fn call(mut self, id: FunctionIdent, span: SourceSpan) { + self.build(self.ip, MasmOp::Call(id), span); } /// Executes the named procedure as a syscall. - pub fn syscall(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::Syscall(id)); + pub fn syscall(mut self, id: FunctionIdent, span: SourceSpan) { + self.build(self.ip, MasmOp::Syscall(id), span); } /// Push the hash of the named function on the stack for use with dyn(exec|call) - pub fn procref(mut self, id: FunctionIdent) { - self.build(self.ip, MasmOp::ProcRef(id)) + pub fn procref(mut self, id: FunctionIdent, span: SourceSpan) { + self.build(self.ip, MasmOp::ProcRef(id), span) } /// Pops two field elements from the stack, adds them, and places the result on the stack. - pub fn add(mut self) { - self.build(self.ip, MasmOp::Add); + pub fn add(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Add, span); } /// Pops a field element from the stack, adds the given value to it, and places the result on /// the stack. - pub fn add_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::AddImm(imm)); + pub fn add_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::AddImm(imm), span); } /// Pops two field elements from the stack, subtracts the second from the first, and places the /// result on the stack. - pub fn sub(mut self) { - self.build(self.ip, MasmOp::Sub); + pub fn sub(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Sub, span); } /// Pops a field element from the stack, subtracts the given value from it, and places the /// result on the stack. - pub fn sub_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::SubImm(imm)); + pub fn sub_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::SubImm(imm), span); } /// Pops two field elements from the stack, multiplies them, and places the result on the stack. - pub fn mul(mut self) { - self.build(self.ip, MasmOp::Mul); + pub fn mul(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Mul, span); } /// Pops a field element from the stack, multiplies it by the given value, and places the result /// on the stack. - pub fn mul_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::MulImm(imm)); + pub fn mul_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::MulImm(imm), span); } /// Pops two field elements from the stack, divides the first by the second, and places the /// result on the stack. - pub fn div(mut self) { - self.build(self.ip, MasmOp::Div); + pub fn div(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Div, span); } /// Pops a field element from the stack, divides it by the given value, and places the result on /// the stack. - pub fn div_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::DivImm(imm)); + pub fn div_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::DivImm(imm), span); } /// Negates the field element on top of the stack - pub fn neg(mut self) { - self.build(self.ip, MasmOp::Neg); + pub fn neg(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Neg, span); } /// Replaces the field element on top of the stack with it's multiplicative inverse, i.e. `a^-1 /// mod p` - pub fn inv(mut self) { - self.build(self.ip, MasmOp::Inv); + pub fn inv(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Inv, span); } /// Increments the field element on top of the stack - pub fn incr(mut self) { - self.build(self.ip, MasmOp::Incr); + pub fn incr(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Incr, span); } /// Pops an element, `a`, from the top of the stack, and places the integral base 2 logarithm /// of that value on the stack. /// /// Traps if `a` is 0. - pub fn ilog2(mut self) { - self.build(self.ip, MasmOp::Ilog2); + pub fn ilog2(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Ilog2, span); } /// Pops an element, `a`, from the top of the stack, and places the result of `2^a` on the /// stack. /// /// Traps if `a` is not in the range 0..=63 - pub fn pow2(mut self) { - self.build(self.ip, MasmOp::Pow2); + pub fn pow2(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Pow2, span); } /// Pops two elements from the stack, `b` and `a` respectively, and places the result of `a^b` /// on the stack. /// /// Traps if `b` is not in the range 0..=63 - pub fn exp(mut self) { - self.build(self.ip, MasmOp::Exp); + pub fn exp(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Exp, span); } /// Pops an element from the stack, `a`, and places the result of `a^b` on the stack, where `b` /// is the given immediate value. /// /// Traps if `b` is not in the range 0..=63 - pub fn exp_imm(mut self, exponent: u8) { - self.build(self.ip, MasmOp::ExpImm(exponent)); + pub fn exp_imm(mut self, exponent: u8, span: SourceSpan) { + self.build(self.ip, MasmOp::ExpImm(exponent), span); } /// Pops a value off the stack, and applies logical NOT, and places the result back on the /// stack. /// /// Traps if the value is not 0 or 1. - pub fn not(mut self) { - self.build(self.ip, MasmOp::Not); + pub fn not(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Not, span); } /// Pops two values off the stack, applies logical AND, and places the result back on the stack. /// /// Traps if either value is not 0 or 1. - pub fn and(mut self) { - self.build(self.ip, MasmOp::And); + pub fn and(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::And, span); } /// Pops a value off the stack, applies logical AND with the given immediate, and places the /// result back on the stack. /// /// Traps if the value is not 0 or 1. - pub fn and_imm(mut self, imm: bool) { - self.build(self.ip, MasmOp::AndImm(imm)); + pub fn and_imm(mut self, imm: bool, span: SourceSpan) { + self.build(self.ip, MasmOp::AndImm(imm), span); } /// Pops two values off the stack, applies logical OR, and places the result back on the stack. /// /// Traps if either value is not 0 or 1. - pub fn or(mut self) { - self.build(self.ip, MasmOp::Or); + pub fn or(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Or, span); } /// Pops a value off the stack, applies logical OR with the given immediate, and places the /// result back on the stack. /// /// Traps if the value is not 0 or 1. - pub fn or_imm(mut self, imm: bool) { - self.build(self.ip, MasmOp::OrImm(imm)); + pub fn or_imm(mut self, imm: bool, span: SourceSpan) { + self.build(self.ip, MasmOp::OrImm(imm), span); } /// Pops two values off the stack, applies logical XOR, and places the result back on the stack. /// /// Traps if either value is not 0 or 1. - pub fn xor(mut self) { - self.build(self.ip, MasmOp::Xor); + pub fn xor(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Xor, span); } /// Pops a value off the stack, applies logical XOR with the given immediate, and places the /// result back on the stack. /// /// Traps if the value is not 0 or 1. - pub fn xor_imm(mut self, imm: bool) { - self.build(self.ip, MasmOp::XorImm(imm)); + pub fn xor_imm(mut self, imm: bool, span: SourceSpan) { + self.build(self.ip, MasmOp::XorImm(imm), span); } /// Pops two elements off the stack, and pushes 1 on the stack if they are equal, else 0. - pub fn eq(mut self) { - self.build(self.ip, MasmOp::Eq); + pub fn eq(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Eq, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value and the given /// immediate are equal, else 0. - pub fn eq_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::EqImm(imm)); + pub fn eq_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::EqImm(imm), span); } /// Pops two words off the stack, and pushes 1 on the stack if they are equal, else 0. - pub fn eqw(mut self) { - self.build(self.ip, MasmOp::Eqw); + pub fn eqw(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Eqw, span); } /// Pops two elements off the stack, and pushes 1 on the stack if they are not equal, else 0. - pub fn neq(mut self) { - self.build(self.ip, MasmOp::Neq); + pub fn neq(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Neq, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value and the given /// immediate are not equal, else 0. - pub fn neq_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::NeqImm(imm)); + pub fn neq_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::NeqImm(imm), span); } /// Pops two elements off the stack, and pushes 1 on the stack if the first is greater than the /// second, else 0. - pub fn gt(mut self) { - self.build(self.ip, MasmOp::Gt); + pub fn gt(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Gt, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value is greater than the /// given immediate, else 0. - pub fn gt_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::GtImm(imm)); + pub fn gt_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::GtImm(imm), span); } /// Pops two elements off the stack, and pushes 1 on the stack if the first is greater than or /// equal to the second, else 0. - pub fn gte(mut self) { - self.build(self.ip, MasmOp::Gte); + pub fn gte(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Gte, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value is greater than or /// equal to the given immediate, else 0. - pub fn gte_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::GteImm(imm)); + pub fn gte_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::GteImm(imm), span); } /// Pops two elements off the stack, and pushes 1 on the stack if the first is less than the /// second, else 0. - pub fn lt(mut self) { - self.build(self.ip, MasmOp::Lt); + pub fn lt(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Lt, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value is less than the /// given immediate, else 0. - pub fn lt_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::LtImm(imm)); + pub fn lt_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::LtImm(imm), span); } /// Pops two elements off the stack, and pushes 1 on the stack if the first is less than or /// equal to the second, else 0. - pub fn lte(mut self) { - self.build(self.ip, MasmOp::Lte); + pub fn lte(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Lte, span); } /// Pops an element off the stack, and pushes 1 on the stack if that value is less than or equal /// to the given immediate, else 0. - pub fn lte_imm(mut self, imm: Felt) { - self.build(self.ip, MasmOp::LteImm(imm)); + pub fn lte_imm(mut self, imm: Felt, span: SourceSpan) { + self.build(self.ip, MasmOp::LteImm(imm), span); } /// Pops an element off the stack, and pushes 1 on the stack if that value is an odd number, /// else 0. - pub fn is_odd(mut self) { - self.build(self.ip, MasmOp::IsOdd); + pub fn is_odd(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::IsOdd, span); } /// Pushes the current value of the cycle counter (clock) on the stack - pub fn clk(mut self) { - self.build(self.ip, MasmOp::Clk); + pub fn clk(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Clk, span); } /// Pushes the hash of the caller on the stack - pub fn caller(mut self) { - self.build(self.ip, MasmOp::Caller); + pub fn caller(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Caller, span); } /// Pushes the current stack depth on the stack - pub fn current_stack_depth(mut self) { - self.build(self.ip, MasmOp::Sdepth); + pub fn current_stack_depth(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::Sdepth, span); } /// Pushes 1 on the stack if the element on top of the stack is less than 2^32, else 0. - pub fn test_u32(mut self) { - self.build(self.ip, MasmOp::U32Test); + pub fn test_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Test, span); } /// Pushes 1 on the stack if every element of the word on top of the stack is less than 2^32, /// else 0. - pub fn testw_u32(mut self) { - self.build(self.ip, MasmOp::U32Testw); + pub fn testw_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Testw, span); } /// Traps if the element on top of the stack is greater than or equal to 2^32 - pub fn assert_u32(mut self, error_code: Option) { + pub fn assert_u32(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::U32AssertWithError).unwrap_or(MasmOp::U32Assert); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Traps if either of the first two elements on top of the stack are greater than or equal to /// 2^32 - pub fn assert2_u32(mut self, error_code: Option) { + pub fn assert2_u32(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::U32Assert2WithError).unwrap_or(MasmOp::U32Assert2); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Traps if any element of the first word on the stack are greater than or equal to 2^32 - pub fn assertw_u32(mut self, error_code: Option) { + pub fn assertw_u32(mut self, error_code: Option, span: SourceSpan) { let op = error_code.map(MasmOp::U32AssertwWithError).unwrap_or(MasmOp::U32Assertw); - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Casts the element on top of the stack, `a`, to a valid u32 value, by computing `a mod 2^32` - pub fn cast_u32(mut self) { - self.build(self.ip, MasmOp::U32Cast); + pub fn cast_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Cast, span); } /// Pops an element, `a`, from the stack, and splits it into two elements, `b` and `c`, each of @@ -744,136 +748,166 @@ impl<'a> MasmOpBuilder<'a> { /// /// The value for `b` is given by `a mod 2^32`, and the value for `c` by `a / 2^32`. They are /// pushed on the stack in that order, i.e. `c` will be on top of the stack afterwards. - pub fn split_u32(mut self) { - self.build(self.ip, MasmOp::U32Split); + pub fn split_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Split, span); } /// Performs unsigned addition of the top two elements on the stack, `b` and `a` respectively, /// which are expected to be valid u32 values. /// /// See the [Overflow] enum for how `overflow` modifies the semantics of this instruction. - pub fn add_u32(mut self, overflow: Overflow) { + pub fn add_u32(mut self, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked => MasmOp::Add, Overflow::Checked => { - return self - .build_many(self.ip, [MasmOp::U32Assert2, MasmOp::Add, MasmOp::U32Assert]); + return self.build_many( + self.ip, + [ + Span::new(span, MasmOp::U32Assert2), + Span::new(span, MasmOp::Add), + Span::new(span, MasmOp::U32Assert), + ], + ); } Overflow::Overflowing => MasmOp::U32OverflowingAdd, Overflow::Wrapping => MasmOp::U32WrappingAdd, }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Same as above, but `a` is provided by the given immediate. - pub fn add_imm_u32(mut self, imm: u32, overflow: Overflow) { + pub fn add_imm_u32(mut self, imm: u32, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked if imm == 1 => MasmOp::Incr, Overflow::Unchecked => MasmOp::AddImm(Felt::new(imm as u64)), Overflow::Checked => { return self.build_many( self.ip, - [MasmOp::U32Assert, MasmOp::AddImm(Felt::new(imm as u64)), MasmOp::U32Assert], + [ + Span::new(span, MasmOp::U32Assert), + Span::new(span, MasmOp::AddImm(Felt::new(imm as u64))), + Span::new(span, MasmOp::U32Assert), + ], ); } Overflow::Overflowing => MasmOp::U32OverflowingAddImm(imm), Overflow::Wrapping => MasmOp::U32WrappingAddImm(imm), }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops three elements from the stack, `c`, `b`, and `a`, and computes `a + b + c` using the /// overflowing semantics of `add_u32`. The first two elements on the stack after this /// instruction will be a boolean indicating whether addition overflowed, and the result /// itself, mod 2^32. - pub fn add3_overflowing_u32(mut self) { - self.build(self.ip, MasmOp::U32OverflowingAdd3); + pub fn add3_overflowing_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32OverflowingAdd3, span); } /// Pops three elements from the stack, `c`, `b`, and `a`, and computes `a + b + c` using the /// wrapping semantics of `add_u32`. The result will be on top of the stack afterwards, mod /// 2^32. - pub fn add3_wrapping_u32(mut self) { - self.build(self.ip, MasmOp::U32WrappingAdd3); + pub fn add3_wrapping_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32WrappingAdd3, span); } /// Performs unsigned subtraction of the top two elements on the stack, `b` and `a` /// respectively, which are expected to be valid u32 values. /// /// See the [Overflow] enum for how `overflow` modifies the semantics of this instruction. - pub fn sub_u32(mut self, overflow: Overflow) { + pub fn sub_u32(mut self, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked => MasmOp::Sub, Overflow::Checked => { - return self - .build_many(self.ip, [MasmOp::U32Assert2, MasmOp::Sub, MasmOp::U32Assert]); + return self.build_many( + self.ip, + [ + Span::new(span, MasmOp::U32Assert2), + Span::new(span, MasmOp::Sub), + Span::new(span, MasmOp::U32Assert), + ], + ); } Overflow::Overflowing => MasmOp::U32OverflowingSub, Overflow::Wrapping => MasmOp::U32WrappingSub, }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Same as above, but `a` is provided by the given immediate. - pub fn sub_imm_u32(mut self, imm: u32, overflow: Overflow) { + pub fn sub_imm_u32(mut self, imm: u32, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked => MasmOp::SubImm(Felt::new(imm as u64)), Overflow::Checked => { return self.build_many( self.ip, - [MasmOp::U32Assert, MasmOp::SubImm(Felt::new(imm as u64)), MasmOp::U32Assert], + [ + Span::new(span, MasmOp::U32Assert), + Span::new(span, MasmOp::SubImm(Felt::new(imm as u64))), + Span::new(span, MasmOp::U32Assert), + ], ); } Overflow::Overflowing => MasmOp::U32OverflowingSubImm(imm), Overflow::Wrapping => MasmOp::U32WrappingSubImm(imm), }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Performs unsigned multiplication of the top two elements on the stack, `b` and `a` /// respectively, which are expected to be valid u32 values. /// /// See the [Overflow] enum for how `overflow` modifies the semantics of this instruction. - pub fn mul_u32(mut self, overflow: Overflow) { + pub fn mul_u32(mut self, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked => MasmOp::Mul, Overflow::Checked => { - return self - .build_many(self.ip, [MasmOp::U32Assert2, MasmOp::Mul, MasmOp::U32Assert]); + return self.build_many( + self.ip, + [ + Span::new(span, MasmOp::U32Assert2), + Span::new(span, MasmOp::Mul), + Span::new(span, MasmOp::U32Assert), + ], + ); } Overflow::Overflowing => MasmOp::U32OverflowingMul, Overflow::Wrapping => MasmOp::U32WrappingMul, }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Same as above, but `a` is provided by the given immediate. - pub fn mul_imm_u32(mut self, imm: u32, overflow: Overflow) { + pub fn mul_imm_u32(mut self, imm: u32, overflow: Overflow, span: SourceSpan) { let op = match overflow { Overflow::Unchecked => MasmOp::MulImm(Felt::new(imm as u64)), Overflow::Checked => { return self.build_many( self.ip, - [MasmOp::U32Assert, MasmOp::MulImm(Felt::new(imm as u64)), MasmOp::U32Assert], + [ + Span::new(span, MasmOp::U32Assert), + Span::new(span, MasmOp::MulImm(Felt::new(imm as u64))), + Span::new(span, MasmOp::U32Assert), + ], ); } Overflow::Overflowing => MasmOp::U32OverflowingMulImm(imm), Overflow::Wrapping => MasmOp::U32WrappingMulImm(imm), }; - self.build(self.ip, op); + self.build(self.ip, op, span); } /// Pops three elements from the stack, `b`, `a`, and `c`, and computes `a * b + c`, using /// overflowing semantics, i.e. the result is wrapped mod 2^32, and a flag is pushed on the /// stack if the result overflowed the u32 range. - pub fn madd_overflowing_u32(mut self) { - self.build(self.ip, MasmOp::U32OverflowingMadd); + pub fn madd_overflowing_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32OverflowingMadd, span); } /// Pops three elements from the stack, `b`, `a`, and `c`, and computes `a * b + c`, using /// wrapping semantics, i.e. the result is wrapped mod 2^32. - pub fn madd_wrapping_u32(mut self) { - self.build(self.ip, MasmOp::U32WrappingMadd); + pub fn madd_wrapping_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32WrappingMadd, span); } /// Performs unsigned division of the top two elements on the stack, `b` and `a` respectively, @@ -882,13 +916,13 @@ impl<'a> MasmOpBuilder<'a> { /// This operation is unchecked, so if either operand is >= 2^32, the result is undefined. /// /// Traps if `b` is 0. - pub fn div_u32(mut self) { - self.build(self.ip, MasmOp::U32Div); + pub fn div_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Div, span); } /// Same as above, but `b` is provided by the given immediate - pub fn div_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32DivImm(imm)); + pub fn div_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32DivImm(imm), span); } /// Pops two elements off the stack, `b` and `a` respectively, and computes `a mod b`. @@ -896,13 +930,13 @@ impl<'a> MasmOpBuilder<'a> { /// This operation is unchecked, so if either operand is >= 2^32, the result is undefined. /// /// Traps if `b` is 0. - pub fn mod_u32(mut self) { - self.build(self.ip, MasmOp::U32Mod); + pub fn mod_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Mod, span); } /// Same as above, but `b` is provided by the given immediate - pub fn mod_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32ModImm(imm)); + pub fn mod_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32ModImm(imm), span); } /// Pops two elements off the stack, `b` and `a` respectively, and computes `a / b`, and `a mod @@ -911,192 +945,192 @@ impl<'a> MasmOpBuilder<'a> { /// This operation is unchecked, so if either operand is >= 2^32, the results are undefined. /// /// Traps if `b` is 0. - pub fn divmod_u32(mut self) { - self.build(self.ip, MasmOp::U32DivMod); + pub fn divmod_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32DivMod, span); } /// Same as above, but `b` is provided by the given immediate - pub fn divmod_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32DivModImm(imm)); + pub fn divmod_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32DivModImm(imm), span); } /// Pops two elements off the stack, and computes the bitwise AND of those values, placing the /// result on the stack. /// /// Traps if either element is not a valid u32 value. - pub fn band_u32(mut self) { - self.build(self.ip, MasmOp::U32And); + pub fn band_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32And, span); } /// Pops two elements off the stack, and computes the bitwise OR of those values, placing the /// result on the stack. /// /// Traps if either element is not a valid u32 value. - pub fn bor_u32(mut self) { - self.build(self.ip, MasmOp::U32Or); + pub fn bor_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Or, span); } /// Pops two elements off the stack, and computes the bitwise XOR of those values, placing the /// result on the stack. /// /// Traps if either element is not a valid u32 value. - pub fn bxor_u32(mut self) { - self.build(self.ip, MasmOp::U32Xor); + pub fn bxor_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Xor, span); } /// Pops an element off the stack, and computes the bitwise NOT of that value, placing the /// result on the stack. /// /// Traps if the element is not a valid u32 value. - pub fn bnot_u32(mut self) { - self.build(self.ip, MasmOp::U32Not); + pub fn bnot_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Not, span); } /// Pops two elements off the stack, `b` and `a` respectively, and shifts `a` left by `b` bits. /// More precisely, the result is computed as `(a * 2^b) mod 2^32`. /// /// The result is undefined if `a` is not a valid u32, or `b` is > 31. - pub fn shl_u32(mut self) { - self.build(self.ip, MasmOp::U32Shl); + pub fn shl_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Shl, span); } /// Same as `shl_u32`, but `b` is provided by immediate. - pub fn shl_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32ShlImm(imm)); + pub fn shl_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32ShlImm(imm), span); } /// Pops two elements off the stack, `b` and `a` respectively, and shifts `a` right by `b` bits. /// More precisely, the result is computed as `a / 2^b`. /// /// The result is undefined if `a` is not a valid u32, or `b` is > 31. - pub fn shr_u32(mut self) { - self.build(self.ip, MasmOp::U32Shr); + pub fn shr_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Shr, span); } /// Same as `shr_u32`, but `b` is provided by immediate. - pub fn shr_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32ShrImm(imm)); + pub fn shr_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32ShrImm(imm), span); } /// Pops two elements off the stack, `b` and `a` respectively, and rotates the binary /// representation of `a` left by `b` bits. /// /// The result is undefined if `a` is not a valid u32, or `b` is > 31. - pub fn rotl_u32(mut self) { - self.build(self.ip, MasmOp::U32Rotl); + pub fn rotl_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Rotl, span); } /// Same as `rotl_u32`, but `b` is provided by immediate. - pub fn rotl_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32RotlImm(imm)); + pub fn rotl_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32RotlImm(imm), span); } /// Pops two elements off the stack, `b` and `a` respectively, and rotates the binary /// representation of `a` right by `b` bits. /// /// The result is undefined if `a` is not a valid u32, or `b` is > 31. - pub fn rotr_u32(mut self) { - self.build(self.ip, MasmOp::U32Rotr); + pub fn rotr_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Rotr, span); } /// Same as `rotr_u32`, but `b` is provided by immediate. - pub fn rotr_imm_u32(mut self, imm: u32) { - self.build(self.ip, MasmOp::U32RotrImm(imm)); + pub fn rotr_imm_u32(mut self, imm: u32, span: SourceSpan) { + self.build(self.ip, MasmOp::U32RotrImm(imm), span); } /// Pops an element off the stack, and computes the number of set bits in its binary /// representation, i.e. its hamming weight, and places the result on the stack. /// /// The result is undefined if the input value is not a valid u32. - pub fn popcnt_u32(mut self) { - self.build(self.ip, MasmOp::U32Popcnt); + pub fn popcnt_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Popcnt, span); } /// Pops an element off the stack, and computes the number of leading zeros in its binary /// representation, and places the result on the stack. /// /// The result is undefined if the input value is not a valid u32. - pub fn clz_u32(mut self) { - self.build(self.ip, MasmOp::U32Clz); + pub fn clz_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Clz, span); } /// Pops an element off the stack, and computes the number of trailing zeros in its binary /// representation, and places the result on the stack. /// /// The result is undefined if the input value is not a valid u32. - pub fn ctz_u32(mut self) { - self.build(self.ip, MasmOp::U32Ctz); + pub fn ctz_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Ctz, span); } /// Pops an element off the stack, and computes the number of leading ones in its binary /// representation, and places the result on the stack. /// /// The result is undefined if the input value is not a valid u32. - pub fn clo_u32(mut self) { - self.build(self.ip, MasmOp::U32Clo); + pub fn clo_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Clo, span); } /// Pops an element off the stack, and computes the number of trailing ones in its binary /// representation, and places the result on the stack. /// /// The result is undefined if the input value is not a valid u32. - pub fn cto_u32(mut self) { - self.build(self.ip, MasmOp::U32Cto); + pub fn cto_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Cto, span); } /// Pushes a boolean on the stack by computing `a < b` for `[b, a]` /// /// The result is undefined if either operand is not a valid u32 value. - pub fn lt_u32(mut self) { - self.build(self.ip, MasmOp::U32Lt); + pub fn lt_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Lt, span); } /// Pushes a boolean on the stack by computing `a <= b` for `[b, a]` /// /// The result is undefined if either operand is not a valid u32 value. - pub fn lte_u32(mut self) { - self.build(self.ip, MasmOp::U32Lte); + pub fn lte_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Lte, span); } /// Pushes a boolean on the stack by computing `a > b` for `[b, a]` /// /// The result is undefined if either operand is not a valid u32 value. - pub fn gt_u32(mut self) { - self.build(self.ip, MasmOp::U32Gt); + pub fn gt_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Gt, span); } /// Pushes a boolean on the stack by computing `a >= b` for `[b, a]` /// /// The result is undefined if either operand is not a valid u32 value. - pub fn gte_u32(mut self) { - self.build(self.ip, MasmOp::U32Gte); + pub fn gte_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Gte, span); } /// Computes the minimum of the two elements on top of the stack. /// /// The result is undefined if either operand is not a valid u32 value. - pub fn min_u32(mut self) { - self.build(self.ip, MasmOp::U32Min); + pub fn min_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Min, span); } /// Computes the maximum of the two elements on top of the stack. /// /// The result is undefined if either operand is not a valid u32 value. - pub fn max_u32(mut self) { - self.build(self.ip, MasmOp::U32Max); + pub fn max_u32(mut self, span: SourceSpan) { + self.build(self.ip, MasmOp::U32Max, span); } #[inline(never)] - fn build(&mut self, ip: MasmBlockId, op: MasmOp) { + fn build(&mut self, ip: MasmBlockId, op: MasmOp, span: SourceSpan) { apply_op_stack_effects(&op, self.stack, self.dfg, self.asm); - self.asm.push(ip, op); + self.asm.push(ip, op, span); } #[inline(never)] - fn build_many(&mut self, ip: MasmBlockId, ops: impl IntoIterator) { + fn build_many(&mut self, ip: MasmBlockId, ops: impl IntoIterator>) { for op in ops.into_iter() { apply_op_stack_effects(&op, self.stack, self.dfg, self.asm); - self.asm.push(ip, op); + self.asm.push(ip, op.into_inner(), op.span()); } } } @@ -1159,6 +1193,8 @@ pub struct IfTrueBuilder<'a> { /// will be used as the expected state of the operand stack when we finish /// constructing the second branch and validate the `if.true`. out_stack: OperandStack, + /// The source span associated with the conditional + span: SourceSpan, /// This is the block to which the `if.true` will be appended ip: MasmBlockId, /// The block id for the then branch, unset until it has been finalized @@ -1202,7 +1238,7 @@ impl<'f> IfTrueBuilder<'f> { pub fn build(mut self) { let then_blk = self.then_blk.expect("missing 'then' block"); let else_blk = self.else_blk.expect("missing 'else' block"); - self.asm.push(self.ip, MasmOp::If(then_blk, else_blk)); + self.asm.push(self.ip, MasmOp::If(then_blk, else_blk), self.span); // Update the operand stack to represent the state after execution of the `if.true` let in_stack = self.in_stack.stack_mut(); in_stack.clear(); @@ -1380,6 +1416,8 @@ pub struct LoopBuilder<'a> { /// as otherwise program behavior is undefined based on whether the loop is /// entered or not. out_stack: OperandStack, + /// The source span associated with the loop + span: SourceSpan, /// The block to which the loop instruction will be appended ip: MasmBlockId, /// The top-level block for the loop @@ -1419,7 +1457,7 @@ impl<'f> LoopBuilder<'f> { "expected the operand stack to be in the same abstract state whether the \ while.true loop is taken or skipped" ); - self.asm.push(self.ip, MasmOp::While(self.body)); + self.asm.push(self.ip, MasmOp::While(self.body), self.span); } LoopType::Repeat(1) => { // This is an edge case, but a single iteration `repeat` is no different than @@ -1461,7 +1499,7 @@ impl<'f> LoopBuilder<'f> { apply_op_stack_effects(op, &mut self.out_stack, self.dfg, self.asm); } } - self.asm.push(self.ip, MasmOp::Repeat(n, self.body)); + self.asm.push(self.ip, MasmOp::Repeat(n, self.body), self.span); } } diff --git a/hir/src/asm/display.rs b/hir/src/asm/display.rs index 3e8ea0d3e..9cf785938 100644 --- a/hir/src/asm/display.rs +++ b/hir/src/asm/display.rs @@ -330,14 +330,3 @@ impl<'a> fmt::Display for DisplayOp<'a> { self.pretty_print(f) } } - -struct Address(u32); -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "0x")?; - for byte in self.0.to_be_bytes() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} diff --git a/hir/src/asm/import.rs b/hir/src/asm/import.rs index 23474de7a..2a0a2f844 100644 --- a/hir/src/asm/import.rs +++ b/hir/src/asm/import.rs @@ -5,10 +5,12 @@ use core::{ }; use anyhow::bail; -use miden_diagnostics::{SourceSpan, Spanned}; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{FunctionIdent, Ident, Symbol}; +use crate::{ + diagnostics::{SourceSpan, Spanned}, + FunctionIdent, Ident, Symbol, +}; #[derive(Default, Debug, Clone)] pub struct ModuleImportInfo { diff --git a/hir/src/asm/isa.rs b/hir/src/asm/isa.rs index a22cec29b..f365ff1b1 100644 --- a/hir/src/asm/isa.rs +++ b/hir/src/asm/isa.rs @@ -4,7 +4,10 @@ use cranelift_entity::entity_impl; pub use miden_assembly::ast::{AdviceInjectorNode, DebugOptions}; use smallvec::{smallvec, SmallVec}; -use crate::{Felt, FunctionIdent, Ident, LocalId}; +use crate::{ + diagnostics::{SourceSpan, Span}, + Felt, FunctionIdent, Ident, LocalId, +}; /// A handle that refers to a MASM code block #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -15,7 +18,7 @@ entity_impl!(MasmBlockId, "blk"); #[derive(Debug, Clone, PartialEq)] pub struct MasmBlock { pub id: MasmBlockId, - pub ops: SmallVec<[MasmOp; 4]>, + pub ops: SmallVec<[Span; 4]>, } impl MasmBlock { /// Returns true if there are no instructions in this block @@ -26,19 +29,20 @@ impl MasmBlock { /// Returns the instructions contained in this block as a slice #[inline(always)] - pub fn ops(&self) -> &[MasmOp] { + pub fn ops(&self) -> &[Span] { self.ops.as_slice() } /// Appends `op` to this code block #[inline(always)] - pub fn push(&mut self, op: MasmOp) { - self.ops.push(op); + pub fn push(&mut self, op: MasmOp, span: SourceSpan) { + self.ops.push(Span::new(span, op)); } /// Append `n` copies of `op` to the current block #[inline] - pub fn push_n(&mut self, count: usize, op: MasmOp) { + pub fn push_n(&mut self, count: usize, op: MasmOp, span: SourceSpan) { + let op = Span::new(span, op); for _ in 0..count { self.ops.push(op); } @@ -46,7 +50,7 @@ impl MasmBlock { /// Append `n` copies of the sequence `ops` to this block #[inline] - pub fn push_repeat(&mut self, ops: &[MasmOp], count: usize) { + pub fn push_repeat(&mut self, ops: &[Span], count: usize) { for _ in 0..count { self.ops.extend_from_slice(ops); } @@ -56,7 +60,7 @@ impl MasmBlock { #[inline] pub fn push_template(&mut self, count: usize, template: F) where - F: Fn(usize) -> [MasmOp; N], + F: Fn(usize) -> [Span; N], { for n in 0..count { self.ops.extend_from_slice(&template(n)); @@ -65,13 +69,13 @@ impl MasmBlock { /// Appends instructions from `slice` to the end of this block #[inline] - pub fn extend_from_slice(&mut self, slice: &[MasmOp]) { + pub fn extend_from_slice(&mut self, slice: &[Span]) { self.ops.extend_from_slice(slice); } /// Appends instructions from `slice` to the end of this block #[inline] - pub fn extend(&mut self, ops: impl IntoIterator) { + pub fn extend(&mut self, ops: impl IntoIterator>) { self.ops.extend(ops); } @@ -79,7 +83,7 @@ impl MasmBlock { #[inline] pub fn append(&mut self, other: &mut SmallVec) where - B: smallvec::Array, + B: smallvec::Array>, { self.ops.append(other); } @@ -1182,13 +1186,9 @@ impl MasmOp { Instruction::NeqImm(imm) => Self::NeqImm(unwrap_imm!(imm)), Instruction::Eqw => Self::Eqw, Instruction::Lt => Self::Lt, - Instruction::LtImm(imm) => Self::LtImm(unwrap_imm!(imm)), Instruction::Lte => Self::Lte, - Instruction::LteImm(imm) => Self::LteImm(unwrap_imm!(imm)), Instruction::Gt => Self::Gt, - Instruction::GtImm(imm) => Self::GtImm(unwrap_imm!(imm)), Instruction::Gte => Self::Gte, - Instruction::GteImm(imm) => Self::GteImm(unwrap_imm!(imm)), Instruction::IsOdd => Self::IsOdd, Instruction::Hash => Self::Hash, Instruction::HMerge => Self::Hmerge, @@ -1258,17 +1258,11 @@ impl MasmOp { Instruction::U32Clo => Self::U32Clo, Instruction::U32Cto => Self::U32Cto, Instruction::U32Lt => Self::U32Lt, - Instruction::U32LtImm(imm) => Self::U32LtImm(unwrap_u32!(imm)), Instruction::U32Lte => Self::U32Lte, - Instruction::U32LteImm(imm) => Self::U32LteImm(unwrap_u32!(imm)), Instruction::U32Gt => Self::U32Gt, - Instruction::U32GtImm(imm) => Self::U32GtImm(unwrap_u32!(imm)), Instruction::U32Gte => Self::U32Gte, - Instruction::U32GteImm(imm) => Self::U32GteImm(unwrap_u32!(imm)), Instruction::U32Min => Self::U32Min, - Instruction::U32MinImm(imm) => Self::U32MinImm(unwrap_u32!(imm)), Instruction::U32Max => Self::U32Max, - Instruction::U32MaxImm(imm) => Self::U32MaxImm(unwrap_u32!(imm)), Instruction::Drop => Self::Drop, Instruction::DropW => Self::Dropw, Instruction::PadW => Self::Padw, @@ -1465,7 +1459,6 @@ impl MasmOp { locals: &BTreeSet, ) -> SmallVec<[miden_assembly::ast::Instruction; 2]> { use miden_assembly::{ - self as masm, ast::{Instruction, InvocationTarget, ProcedureName}, LibraryPath, }; @@ -1632,19 +1625,23 @@ impl MasmOp { | Self::Syscall(ref callee) | Self::ProcRef(ref callee)) => { let target = if locals.contains(callee) { - let callee = ProcedureName::new(callee.function.as_str()) - .expect("invalid procedure name"); + let callee = ProcedureName::new_unchecked(super::utils::translate_ident( + callee.function, + )); InvocationTarget::ProcedureName(callee) } else if let Some(alias) = imports.alias(&callee.module) { - let name = ProcedureName::new(callee.function.as_str()) - .expect("invalid procedure name"); + let alias = super::utils::translate_ident(alias); + let name = ProcedureName::new_unchecked(super::utils::translate_ident( + callee.function, + )); InvocationTarget::ProcedurePath { name, - module: masm::ast::Ident::new(alias.as_str()).expect("invalid module path"), + module: alias, } } else { - let name = ProcedureName::new(callee.function.as_str()) - .expect("invalid procedure name"); + let name = ProcedureName::new_unchecked(super::utils::translate_ident( + callee.function, + )); let path = LibraryPath::new(callee.module.as_str()).expect("invalid procedure path"); InvocationTarget::AbsoluteProcedurePath { name, path } @@ -1781,17 +1778,25 @@ impl MasmOp { Self::U32Clo => Instruction::U32Clo, Self::U32Cto => Instruction::U32Cto, Self::U32Lt => Instruction::U32Lt, - Self::U32LtImm(imm) => Instruction::U32LtImm(imm.into()), + Self::U32LtImm(imm) => return smallvec![Instruction::PushU32(imm), Instruction::U32Lt], Self::U32Lte => Instruction::U32Lte, - Self::U32LteImm(imm) => Instruction::U32LteImm(imm.into()), + Self::U32LteImm(imm) => { + return smallvec![Instruction::PushU32(imm), Instruction::U32Lte] + } Self::U32Gt => Instruction::U32Gt, - Self::U32GtImm(imm) => Instruction::U32GtImm(imm.into()), + Self::U32GtImm(imm) => return smallvec![Instruction::PushU32(imm), Instruction::U32Gt], Self::U32Gte => Instruction::U32Gte, - Self::U32GteImm(imm) => Instruction::U32GteImm(imm.into()), + Self::U32GteImm(imm) => { + return smallvec![Instruction::PushU32(imm), Instruction::U32Gte]; + } Self::U32Min => Instruction::U32Min, - Self::U32MinImm(imm) => Instruction::U32MinImm(imm.into()), + Self::U32MinImm(imm) => { + return smallvec![Instruction::PushU32(imm), Instruction::U32Min]; + } Self::U32Max => Instruction::U32Max, - Self::U32MaxImm(imm) => Instruction::U32MaxImm(imm.into()), + Self::U32MaxImm(imm) => { + return smallvec![Instruction::PushU32(imm), Instruction::U32Max]; + } Self::Breakpoint => Instruction::Breakpoint, Self::DebugStack => Instruction::Debug(DebugOptions::StackAll), Self::DebugStackN(n) => Instruction::Debug(DebugOptions::StackTop(n.into())), diff --git a/hir/src/asm/mod.rs b/hir/src/asm/mod.rs index 4ae668cfc..3ba126025 100644 --- a/hir/src/asm/mod.rs +++ b/hir/src/asm/mod.rs @@ -3,6 +3,7 @@ mod display; mod import; mod isa; mod stack; +pub mod utils; use cranelift_entity::PrimaryMap; use smallvec::smallvec; @@ -14,7 +15,7 @@ pub use self::{ isa::*, stack::{OperandStack, Stack, StackElement}, }; -use super::{DataFlowGraph, Opcode, Type, ValueList}; +use crate::{diagnostics::SourceSpan, DataFlowGraph, Opcode, Type, ValueList}; /// Represents Miden Assembly (MASM) directly in the IR /// @@ -78,8 +79,8 @@ impl InlineAsm { } /// Appends `op` to the end of `block` - pub fn push(&mut self, block: MasmBlockId, op: MasmOp) { - self.blocks[block].push(op); + pub fn push(&mut self, block: MasmBlockId, op: MasmOp, span: SourceSpan) { + self.blocks[block].push(op, span); } pub fn display<'a, 'b: 'a>( diff --git a/hir/src/asm/stack.rs b/hir/src/asm/stack.rs index 7e4bf1480..0f4dd2689 100644 --- a/hir/src/asm/stack.rs +++ b/hir/src/asm/stack.rs @@ -1,4 +1,4 @@ -use std::{ +use core::{ fmt, ops::{Index, IndexMut}, }; diff --git a/hir/src/asm/utils.rs b/hir/src/asm/utils.rs new file mode 100644 index 000000000..508061838 --- /dev/null +++ b/hir/src/asm/utils.rs @@ -0,0 +1,9 @@ +use alloc::sync::Arc; + +use crate::diagnostics::Span; + +/// Obtain a [miden_assembly::ast::Ident] from a [crate::Ident], with source span intact. +pub fn translate_ident(id: crate::Ident) -> miden_assembly::ast::Ident { + let name = Arc::from(id.as_str().to_string().into_boxed_str()); + miden_assembly::ast::Ident::new_unchecked(Span::new(id.span, name)) +} diff --git a/hir/src/attribute.rs b/hir/src/attribute.rs index 122f22247..12f090449 100644 --- a/hir/src/attribute.rs +++ b/hir/src/attribute.rs @@ -1,4 +1,5 @@ -use std::{borrow::Borrow, collections::BTreeMap, fmt}; +use alloc::collections::BTreeMap; +use core::{borrow::Borrow, fmt}; use crate::Symbol; diff --git a/hir/src/builder.rs b/hir/src/builder.rs index e411bd88e..1f9a481f7 100644 --- a/hir/src/builder.rs +++ b/hir/src/builder.rs @@ -1,4 +1,4 @@ -use super::*; +use crate::{diagnostics::Span, *}; pub struct FunctionBuilder<'f> { pub func: &'f mut Function, @@ -181,8 +181,6 @@ impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> { ctrl_typevar: Type, span: SourceSpan, ) -> (Inst, &'f mut DataFlowGraph) { - use miden_diagnostics::Span; - // Splat the new instruction on top of the old one. self.dfg.insts[self.inst].replace(Span::new(span, data)); // The old result values, if any, were either detached or non-existent. @@ -1419,9 +1417,9 @@ pub trait InstBuilder<'f>: InstBuilderBase<'f> { self.CondBr(cond, then_dest, then_vlist, else_dest, else_vlist, span).0 } - fn switch(self, arg: Value, arms: Vec<(u32, Block)>, default: Block, span: SourceSpan) -> Inst { + fn switch(self, arg: Value, span: SourceSpan) -> SwitchBuilder<'f, Self> { require_integer!(self, arg, Type::U32); - self.Switch(arg, arms, default, span).0 + SwitchBuilder::new(self, arg, span) } fn ret(mut self, returning: Option, span: SourceSpan) -> Inst { @@ -1471,8 +1469,14 @@ pub trait InstBuilder<'f>: InstBuilderBase<'f> { let data = Instruction::CondBr(CondBr { op: Opcode::CondBr, cond, - then_dest: (then_dest, then_args), - else_dest: (else_dest, else_args), + then_dest: Successor { + destination: then_dest, + args: then_args, + }, + else_dest: Successor { + destination: else_dest, + args: else_args, + }, }); self.build(data, Type::Unit, span) } @@ -1488,8 +1492,7 @@ pub trait InstBuilder<'f>: InstBuilderBase<'f> { ) -> (Inst, &'f mut DataFlowGraph) { let data = Instruction::Br(Br { op, - destination, - args, + successor: Successor { destination, args }, }); self.build(data, ty, span) } @@ -1498,8 +1501,8 @@ pub trait InstBuilder<'f>: InstBuilderBase<'f> { fn Switch( self, arg: Value, - arms: Vec<(u32, Block)>, - default: Block, + arms: Vec, + default: Successor, span: SourceSpan, ) -> (Inst, &'f mut DataFlowGraph) { let data = Instruction::Switch(Switch { @@ -1726,3 +1729,63 @@ pub trait InstBuilder<'f>: InstBuilderBase<'f> { } impl<'f, T: InstBuilderBase<'f>> InstBuilder<'f> for T {} + +/// An instruction builder for `switch`, to ensure it is validated during construction +pub struct SwitchBuilder<'f, T: InstBuilder<'f>> { + builder: T, + arg: Value, + span: SourceSpan, + arms: Vec, + _marker: core::marker::PhantomData<&'f Function>, +} +impl<'f, T: InstBuilder<'f>> SwitchBuilder<'f, T> { + fn new(builder: T, arg: Value, span: SourceSpan) -> Self { + Self { + builder, + arg, + span, + arms: Default::default(), + _marker: core::marker::PhantomData, + } + } + + /// Specify to what block a specific discriminant value should be dispatched + pub fn case(mut self, discriminant: u32, target: Block, args: &[Value]) -> Self { + assert_eq!( + self.arms + .iter() + .find(|arm| arm.value == discriminant) + .map(|arm| arm.successor.destination), + None, + "duplicate switch case value '{discriminant}': already matched" + ); + let mut vlist = ValueList::default(); + { + let pool = &mut self.builder.data_flow_graph_mut().value_lists; + vlist.extend(args.iter().copied(), pool); + } + let arm = SwitchArm { + value: discriminant, + successor: Successor { + destination: target, + args: vlist, + }, + }; + self.arms.push(arm); + self + } + + /// Build the `switch` by specifying the fallback destination if none of the arms match + pub fn or_else(mut self, target: Block, args: &[Value]) -> Inst { + let mut vlist = ValueList::default(); + { + let pool = &mut self.builder.data_flow_graph_mut().value_lists; + vlist.extend(args.iter().copied(), pool); + } + let fallback = Successor { + destination: target, + args: vlist, + }; + self.builder.Switch(self.arg, self.arms, fallback, self.span).0 + } +} diff --git a/hir/src/component/interface.rs b/hir/src/component/interface.rs index 095b2c82d..3a3f3fbb2 100644 --- a/hir/src/component/interface.rs +++ b/hir/src/component/interface.rs @@ -6,7 +6,7 @@ use crate::formatter::PrettyPrint; /// A fully-qualified identifier for the interface being imported, e.g. /// `namespace::package/interface@version` -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InterfaceIdent { /// A fully-qualified identifier for the interface being imported, e.g. /// `namespace::package/interface@version` @@ -23,6 +23,11 @@ impl InterfaceIdent { } } +impl fmt::Debug for InterfaceIdent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt::Display::fmt(&self.full_name, f) + } +} impl fmt::Display for InterfaceIdent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\"{}\"", self.full_name.as_str().escape_default()) @@ -30,7 +35,7 @@ impl fmt::Display for InterfaceIdent { } /// An identifier for a function in an interface -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InterfaceFunctionIdent { /// An interface identifier for the interface being imported (e.g. /// `namespace::package/interface@version`) @@ -50,6 +55,11 @@ impl InterfaceFunctionIdent { } } +impl fmt::Debug for InterfaceFunctionIdent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} @ {:?}", &self.function, &self.interface) + } +} impl fmt::Display for InterfaceFunctionIdent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.pretty_print(f) diff --git a/hir/src/component/mod.rs b/hir/src/component/mod.rs index 405fee28b..d74250924 100644 --- a/hir/src/component/mod.rs +++ b/hir/src/component/mod.rs @@ -5,7 +5,10 @@ use indexmap::IndexMap; use miden_core::crypto::hash::RpoDigest; use self::formatter::PrettyPrint; -use super::*; +use crate::{ + diagnostics::{DiagnosticsHandler, Report}, + *, +}; mod interface; @@ -255,10 +258,10 @@ pub struct ComponentBuilder<'a> { imports: BTreeMap, exports: BTreeMap, entry: Option, - diagnostics: &'a miden_diagnostics::DiagnosticsHandler, + diagnostics: &'a DiagnosticsHandler, } impl<'a> ComponentBuilder<'a> { - pub fn new(diagnostics: &'a miden_diagnostics::DiagnosticsHandler) -> Self { + pub fn new(diagnostics: &'a DiagnosticsHandler) -> Self { Self { modules: Default::default(), entry: None, @@ -291,7 +294,7 @@ impl<'a> ComponentBuilder<'a> { pub fn add_module(&mut self, module: Box) -> Result<(), ModuleConflictError> { let module_name = module.name; if self.modules.contains_key(&module_name) { - return Err(ModuleConflictError(module_name)); + return Err(ModuleConflictError::new(module_name)); } self.modules.insert(module_name, module); @@ -394,15 +397,15 @@ impl<'a, 'b: 'a> AsMut for ComponentModuleBuilder<'a, 'b> { /// This is used to build a [Function] from a [ComponentModuleBuilder]. /// /// It is basically just a wrapper around [ModuleFunctionBuilder], but overrides -/// `build` to use the [miden_diagnostics::DiagnosticsHandler] of the parent +/// `build` to use the [DiagnosticsHandler] of the parent /// [ComponentBuilder]. pub struct ComponentFunctionBuilder<'a, 'b: 'a> { - diagnostics: &'b miden_diagnostics::DiagnosticsHandler, + diagnostics: &'b DiagnosticsHandler, fb: ModuleFunctionBuilder<'a>, } impl<'a, 'b: 'a> ComponentFunctionBuilder<'a, 'b> { /// Build the current function - pub fn build(self) -> Result { + pub fn build(self) -> Result { let diagnostics = self.diagnostics; self.fb.build(diagnostics) } diff --git a/hir/src/dataflow.rs b/hir/src/dataflow.rs index fdd5d81a1..1f5d5a4e2 100644 --- a/hir/src/dataflow.rs +++ b/hir/src/dataflow.rs @@ -1,11 +1,13 @@ -use std::ops::{Deref, Index, IndexMut}; +use core::ops::{Deref, DerefMut, Index, IndexMut}; use cranelift_entity::{PrimaryMap, SecondaryMap}; -use miden_diagnostics::{Span, Spanned}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use super::*; +use crate::{ + diagnostics::{SourceSpan, Span, Spanned}, + *, +}; pub struct DataFlowGraph { pub entry: Block, @@ -424,9 +426,11 @@ impl DataFlowGraph { /// Replace uses of `value` with `replacement` in the arguments of `inst` pub fn replace_uses(&mut self, inst: Inst, value: Value, replacement: Value) { let ix = &mut self.insts[inst]; - match &mut ix.data.item { - Instruction::Br(Br { ref mut args, .. }) => { - let args = args.as_mut_slice(&mut self.value_lists); + match ix.data.deref_mut() { + Instruction::Br(Br { + ref mut successor, .. + }) => { + let args = successor.args.as_mut_slice(&mut self.value_lists); for arg in args.iter_mut() { if arg == &value { *arg = replacement; @@ -435,26 +439,50 @@ impl DataFlowGraph { } Instruction::CondBr(CondBr { ref mut cond, - then_dest: (_, ref mut then_args), - else_dest: (_, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { if cond == &value { *cond = replacement; } - let then_args = then_args.as_mut_slice(&mut self.value_lists); + let then_args = then_dest.args.as_mut_slice(&mut self.value_lists); for arg in then_args.iter_mut() { if arg == &value { *arg = replacement; } } - let else_args = else_args.as_mut_slice(&mut self.value_lists); + let else_args = else_dest.args.as_mut_slice(&mut self.value_lists); for arg in else_args.iter_mut() { if arg == &value { *arg = replacement; } } } + Instruction::Switch(Switch { + ref mut arg, + ref mut arms, + default: default_succ, + .. + }) => { + if arg == &value { + *arg = replacement; + } + let default_args = default_succ.args.as_mut_slice(&mut self.value_lists); + for arg in default_args.iter_mut() { + if arg == &value { + *arg = replacement; + } + } + for arm in arms.iter_mut() { + let args = arm.successor.args.as_mut_slice(&mut self.value_lists); + for arg in args.iter_mut() { + if arg == &value { + *arg = replacement; + } + } + } + } ix => { for arg in ix.arguments_mut(&mut self.value_lists) { if arg == &value { @@ -483,28 +511,38 @@ impl DataFlowGraph { replacement: Value, ) { let ix = &mut self.insts[inst]; - match ix.data.as_mut() { - Instruction::Br(Br { ref mut args, .. }) => { + match ix.data.deref_mut() { + Instruction::Br(Br { + ref mut successor, .. + }) => { debug_assert_eq!(succ_index, 0); - args.as_mut_slice(&mut self.value_lists)[index] = replacement; + successor.args.as_mut_slice(&mut self.value_lists)[index] = replacement; } Instruction::CondBr(CondBr { - then_dest: (_, ref mut then_args), - else_dest: (_, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => match succ_index { 0 => { - then_args.as_mut_slice(&mut self.value_lists)[index] = replacement; + then_dest.args.as_mut_slice(&mut self.value_lists)[index] = replacement; } 1 => { - else_args.as_mut_slice(&mut self.value_lists)[index] = replacement; + else_dest.args.as_mut_slice(&mut self.value_lists)[index] = replacement; } _ => unreachable!("expected valid successor index for cond_br, got {succ_index}"), }, - Instruction::Switch(_) => unimplemented!( - "invalid instruction: cannot replace successor arguments for 'switch': arms \ - cannot have arguments yet" - ), + Instruction::Switch(Switch { + ref mut arms, + default: ref mut default_succ, + .. + }) => { + debug_assert!(succ_index < arms.len() + 1); + if succ_index == arms.len() { + default_succ.args.as_mut_slice(&mut self.value_lists)[index] = replacement; + } + arms[succ_index].successor.args.as_mut_slice(&mut self.value_lists)[index] = + replacement; + } ix => panic!("invalid instruction: expected branch instruction, got {ix:#?}"), } } @@ -552,7 +590,7 @@ impl DataFlowGraph { self.insts[inst].analyze_call(&self.value_lists) } - pub fn analyze_branch(&self, inst: Inst) -> BranchInfo { + pub fn analyze_branch(&self, inst: Inst) -> BranchInfo<'_> { self.insts[inst].analyze_branch(&self.value_lists) } @@ -750,32 +788,38 @@ impl DataFlowGraph { dest: Block, value: Value, ) { - match self.insts[branch_inst].data.as_mut() { + match self.insts[branch_inst].data.deref_mut() { Instruction::Br(Br { - destination, - ref mut args, - .. + ref mut successor, .. }) => { - debug_assert_eq!(*destination, dest); - args.push(value, &mut self.value_lists); + debug_assert_eq!(successor.destination, dest); + successor.args.push(value, &mut self.value_lists); } Instruction::CondBr(CondBr { - then_dest: (then_dest, ref mut then_args), - else_dest: (else_dest, ref mut else_args), + ref mut then_dest, + ref mut else_dest, .. }) => { - if *then_dest == dest { - then_args.push(value, &mut self.value_lists); + if then_dest.destination == dest { + then_dest.args.push(value, &mut self.value_lists); } - if *else_dest == dest { - else_args.push(value, &mut self.value_lists); + if else_dest.destination == dest { + else_dest.args.push(value, &mut self.value_lists); } } - Instruction::Switch(_) => { - panic!( - "cannot append argument {value} to Switch destination block {dest}, since it \ - has no block arguments support" - ); + Instruction::Switch(Switch { + ref mut arms, + default: ref mut default_succ, + .. + }) => { + if default_succ.destination == dest { + default_succ.args.push(value, &mut self.value_lists); + } + for arm in arms.iter_mut() { + if arm.successor.destination == dest { + arm.successor.args.push(value, &mut self.value_lists); + } + } } _ => panic!("{} must be a branch instruction", branch_inst), } diff --git a/hir/src/display.rs b/hir/src/display.rs index a50458658..eeeed5070 100644 --- a/hir/src/display.rs +++ b/hir/src/display.rs @@ -1,4 +1,4 @@ -use std::{cell::Cell, fmt}; +use core::{cell::Cell, fmt}; use super::{Block, Inst}; diff --git a/hir/src/function.rs b/hir/src/function.rs index 78dd91c9a..3e4654c3b 100644 --- a/hir/src/function.rs +++ b/hir/src/function.rs @@ -1,13 +1,16 @@ use cranelift_entity::entity_impl; use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink}; -use miden_diagnostics::Spanned; use self::formatter::PrettyPrint; -use super::*; +use crate::{ + diagnostics::{miette, Diagnostic, Spanned}, + *, +}; /// This error is raised when two function declarations conflict with the same symbol name -#[derive(Debug, thiserror::Error)] -#[error("item with this name has already been declared, or cannot be merged")] +#[derive(Debug, thiserror::Error, Diagnostic)] +#[error("item named '{}' has already been declared, or cannot be merged", .0)] +#[diagnostic()] pub struct SymbolConflictError(pub FunctionIdent); /// A handle that refers to an [ExternalFunction] @@ -644,24 +647,24 @@ impl<'a> fmt::Display for CfgPrinter<'a> { let opcode = self.function.dfg.inst(last_inst).opcode(); writeln!(f, " {block_id} --> {opcode}")?; } - BranchInfo::SingleDest(succ, _) => { + BranchInfo::SingleDest(info) => { assert!( - self.function.dfg.is_block_linked(succ), + self.function.dfg.is_block_linked(info.destination), "reference to detached block in attached block {}", - succ + info.destination ); - writeln!(f, " {block_id} --> {succ}")?; - block_q.push_back(succ); + writeln!(f, " {block_id} --> {}", info.destination)?; + block_q.push_back(info.destination); } - BranchInfo::MultiDest(ref jts) => { - for jt in jts { + BranchInfo::MultiDest(ref infos) => { + for info in infos { assert!( - self.function.dfg.is_block_linked(jt.destination), + self.function.dfg.is_block_linked(info.destination), "reference to detached block in attached block {}", - jt.destination + info.destination ); - writeln!(f, " {block_id} --> {}", jt.destination)?; - block_q.push_back(jt.destination); + writeln!(f, " {block_id} --> {}", info.destination)?; + block_q.push_back(info.destination); } } } diff --git a/hir/src/globals.rs b/hir/src/globals.rs index 198863e5f..ea775fe22 100644 --- a/hir/src/globals.rs +++ b/hir/src/globals.rs @@ -1,15 +1,16 @@ -use std::{ - alloc::Layout, - collections::{hash_map::DefaultHasher, BTreeMap}, +use alloc::{alloc::Layout, collections::BTreeMap}; +use core::{ fmt::{self, Write}, hash::{Hash, Hasher}, }; use cranelift_entity::entity_impl; use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink}; -use miden_diagnostics::Spanned; -use super::*; +use crate::{ + diagnostics::{miette, Diagnostic, Spanned}, + *, +}; /// The policy to apply to a global variable (or function) when linking /// together a program during code generation. @@ -79,21 +80,24 @@ intrusive_adapter!(pub GlobalVariableAdapter = UnsafeRef: Gl /// For example, two global variables with the same name, but differing /// types will result in this error, as there is no way to resolve the /// conflict. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, Diagnostic)] pub enum GlobalVariableError { /// There are multiple conflicting definitions of the given global symbol #[error( "invalid global variable: there are multiple conflicting definitions for symbol '{0}'" )] + #[diagnostic()] NameConflict(Ident), /// An attempt was made to set the initializer for a global that already has one #[error("cannot set an initializer for '{0}', it is already initialized")] + #[diagnostic()] AlreadyInitialized(Ident), /// The initializer data is invalid for the declared type of the given global, e.g. size /// mismatch. #[error( "invalid global variable initializer for '{0}': the data does not match the declared type" )] + #[diagnostic()] InvalidInit(Ident), } @@ -389,7 +393,7 @@ impl GlobalVariableTable { let unique_id = self.next_unique_id; self.next_unique_id += 1; // Calculate the hash of the global variable data - let mut hasher = DefaultHasher::new(); + let mut hasher = rustc_hash::FxHasher::default(); data.hash(&mut hasher); unique_id.hash(&mut hasher); let hash = hasher.finish(); @@ -675,7 +679,8 @@ impl Default for GlobalValue { /// known statically, we instructions which manipulate globals are converted to /// loads/stores using constant addresses when translated to MASM. /// -/// Like other entities, globals may also have a [SourceSpan] associated with them. +/// Like other entities, globals may also have a [crate::diagnostics::SourceSpan] associated with +/// them. #[derive(Debug, Clone)] pub enum GlobalValueData { /// A symbolic reference to a global variable symbol diff --git a/hir/src/ident.rs b/hir/src/ident.rs index 2bf846b23..27ecb1054 100644 --- a/hir/src/ident.rs +++ b/hir/src/ident.rs @@ -6,9 +6,9 @@ use core::{ }; use anyhow::anyhow; -use miden_diagnostics::{SourceSpan, Spanned}; -use super::{ +use crate::{ + diagnostics::{SourceSpan, Spanned}, formatter::{self, PrettyPrint}, symbols, Symbol, }; @@ -20,6 +20,17 @@ pub struct FunctionIdent { #[span] pub function: Ident, } +impl FunctionIdent { + pub fn display(&self) -> impl fmt::Display + '_ { + use crate::formatter::*; + + flatten( + const_text(self.module.as_str()) + + const_text("::") + + const_text(self.function.as_str()), + ) + } +} impl FromStr for FunctionIdent { type Err = anyhow::Error; diff --git a/hir/src/immediates.rs b/hir/src/immediates.rs index 4b7b05f16..1c211bdfd 100644 --- a/hir/src/immediates.rs +++ b/hir/src/immediates.rs @@ -1,4 +1,4 @@ -use std::{ +use core::{ fmt, hash::{Hash, Hasher}, }; diff --git a/hir/src/instruction.rs b/hir/src/instruction.rs index 1dc3e197f..36eae627f 100644 --- a/hir/src/instruction.rs +++ b/hir/src/instruction.rs @@ -1,12 +1,15 @@ use core::ops::{Deref, DerefMut}; +use std::collections::BTreeSet; use cranelift_entity::entity_impl; use intrusive_collections::{intrusive_adapter, LinkedListLink}; -use miden_diagnostics::{Span, Spanned}; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use self::formatter::PrettyPrint; -use super::*; +use crate::{ + diagnostics::{Span, Spanned}, + *, +}; /// A handle to a single instruction #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -125,15 +128,26 @@ impl Instruction { ..call.clone() }), Self::Br(br) => Self::Br(Br { - args: br.args.deep_clone(value_lists), + successor: br.successor.deep_clone(value_lists), ..br.clone() }), Self::CondBr(br) => Self::CondBr(CondBr { - then_dest: (br.then_dest.0, br.then_dest.1.deep_clone(value_lists)), - else_dest: (br.else_dest.0, br.else_dest.1.deep_clone(value_lists)), + then_dest: br.then_dest.deep_clone(value_lists), + else_dest: br.else_dest.deep_clone(value_lists), ..br.clone() }), - Self::Switch(op) => Self::Switch(op.clone()), + Self::Switch(op) => Self::Switch(Switch { + arms: op + .arms + .iter() + .map(|arm| SwitchArm { + value: arm.value, + successor: arm.successor.deep_clone(value_lists), + }) + .collect(), + default: op.default.deep_clone(value_lists), + ..op.clone() + }), Self::Ret(op) => Self::Ret(Ret { args: op.args.deep_clone(value_lists), ..op.clone() @@ -255,23 +269,27 @@ impl Instruction { pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> { match self { - Self::Br(ref b) => BranchInfo::SingleDest(b.destination, b.args.as_slice(pool)), + Self::Br(Br { successor, .. }) => { + BranchInfo::SingleDest(SuccessorInfo::new(successor, pool)) + } Self::CondBr(CondBr { ref then_dest, ref else_dest, .. }) => BranchInfo::MultiDest(vec![ - JumpTable::new(then_dest.0, then_dest.1.as_slice(pool)), - JumpTable::new(else_dest.0, else_dest.1.as_slice(pool)), + SuccessorInfo::new(then_dest, pool), + SuccessorInfo::new(else_dest, pool), ]), Self::Switch(Switch { ref arms, ref default, .. }) => { - let mut targets = - arms.iter().map(|(_, b)| JumpTable::new(*b, &[])).collect::>(); - targets.push(JumpTable::new(*default, &[])); + let mut targets = arms + .iter() + .map(|succ| SuccessorInfo::new(&succ.successor, pool)) + .collect::>(); + targets.push(SuccessorInfo::new(default, pool)); BranchInfo::MultiDest(targets) } _ => BranchInfo::NotABranch, @@ -289,19 +307,8 @@ impl Instruction { #[derive(Debug)] pub enum BranchInfo<'a> { NotABranch, - SingleDest(Block, &'a [Value]), - MultiDest(Vec>), -} - -#[derive(Debug)] -pub struct JumpTable<'a> { - pub destination: Block, - pub args: &'a [Value], -} -impl<'a> JumpTable<'a> { - pub fn new(destination: Block, args: &'a [Value]) -> Self { - Self { destination, args } - } + SingleDest(SuccessorInfo<'a>), + MultiDest(Vec>), } pub enum CallInfo<'a> { @@ -1007,10 +1014,7 @@ pub struct Call { #[derive(Debug, Clone)] pub struct Br { pub op: Opcode, - pub destination: Block, - /// NOTE: Block arguments are always in stack order, i.e. the top operand on - /// the stack is the first block argument - pub args: ValueList, + pub successor: Successor, } /// Conditional Branch @@ -1018,12 +1022,8 @@ pub struct Br { pub struct CondBr { pub op: Opcode, pub cond: Value, - /// NOTE: Block arguments are always in stack order, i.e. the top operand on - /// the stack is the first block argument - pub then_dest: (Block, ValueList), - /// NOTE: Block arguments are always in stack order, i.e. the top operand on - /// the stack is the first block argument - pub else_dest: (Block, ValueList), + pub then_dest: Successor, + pub else_dest: Successor, } /// Multi-way Branch w/Selector @@ -1031,8 +1031,82 @@ pub struct CondBr { pub struct Switch { pub op: Opcode, pub arg: Value, - pub arms: Vec<(u32, Block)>, - pub default: Block, + pub arms: Vec, + pub default: Successor, +} + +#[derive(Debug, Clone)] +pub struct SwitchArm { + pub value: u32, + pub successor: Successor, +} + +#[derive(Debug, Clone)] +pub struct Successor { + pub destination: Block, + /// NOTE: Block arguments are always in stack order, i.e. the top operand on + /// the stack is the first block argument + pub args: ValueList, +} +impl Successor { + pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self { + Self { + destination: self.destination, + args: self.args.deep_clone(pool), + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct SuccessorInfo<'a> { + pub destination: Block, + pub args: &'a [Value], +} +impl<'a> SuccessorInfo<'a> { + pub fn new(successor: &Successor, pool: &'a ValueListPool) -> Self { + Self { + destination: successor.destination, + args: successor.args.as_slice(pool), + } + } +} +impl<'a> formatter::PrettyPrint for SuccessorInfo<'a> { + fn render(&self) -> miden_core::prettier::Document { + use crate::formatter::*; + + if self.args.is_empty() { + return self.destination.render(); + } + + let args = self + .args + .iter() + .copied() + .map(display) + .reduce(|acc, arg| acc + const_text(" ") + arg) + .map(|args| const_text(" ") + args) + .unwrap_or_default(); + const_text("(") + + const_text("block") + + const_text(" ") + + display(self.destination.as_u32()) + + args + + const_text(")") + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SuccessorInfoMut<'a> { + pub destination: Block, + pub args: &'a mut [Value], +} +impl<'a> SuccessorInfoMut<'a> { + pub fn new(successor: &'a mut Successor, pool: &'a mut ValueListPool) -> Self { + Self { + destination: successor.destination, + args: successor.args.as_mut_slice(pool), + } + } } /// Return @@ -1122,20 +1196,31 @@ impl<'a> PartialEq for InstructionWithValueListPool<'a> { && l.args.as_slice(self.value_lists) == r.args.as_slice(self.value_lists) } (Instruction::Br(l), Instruction::Br(r)) => { - l.destination == r.destination - && l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) + let l = SuccessorInfo::new(&l.successor, self.value_lists); + let r = SuccessorInfo::new(&r.successor, self.value_lists); + l == r } (Instruction::CondBr(l), Instruction::CondBr(r)) => { - l.cond == r.cond - && l.then_dest.0 == r.then_dest.0 - && l.else_dest.0 == r.else_dest.0 - && l.then_dest.1.as_slice(self.value_lists) - == r.then_dest.1.as_slice(other.value_lists) - && l.else_dest.1.as_slice(self.value_lists) - == r.else_dest.1.as_slice(other.value_lists) + let l_then = SuccessorInfo::new(&l.then_dest, self.value_lists); + let l_else = SuccessorInfo::new(&l.else_dest, self.value_lists); + let r_then = SuccessorInfo::new(&r.then_dest, self.value_lists); + let r_else = SuccessorInfo::new(&r.else_dest, self.value_lists); + l.cond == r.cond && l_then == r_then && l_else == r_else } (Instruction::Switch(l), Instruction::Switch(r)) => { - l.arg == r.arg && l.default == r.default && l.arms == r.arms + if l.arg != r.arg { + return false; + } + let l_arms = BTreeSet::from_iter( + l.arms.iter().map(|arm| SuccessorInfo::new(&arm.successor, self.value_lists)), + ); + let r_arms = BTreeSet::from_iter( + r.arms.iter().map(|arm| SuccessorInfo::new(&arm.successor, self.value_lists)), + ); + let same_arms = l_arms == r_arms; + let same_default = SuccessorInfo::new(&l.default, self.value_lists) + == SuccessorInfo::new(&r.default, self.value_lists); + same_arms && same_default } (Instruction::Ret(l), Instruction::Ret(r)) => { l.args.as_slice(self.value_lists) == r.args.as_slice(other.value_lists) @@ -1286,94 +1371,42 @@ impl<'a> formatter::PrettyPrint for InstPrettyPrinter<'a> { } Instruction::CondBr(CondBr { cond, - then_dest, - else_dest, + ref then_dest, + ref else_dest, .. - }) => { - let then_dest = if then_dest.1.is_empty() { - then_dest.0.render() - } else { - let then_args = then_dest - .1 - .as_slice(&self.dfg.value_lists) - .iter() - .copied() - .map(display) - .reduce(|acc, arg| acc + const_text(" ") + arg) - .map(|args| const_text(" ") + args) - .unwrap_or_default(); - const_text("(") - + const_text("block") - + const_text(" ") - + display(then_dest.0.as_u32()) - + then_args - + const_text(")") - }; - let else_dest = if else_dest.1.is_empty() { - else_dest.0.render() - } else { - let else_args = else_dest - .1 - .as_slice(&self.dfg.value_lists) - .iter() - .copied() - .map(display) - .reduce(|acc, arg| acc + const_text(" ") + arg) - .map(|args| const_text(" ") + args) - .unwrap_or_default(); - const_text("(") - + const_text("block") - + const_text(" ") - + display(else_dest.0.as_u32()) - + else_args - + const_text(")") - }; - (vec![], vec![display(*cond), then_dest, else_dest]) - } - Instruction::Br(Br { - destination, args, .. - }) => { - if args.is_empty() { - (vec![], vec![destination.render()]) - } else { - let dest_args = args - .as_slice(&self.dfg.value_lists) - .iter() - .copied() - .map(display) - .reduce(|acc, e| acc + const_text(" ") + e) - .map(|args| const_text(" ") + args) - .unwrap_or_default(); - let dest = const_text("(") - + const_text("block") - + const_text(" ") - + display(destination.as_u32()) - + dest_args - + const_text(")"); - (vec![], vec![dest]) - } + }) => ( + vec![], + vec![ + display(*cond), + SuccessorInfo::new(then_dest, &self.dfg.value_lists).render(), + SuccessorInfo::new(else_dest, &self.dfg.value_lists).render(), + ], + ), + Instruction::Br(Br { ref successor, .. }) => { + (vec![], vec![SuccessorInfo::new(successor, &self.dfg.value_lists).render()]) } Instruction::Switch(Switch { - arg, arms, default, .. + arg, + arms, + ref default, + .. }) => { let default = const_text("(") + const_text("_") + const_text(" ") + const_text(".") + const_text(" ") - + const_text("(") - + display(*default) - + const_text(")") + + SuccessorInfo::new(default, &self.dfg.value_lists).render() + const_text(")"); let arms = arms .iter() - .map(|(value, dest)| { + .map(|arm| { const_text("(") - + display(*value) + + display(arm.value) + const_text(" ") + const_text(".") + const_text(" ") - + dest.render() + + SuccessorInfo::new(&arm.successor, &self.dfg.value_lists).render() + const_text(")") }) .chain(core::iter::once(default)) diff --git a/hir/src/layout.rs b/hir/src/layout.rs index 8a558e6d5..729dc7e82 100644 --- a/hir/src/layout.rs +++ b/hir/src/layout.rs @@ -1,4 +1,4 @@ -use std::{ +use core::{ ops::{Index, IndexMut}, ptr::NonNull, }; diff --git a/hir/src/lib.rs b/hir/src/lib.rs index 6cd6578dc..773579f5b 100644 --- a/hir/src/lib.rs +++ b/hir/src/lib.rs @@ -20,10 +20,10 @@ extern crate lalrpop_util; pub use intrusive_collections::UnsafeRef; pub use miden_core::{FieldElement, StarkField}; -pub use miden_diagnostics::SourceSpan; pub use midenc_hir_macros::*; pub use midenc_hir_symbol::{symbols, Symbol}; pub use midenc_hir_type::{AddressSpace, Alignable, FunctionType, StructType, Type, TypeRepr}; +pub use midenc_session::diagnostics::{self, SourceSpan}; /// Represents a field element in Miden pub type Felt = miden_core::Felt; @@ -80,66 +80,44 @@ macro_rules! diagnostic { ($diagnostics:ident, $severity:expr, $msg:literal, $span:expr, $label:expr) => {{ let span = $span; - if span.is_unknown() { - $diagnostics.diagnostic($severity).with_message($msg).with_note($label).emit(); - } else { - $diagnostics - .diagnostic($severity) - .with_message($msg) - .with_primary_label($span, $label) - .emit(); - } + $diagnostics + .diagnostic($severity) + .with_message($msg) + .with_primary_label($span, $label) + .emit(); }}; ($diagnostics:ident, $severity:expr, $msg:literal, $span:expr, $label:expr, $note:expr) => {{ let span = $span; - if span.is_unknown() { - $diagnostics - .diagnostic($severity) - .with_message($msg) - .with_note($label) - .with_note($note) - .emit(); - } else { - $diagnostics - .diagnostic($severity) - .with_message($msg) - .with_primary_label(span, $label) - .with_note($note) - .emit(); - } + $diagnostics + .diagnostic($severity) + .with_message($msg) + .with_primary_label(span, $label) + .with_note($note) + .emit(); }}; ($diagnostics:ident, $severity:expr, $msg:literal, $span:expr, $label:expr, $span2:expr, $label2:expr) => {{ let span = $span; let span2 = $span2; - let diag = $diagnostics.diagnostic($severity).with_message($msg); - if span.is_unknown() { - diag.with_note($label); - } else { - diag.with_primary_label(span, $label); - } - if span2.is_unknown() { - diag.with_note($label2).emit(); - } else { - diag.with_secondary_label(span2, $label2).emit(); - } + $diagnostics + .diagnostic($severity) + .with_message($msg) + .with_primary_label(span, $label) + .with_secondary_label(span2, $label2) + .emit(); }}; ($diagnostics:ident, $severity:expr, $msg:literal, $span:expr, $label:expr, $span2:expr, $label2:expr, $note:expr) => {{ let span = $span; let span2 = $span2; - let diag = $diagnostics.diagnostic($severity).with_message($msg); - if span.is_unknown() { - diag.with_note($label); - } else { - diag.with_primary_label(span, $label); - } - if span2.is_unknown() { - diag.with_note($label2).with_note($note).emit(); - } else { - diag.with_secondary_label(span2, $label2).with_note($note).emit(); - } + $diagnostics + .diagnostic($severity) + .with_message($msg) + .with_primary_label(span, $label) + .with_secondary_label(span2, $label2) + .with_help($note) + .emit(); }}; } @@ -197,7 +175,7 @@ pub use self::{ AnalysisKey, ConversionPassRegistration, ModuleRewritePassAdapter, PassInfo, RewritePassRegistration, }, - program::{Linker, LinkerError, Program, ProgramAnalysisKey, ProgramBuilder}, + program::{Linker, Program, ProgramAnalysisKey, ProgramBuilder}, segments::{DataSegment, DataSegmentAdapter, DataSegmentError, DataSegmentTable}, value::{Value, ValueData, ValueList, ValueListPool}, }; diff --git a/hir/src/locals.rs b/hir/src/locals.rs index fc67d09c5..31431e7d7 100644 --- a/hir/src/locals.rs +++ b/hir/src/locals.rs @@ -1,4 +1,5 @@ -use std::{alloc::Layout, fmt}; +use alloc::alloc::Layout; +use core::fmt; use super::Type; diff --git a/hir/src/module.rs b/hir/src/module.rs index 4a5d55998..366c3bbb2 100644 --- a/hir/src/module.rs +++ b/hir/src/module.rs @@ -5,16 +5,31 @@ use intrusive_collections::{ linked_list::{Cursor, CursorMut}, LinkedList, RBTreeLink, }; -use miden_diagnostics::{DiagnosticsHandler, Severity, Spanned}; use rustc_hash::FxHashSet; use self::formatter::PrettyPrint; -use super::*; +use crate::{ + diagnostics::{miette, Diagnostic, DiagnosticsHandler, Report, Severity, Spanned}, + *, +}; /// This error is raised when two modules conflict with the same symbol name -#[derive(Debug, thiserror::Error)] -#[error("module {} has already been declared", .0)] -pub struct ModuleConflictError(pub Ident); +#[derive(Debug, thiserror::Error, Diagnostic)] +#[error("module {} has already been declared", .name)] +#[diagnostic()] +pub struct ModuleConflictError { + #[label("duplicate declaration occurs here")] + pub span: SourceSpan, + pub name: Symbol, +} +impl ModuleConflictError { + pub fn new(name: Ident) -> Self { + Self { + span: name.span, + name: name.as_symbol(), + } + } +} intrusive_adapter!(pub ModuleTreeAdapter = Box: Module { link: RBTreeLink }); impl<'a> intrusive_collections::KeyAdapter<'a> for ModuleTreeAdapter { @@ -804,23 +819,20 @@ impl<'m> ModuleFunctionBuilder<'m> { DefaultInstBuilder::new(&mut self.function.dfg, self.position) } - pub fn build( - self, - diagnostics: &DiagnosticsHandler, - ) -> Result { + pub fn build(self, diagnostics: &DiagnosticsHandler) -> Result { let sig = self.function.signature(); match sig.linkage { Linkage::External | Linkage::Internal => (), linkage => { - diagnostics + return Err(diagnostics .diagnostic(Severity::Error) - .with_message(format!( - "invalid linkage ('{}') for function '{}'", - linkage, &self.function.id - )) - .with_note("Only 'external' and 'internal' linkage are valid for functions") - .emit(); - return Err(InvalidFunctionError); + .with_message("invalid function definition") + .with_primary_label( + self.function.span(), + format!("invalid linkage: '{linkage}'"), + ) + .with_help("Only 'external' and 'internal' linkage are valid for functions") + .into_report()); } } @@ -830,47 +842,47 @@ impl<'m> ModuleFunctionBuilder<'m> { match sig.cc { CallConv::Kernel if is_kernel_module => { if !is_public { - diagnostics + return Err(diagnostics .diagnostic(Severity::Error) - .with_message(format!( - "expected external linkage for kernel function '{}'", - &self.function.id - )) - .with_note( - "This function is private, but uses the 'kernel' calling convention. \ - It must either be made public, or use a different convention", + .with_message("invalid function definition") + .with_primary_label( + self.function.span(), + format!("expected 'external' linkage, but got '{}'", &sig.linkage), + ) + .with_help( + "Functions declared with the 'kernel' calling convention must have \ + 'external' linkage", ) - .emit(); - return Err(InvalidFunctionError); + .into_report()); } } CallConv::Kernel => { - diagnostics + return Err(diagnostics .diagnostic(Severity::Error) - .with_message(format!( - "invalid calling convention for function '{}'", - &self.function.id - )) - .with_note( + .with_message("invalid function definition") + .with_primary_label( + self.function.span(), + "unsupported use of 'kernel' calling convention", + ) + .with_help( "The 'kernel' calling convention is only allowed in kernel modules, on \ functions with external linkage", ) - .emit(); - return Err(InvalidFunctionError); + .into_report()); } - _ if is_kernel_module && is_public => { - diagnostics + cc if is_kernel_module && is_public => { + return Err(diagnostics .diagnostic(Severity::Error) - .with_message(format!( - "invalid calling convention for function '{}'", - &self.function.id - )) - .with_note( + .with_message("invalid function definition") + .with_primary_label( + self.function.span(), + format!("unsupported use of '{cc}' calling convention"), + ) + .with_help( "Functions with external linkage, must use the 'kernel' calling \ convention when defined in a kernel module", ) - .emit(); - return Err(InvalidFunctionError); + .into_report()); } _ => (), } @@ -881,6 +893,3 @@ impl<'m> ModuleFunctionBuilder<'m> { Ok(id) } } - -#[derive(Debug)] -pub struct InvalidFunctionError; diff --git a/hir/src/parser/ast/convert.rs b/hir/src/parser/ast/convert.rs index ecc43a202..30a75c24f 100644 --- a/hir/src/parser/ast/convert.rs +++ b/hir/src/parser/ast/convert.rs @@ -7,8 +7,7 @@ use midenc_session::Session; use super::*; use crate::{ - parser::ParseError, - pass::{AnalysisManager, ConversionError, ConversionPass, ConversionResult}, + pass::{AnalysisManager, ConversionPass, ConversionResult}, Immediate, Opcode, PassInfo, Signature, Type, }; @@ -43,7 +42,7 @@ impl ConversionPass for ConvertAstToHir { let mut remapped_constants = RemappedConstants::default(); for (constant_id, constant_data) in constants_by_id.into_iter() { if let Entry::Vacant(entry) = remapped_constants.entry(constant_id) { - let new_constant_id = module.globals.insert_constant(constant_data.item); + let new_constant_id = module.globals.insert_constant(constant_data.into_inner()); entry.insert(new_constant_id); } } @@ -55,7 +54,7 @@ impl ConversionPass for ConvertAstToHir { for (_, gv_data) in globals_by_id.into_iter() { unsafe { - module.globals.insert(gv_data.item); + module.globals.insert(gv_data.into_inner()); } } @@ -202,7 +201,7 @@ impl ConversionPass for ConvertAstToHir { "expected that {id} is always ahead of, or equal to {next_id}" ); if raw_id == next_raw_id { - f.dfg.values.push(data.item); + f.dfg.values.push(data.into_inner()); continue; } @@ -215,7 +214,7 @@ impl ConversionPass for ConvertAstToHir { inst: crate::Inst::reserved_value(), }); } - assert_eq!(f.dfg.values.push(data.item), id); + assert_eq!(f.dfg.values.push(data.into_inner()), id); } // Also record all of the instruction results @@ -233,7 +232,7 @@ impl ConversionPass for ConvertAstToHir { if used_imports.contains(id) { None } else { - Some((*id, ext.item.clone())) + Some((*id, ext.inner().clone())) } })); module.functions.push_back(function); @@ -245,7 +244,12 @@ impl ConversionPass for ConvertAstToHir { if is_valid { Ok(module) } else { - Err(ConversionError::Failed(ParseError::InvalidModule.into())) + Err(session + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("failed to validate '{}'", module.name)) + .with_help("One or more diagnostics have been emitted, see them for details") + .into_report()) } } } @@ -281,7 +285,7 @@ fn try_insert_inst( } => Some(Instruction::BinaryOp(BinaryOp { op, overflow, - args: [rhs.item, lhs.item], + args: [rhs.into_inner(), lhs.into_inner()], })), InstType::BinaryOp { opcode: op, @@ -293,7 +297,7 @@ fn try_insert_inst( Instruction::BinaryOpImm(BinaryOpImm { op, overflow, - arg: rhs.item, + arg: rhs.into_inner(), imm, }) }) @@ -319,7 +323,7 @@ fn try_insert_inst( } => Some(Instruction::UnaryOp(UnaryOp { op, overflow, - arg: operand.item, + arg: operand.into_inner(), })), InstType::UnaryOp { opcode: op, @@ -380,13 +384,15 @@ fn try_insert_inst( blockq.push_back(next); } let args = crate::ValueList::from_iter( - successor.args.iter().map(|arg| arg.item), + successor.args.iter().map(|arg| arg.into_inner()), &mut function.dfg.value_lists, ); Some(Instruction::Br(crate::Br { op, - destination: successor.id, - args, + successor: crate::Successor { + destination: successor.id, + args, + }, })) } Err(_) => None, @@ -438,18 +444,24 @@ fn try_insert_inst( if is_valid { let then_args = crate::ValueList::from_iter( - then_dest.args.iter().map(|arg| arg.item), + then_dest.args.iter().map(|arg| arg.into_inner()), &mut function.dfg.value_lists, ); let else_args = crate::ValueList::from_iter( - else_dest.args.iter().map(|arg| arg.item), + else_dest.args.iter().map(|arg| arg.into_inner()), &mut function.dfg.value_lists, ); Some(Instruction::CondBr(crate::CondBr { op, - cond: cond.item, - then_dest: (then_dest.id, then_args), - else_dest: (else_dest.id, else_args), + cond: cond.into_inner(), + then_dest: crate::Successor { + destination: then_dest.id, + args: then_args, + }, + else_dest: crate::Successor { + destination: else_dest.id, + args: else_args, + }, })) } else { None @@ -467,7 +479,7 @@ fn try_insert_inst( let mut arms = Vec::with_capacity(successors.len()); for arm in successors.into_iter() { let arm_span = arm.span(); - let discriminant = arm.item.0; + let discriminant = arm.inner().0; if !used_discriminants.insert(Span::new(arm_span, discriminant)) { let prev = used_discriminants .get(&Span::new(SourceSpan::UNKNOWN, discriminant)) @@ -484,8 +496,18 @@ fn try_insert_inst( .emit(); is_valid = false; } - let successor = arm.item.1; - arms.push((discriminant, successor.id)); + let successor = arm.into_inner().1; + let successor_args = crate::ValueList::from_iter( + successor.args.iter().map(|arg| arg.into_inner()), + &mut function.dfg.value_lists, + ); + arms.push(crate::SwitchArm { + value: discriminant, + successor: crate::Successor { + destination: successor.id, + args: successor_args, + }, + }); match is_valid_successor( &successor, span, @@ -504,6 +526,11 @@ fn try_insert_inst( } } } + + let fallback_args = crate::ValueList::from_iter( + fallback.args.iter().map(|arg| arg.into_inner()), + &mut function.dfg.value_lists, + ); match is_valid_successor( &fallback, span, @@ -525,9 +552,12 @@ fn try_insert_inst( if is_valid { Some(Instruction::Switch(crate::Switch { op, - arg: selector.item, + arg: selector.into_inner(), arms, - default: fallback.id, + default: crate::Successor { + destination: fallback.id, + args: fallback_args, + }, })) } else { None @@ -562,7 +592,7 @@ fn try_insert_inst( Operand::Value(v) => { if is_valid_value_reference(&v, span, values_by_id, diagnostics) { let args = crate::ValueList::from_slice( - &[v.item], + &[v.into_inner()], &mut function.dfg.value_lists, ); Some(Instruction::Ret(crate::Ret { op, args })) @@ -582,7 +612,7 @@ fn try_insert_inst( let mut args = crate::ValueList::new(); for operand in operands.iter() { if let Operand::Value(ref v) = operand { - args.push(v.item, &mut function.dfg.value_lists); + args.push(v.into_inner(), &mut function.dfg.value_lists); is_valid &= is_valid_value_reference(v, span, values_by_id, diagnostics); } else { @@ -624,7 +654,7 @@ fn try_insert_inst( if let Entry::Vacant(entry) = function.dfg.imports.entry(callee) { entry.insert(ExternalFunction { id: callee, - signature: sig.item.clone(), + signature: sig.inner().clone(), }); } } else { @@ -646,7 +676,7 @@ fn try_insert_inst( used_imports.insert(external); if let Entry::Vacant(entry) = function.dfg.imports.entry(external) { if let Some(ef) = imports_by_id.get(&external) { - entry.insert(ef.item.clone()); + entry.insert(ef.inner().clone()); } else { diagnostics .diagnostic(Severity::Error) @@ -669,7 +699,7 @@ fn try_insert_inst( is_valid_value_references(operands.as_slice(), span, values_by_id, diagnostics); if is_valid { let args = crate::ValueList::from_iter( - operands.iter().map(|arg| arg.item), + operands.iter().map(|arg| arg.into_inner()), &mut function.dfg.value_lists, ); Some(Instruction::Call(crate::Call { op, callee, args })) @@ -699,13 +729,13 @@ fn try_insert_inst( Operand::Value(v) => { is_valid &= is_valid_value_reference(&v, span, values_by_id, diagnostics); - args.push(v.item, &mut function.dfg.value_lists); + args.push(v.into_inner(), &mut function.dfg.value_lists); } operand @ (Operand::Int(_) | Operand::BigInt(_)) if is_first => { imm = match op { Opcode::AssertEq => { if let Some(value) = operands[i + 1].as_value() { - match values_by_id.get(&value.item).map(|vd| vd.ty()) { + match values_by_id.get(value.inner()).map(|vd| vd.ty()) { Some(ty) => { operand_to_immediate(operand, ty, diagnostics) } @@ -883,7 +913,7 @@ fn is_valid_value_reference( values_by_id: &ValuesById, diagnostics: &DiagnosticsHandler, ) -> bool { - let is_valid = values_by_id.contains_key(&value.item); + let is_valid = values_by_id.contains_key(value.inner()); if !is_valid { diagnostics .diagnostic(Severity::Error) @@ -969,8 +999,8 @@ fn operand_to_immediate( diagnostics: &DiagnosticsHandler, ) -> Option { match operand { - Operand::Int(i) => smallint_to_immediate(i.span(), i.item, ty, diagnostics), - Operand::BigInt(i) => bigint_to_immediate(i.span(), i.item, ty, diagnostics), + Operand::Int(i) => smallint_to_immediate(i.span(), i.into_inner(), ty, diagnostics), + Operand::BigInt(i) => bigint_to_immediate(i.span(), i.into_inner(), ty, diagnostics), Operand::Value(_) => panic!("cannot convert ssa values to immediate"), } } diff --git a/hir/src/parser/ast/functions.rs b/hir/src/parser/ast/functions.rs index 2d438ad25..dd566b76d 100644 --- a/hir/src/parser/ast/functions.rs +++ b/hir/src/parser/ast/functions.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{AttributeSet, Signature}; +use crate::{diagnostics::DiagnosticsHandler, AttributeSet, Signature}; /// Represents the declaration of a function in a [Module] #[derive(Spanned)] @@ -29,10 +29,7 @@ impl FunctionDeclaration { } /// Returns true if the entry block and signature match for this declaration - pub fn is_declaration_valid( - &self, - diagnostics: &miden_diagnostics::DiagnosticsHandler, - ) -> bool { + pub fn is_declaration_valid(&self, diagnostics: &DiagnosticsHandler) -> bool { let entry_block = &self.blocks[0]; if entry_block.params.len() != self.signature.arity() { let num_expected = entry_block.params.len(); @@ -82,7 +79,7 @@ impl FunctionDeclaration { pub(super) fn populate_block_map( &mut self, - diagnostics: &miden_diagnostics::DiagnosticsHandler, + diagnostics: &DiagnosticsHandler, ) -> Result { use alloc::collections::btree_map::Entry; diff --git a/hir/src/parser/ast/globals.rs b/hir/src/parser/ast/globals.rs index a8d6f2c44..b79788954 100644 --- a/hir/src/parser/ast/globals.rs +++ b/hir/src/parser/ast/globals.rs @@ -1,7 +1,6 @@ use core::fmt; -use miden_diagnostics::{SourceSpan, Spanned}; - +use super::{SourceSpan, Spanned}; use crate::{ConstantData, Ident, Linkage, Type}; /// This represents the declaration of a global variable diff --git a/hir/src/parser/ast/mod.rs b/hir/src/parser/ast/mod.rs index 47d33a1db..606c60316 100644 --- a/hir/src/parser/ast/mod.rs +++ b/hir/src/parser/ast/mod.rs @@ -7,10 +7,11 @@ mod instruction; use alloc::collections::BTreeMap; use core::fmt; -use miden_diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Span, Spanned}; - pub use self::{block::*, convert::ConvertAstToHir, functions::*, globals::*, instruction::*}; -use crate::{ExternalFunction, FunctionIdent, Ident}; +use crate::{ + diagnostics::{DiagnosticsHandler, Severity, SourceSpan, Span, Spanned}, + ExternalFunction, FunctionIdent, Ident, +}; /// This represents the parsed contents of a single Miden IR module #[derive(Spanned)] diff --git a/hir/src/parser/error.rs b/hir/src/parser/error.rs index 0b3ec4969..0d2b85294 100644 --- a/hir/src/parser/error.rs +++ b/hir/src/parser/error.rs @@ -1,47 +1,78 @@ -use miden_diagnostics::{Diagnostic, Label, SourceIndex, SourceSpan, ToDiagnostic}; - use super::lexer::{LexicalError, Token}; +use crate::{ + diagnostics::{miette, ByteIndex, Diagnostic, SourceSpan}, + DisplayValues, +}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, Diagnostic)] pub enum ParseError { + #[diagnostic(transparent)] #[error(transparent)] Lexer(#[from] LexicalError), #[error("error reading {path:?}: {source}")] + #[diagnostic()] FileError { + #[source] source: std::io::Error, path: std::path::PathBuf, }, #[error("invalid token")] - InvalidToken(SourceIndex), + #[diagnostic()] + InvalidToken(#[label] SourceSpan), #[error("unexpected end of file")] + #[diagnostic()] UnexpectedEof { - at: SourceIndex, + #[label("expected one of: {}", DisplayValues::new(expected.iter()))] + at: SourceSpan, expected: Vec, }, #[error("unrecognized token '{token}'")] UnrecognizedToken { + #[label("expected one of: {}", DisplayValues::new(expected.iter()))] span: SourceSpan, token: Token, expected: Vec, }, #[error("extraneous token '{token}'")] - ExtraToken { span: SourceSpan, token: Token }, + ExtraToken { + #[label] + span: SourceSpan, + token: Token, + }, #[error("expected valid u32 immediate value, got '{value}'")] - InvalidU32 { span: SourceSpan, value: isize }, + InvalidU32 { + #[label] + span: SourceSpan, + value: isize, + }, #[error("expected valid offset value, got '{value}'")] - InvalidOffset { span: SourceSpan, value: isize }, + InvalidOffset { + #[label] + span: SourceSpan, + value: isize, + }, #[error("expected valid alignment value, got '{value}'")] - InvalidAlignment { span: SourceSpan, value: isize }, + InvalidAlignment { + #[label] + span: SourceSpan, + value: isize, + }, #[error("expected valid address space, got '{value}'")] - InvalidAddrSpace { span: SourceSpan, value: isize }, - #[error("parsing succeeded, but validation failed, see diagnostics for details")] - InvalidModule, + InvalidAddrSpace { + #[label] + span: SourceSpan, + value: isize, + }, #[error("invalid function definition: cannot have empty body")] - EmptyFunction { span: SourceSpan }, + EmptyFunction { + #[label] + span: SourceSpan, + }, #[error("invalid function import declaration: cannot have body")] - ImportedFunctionWithBody { span: SourceSpan }, - #[error("parsing failed, see diagnostics for details")] - Failed, + ImportedFunctionWithBody { + #[label] + span: SourceSpan, + }, } impl Eq for ParseError {} impl PartialEq for ParseError { @@ -71,8 +102,6 @@ impl PartialEq for ParseError { }, ) => lt == rt && l == r, (Self::ExtraToken { token: l, .. }, Self::ExtraToken { token: r, .. }) => l == r, - (Self::InvalidModule, Self::InvalidModule) => true, - (Self::Failed, Self::Failed) => true, (Self::EmptyFunction { .. }, Self::EmptyFunction { .. }) => true, (Self::ImportedFunctionWithBody { .. }, Self::ImportedFunctionWithBody { .. }) => true, (Self::InvalidU32 { value: l, .. }, Self::InvalidU32 { value: r, .. }) => l == r, @@ -87,102 +116,36 @@ impl PartialEq for ParseError { } } } -impl From> for ParseError { - fn from(err: lalrpop_util::ParseError) -> Self { +impl From> for ParseError { + fn from(err: lalrpop_util::ParseError) -> Self { use lalrpop_util::ParseError as LError; match err { - LError::InvalidToken { location } => Self::InvalidToken(location), + LError::InvalidToken { location } => { + Self::InvalidToken(SourceSpan::at(Default::default(), location)) + } LError::UnrecognizedEof { location: at, expected, - } => Self::UnexpectedEof { at, expected }, + } => Self::UnexpectedEof { + at: SourceSpan::at(Default::default(), at), + expected, + }, LError::UnrecognizedToken { token: (l, token, r), expected, } => Self::UnrecognizedToken { - span: SourceSpan::new(l, r), + span: SourceSpan::new(Default::default(), l..r), token, expected, }, LError::ExtraToken { token: (l, token, r), } => Self::ExtraToken { - span: SourceSpan::new(l, r), + span: SourceSpan::new(Default::default(), l..r), token, }, LError::User { error } => error, } } } -impl ToDiagnostic for ParseError { - fn to_diagnostic(self) -> Diagnostic { - match self { - Self::Lexer(err) => err.to_diagnostic(), - Self::InvalidToken(start) => Diagnostic::error() - .with_message("invalid token") - .with_labels(vec![Label::primary( - start.source_id(), - SourceSpan::new(start, start), - )]), - Self::UnexpectedEof { at, ref expected } => { - let mut message = "expected one of: ".to_string(); - for (i, t) in expected.iter().enumerate() { - if i == 0 { - message.push_str(&format!("'{}'", t)); - } else { - message.push_str(&format!(", '{}'", t)); - } - } - - Diagnostic::error() - .with_message("unexpected eof") - .with_labels(vec![Label::primary(at.source_id(), SourceSpan::new(at, at)) - .with_message(message)]) - } - Self::UnrecognizedToken { - span, ref expected, .. - } => { - let mut message = "expected one of: ".to_string(); - for (i, t) in expected.iter().enumerate() { - if i == 0 { - message.push_str(&format!("'{}'", t)); - } else { - message.push_str(&format!(", '{}'", t)); - } - } - - Diagnostic::error() - .with_message("unexpected token") - .with_labels(vec![Label::primary(span.source_id(), span).with_message(message)]) - } - Self::ExtraToken { span, .. } => Diagnostic::error() - .with_message("extraneous token") - .with_labels(vec![Label::primary(span.source_id(), span)]), - Self::InvalidU32 { span, .. } => Diagnostic::error() - .with_message("expected valid unsigned 32-bit immediate value") - .with_labels(vec![Label::primary(span.source_id(), span)]), - Self::InvalidOffset { span, .. } => Diagnostic::error() - .with_message("expected valid 32-bit offset value") - .with_labels(vec![Label::primary(span.source_id(), span)]), - Self::InvalidAlignment { span, .. } => Diagnostic::error() - .with_message("expected valid alignment value") - .with_labels(vec![Label::primary(span.source_id(), span).with_message( - "alignment must be a non-zero, power of two, valid for a 32-bit address space", - )]), - Self::InvalidAddrSpace { span, .. } => Diagnostic::error() - .with_message("expected valid address space") - .with_labels(vec![Label::primary(span.source_id(), span) - .with_message("address space must be a value in 1..=65535")]), - Self::EmptyFunction { span } => Diagnostic::error() - .with_message("invalid function definition") - .with_labels(vec![Label::primary(span.source_id(), span) - .with_message("cannot have an empty body")]), - Self::ImportedFunctionWithBody { span } => Diagnostic::error() - .with_message("invalid function import declaration") - .with_labels(vec![Label::primary(span.source_id(), span) - .with_message("function import declarations cannot have a body")]), - err => Diagnostic::error().with_message(err.to_string()), - } - } -} diff --git a/hir/src/parser/grammar.lalrpop b/hir/src/parser/grammar.lalrpop index b699e4c75..03bf44d9f 100644 --- a/hir/src/parser/grammar.lalrpop +++ b/hir/src/parser/grammar.lalrpop @@ -1,9 +1,8 @@ -use alloc::sync::Arc; use core::num::NonZeroU16; use either::Either::{self, Left, Right}; -use miden_diagnostics::{CodeMap, DiagnosticsHandler, Span, Spanned, SourceSpan}; +use crate::diagnostics::{Span, SourceId, SourceSpan, ByteIndex}; use crate::{AbiParam, ArgumentExtension, ArgumentPurpose}; use crate::{CallConv, ConstantData, ExternalFunction, FunctionIdent}; use crate::{Ident, Linkage, Opcode, Overflow, Signature, symbols, Symbol}; @@ -85,7 +84,7 @@ use crate::parser::{ /// BARE_NAME ::= [^[[:cntrl:]]:;,'"\s\\]+ -grammar(diagnostics: &DiagnosticsHandler, codemap: &Arc, next_var: &mut usize); +grammar(source_id: SourceId, next_var: &mut usize); // MACROS // ================================================================================================ @@ -116,7 +115,7 @@ CommaOpt: Vec = { pub Module: Module = { "(" "module" ")" => { let is_kernel = attrs.iter().any(|attr| attr.name == symbols::Kernel && attr.value.as_bool().unwrap_or_default()); - Module::new(span!(l, r), name, is_kernel, forms) + Module::new(span!(source_id, l, r), name, is_kernel, forms) }, } @@ -136,13 +135,13 @@ ModuleForm: Form = { ConstantDeclaration: ConstantDeclaration = { "(" "const" ")" - => ConstantDeclaration::new(span!(l, r), crate::Constant::from_u32(id), init), + => ConstantDeclaration::new(span!(source_id, l, r), crate::Constant::from_u32(id), init), } GlobalVarDeclaration: GlobalVarDeclaration = { "(" "global" ")" => { let (name, linkage) = name_and_linkage; - GlobalVarDeclaration::new(span!(l, r), crate::GlobalVariable::from_u32(id), name, ty, linkage, init) + GlobalVarDeclaration::new(span!(source_id, l, r), crate::GlobalVariable::from_u32(id), name, ty, linkage, init) } } @@ -150,7 +149,7 @@ DataSegmentDeclaration: DataSegmentDeclaration = { "(" "data" ")")> ")")?> ")" => { let readonly = is_mut.is_none(); let size = size.unwrap_or(data.len().try_into().expect("invalid data segment: data cannot be more than 2^32 bytes")); - DataSegmentDeclaration::new(span!(l, r), offset, size, readonly, data) + DataSegmentDeclaration::new(span!(source_id, l, r), offset, size, readonly, data) } } @@ -232,7 +231,7 @@ AddressSpace: AddressSpace = { match u16::try_from(i) { Ok(0) => Ok(AddressSpace::Root), Ok(v) => Ok(AddressSpace::Id(unsafe { NonZeroU16::new_unchecked(v) })), - Err(_) => Err(ParseError::InvalidAddrSpace { span: span!(l, r), value: i }.into()), + Err(_) => Err(ParseError::InvalidAddrSpace { span: span!(source_id, l, r), value: i }.into()), } } } @@ -279,13 +278,13 @@ FunctionDeclaration: Form = { linkage, }; match name { - Left(name) if blocks.is_empty() => Err(ParseError::EmptyFunction { span: span!(l, r) }.into()), + Left(name) if blocks.is_empty() => Err(ParseError::EmptyFunction { span: span!(source_id, l, r) }.into()), Left(name) => { - Ok(Form::Function(FunctionDeclaration::new(span!(l, r), name, signature, blocks, AttributeSet::default()))) + Ok(Form::Function(FunctionDeclaration::new(span!(source_id, l, r), name, signature, blocks, AttributeSet::default()))) } - Right(id) if !blocks.is_empty() => Err(ParseError::ImportedFunctionWithBody { span: span!(l, r) }.into()), + Right(id) if !blocks.is_empty() => Err(ParseError::ImportedFunctionWithBody { span: span!(source_id, l, r) }.into()), Right(id) => { - Ok(Form::ExternalFunction(Span::new(span!(l, r), ExternalFunction { + Ok(Form::ExternalFunction(Span::new(span!(source_id, l, r), ExternalFunction { id, signature, }))) @@ -335,19 +334,19 @@ ArgumentExtension: ArgumentExtension = { Block: Block = { "(" "block" ")" => { let id = crate::Block::from_u32(id); - Block::new(span!(l, r), id, params, insts) + Block::new(span!(source_id, l, r), id, params, insts) } } BlockParam: TypedValue = { - "(" "param" ")" => TypedValue::new(span!(l, r), id, ty), + "(" "param" ")" => TypedValue::new(span!(source_id, l, r), id, ty), } // INSTRUCTIONS // ================================================================================================ TypedValueId: TypedValue = { - "(" ")" => TypedValue::new(value.span(), value.item, ty), + "(" ")" => TypedValue::new(value.span(), value.into_inner(), ty), } Let: (TypedValue, Span) = { @@ -365,37 +364,37 @@ LetMany: (Vec, Span) = { Inst: Inst = { => { let (value, inst_ty) = let_expr; - Inst::new(span!(l, r), inst_ty.item, vec![value]) + Inst::new(span!(source_id, l, r), inst_ty.into_inner(), vec![value]) }, => { let (values, inst_ty) = let_expr; - Inst::new(span!(l, r), inst_ty.item, values) + Inst::new(span!(source_id, l, r), inst_ty.into_inner(), values) }, "(" "call" ")" => { - Inst::new(span!(l, r), InstType::Call { opcode: Opcode::Call, callee, operands }, vec![]) + Inst::new(span!(source_id, l, r), InstType::Call { opcode: Opcode::Call, callee, operands }, vec![]) }, "(" "syscall" ")" => { - Inst::new(span!(l, r), InstType::Call { opcode: Opcode::Syscall, callee, operands }, vec![]) + Inst::new(span!(source_id, l, r), InstType::Call { opcode: Opcode::Syscall, callee, operands }, vec![]) }, "(" "unreachable" ")" => { - Inst::new(span!(l, r), InstType::PrimOp { opcode: Opcode::Unreachable, operands: vec![] }, vec![]) + Inst::new(span!(source_id, l, r), InstType::PrimOp { opcode: Opcode::Unreachable, operands: vec![] }, vec![]) }, "(" "ret" ")" => { let operands = operand.map(|operand| vec![operand]).unwrap_or_default(); - Inst::new(span!(l, r), InstType::Ret { opcode: Opcode::Ret, operands }, vec![]) + Inst::new(span!(source_id, l, r), InstType::Ret { opcode: Opcode::Ret, operands }, vec![]) }, "(" "br" ")" => { - Inst::new(span!(l, r), InstType::Br { opcode: Opcode::Br, successor }, vec![]) + Inst::new(span!(source_id, l, r), InstType::Br { opcode: Opcode::Br, successor }, vec![]) }, "(" "condbr" ")" => { - Inst::new(span!(l, r), InstType::CondBr { opcode: Opcode::CondBr, cond, then_dest, else_dest }, vec![]) + Inst::new(span!(source_id, l, r), InstType::CondBr { opcode: Opcode::CondBr, cond, then_dest, else_dest }, vec![]) }, "(" "switch" ")" =>? { @@ -411,27 +410,27 @@ Inst: Inst = { (None, _, _) => panic!("invalid switch: only one default arm is allowed"), } } - Ok(Inst::new(span!(l, r), InstType::Switch { opcode: Opcode::Switch, selector, successors, fallback }, vec![])) + Ok(Inst::new(span!(source_id, l, r), InstType::Switch { opcode: Opcode::Switch, selector, successors, fallback }, vec![])) }, } InstWithResult: Span = { "(" ")" => { let (opcode, overflow) = op_and_overflow; - Span::new(span!(l, r), InstType::UnaryOp { opcode, overflow, operand }) + Span::new(span!(source_id, l, r), InstType::UnaryOp { opcode, overflow, operand }) }, "(" ")" => { let (opcode, overflow) = op_and_overflow; - Span::new(span!(l, r), InstType::BinaryOp { opcode, overflow, operands: [lhs, rhs] }) + Span::new(span!(source_id, l, r), InstType::BinaryOp { opcode, overflow, operands: [lhs, rhs] }) }, "(" "call" ")" => { - Span::new(span!(l, r), InstType::Call { opcode: Opcode::Call, callee, operands }) + Span::new(span!(source_id, l, r), InstType::Call { opcode: Opcode::Call, callee, operands }) }, "(" "syscall" ")" => { - Span::new(span!(l, r), InstType::Call { opcode: Opcode::Syscall, callee, operands }) + Span::new(span!(source_id, l, r), InstType::Call { opcode: Opcode::Syscall, callee, operands }) }, => Span::new(expr.span(), InstType::GlobalValue { opcode: Opcode::GlobalValue, expr }), @@ -440,27 +439,27 @@ InstWithResult: Span = { InstWithManyResults: Span = { "(" ")" => { let overflow = Some(Overflow::Overflowing); - Span::new(span!(l, r), InstType::UnaryOp { opcode, overflow, operand }) + Span::new(span!(source_id, l, r), InstType::UnaryOp { opcode, overflow, operand }) }, "(" ")" => { let overflow = Some(Overflow::Overflowing); - Span::new(span!(l, r), InstType::BinaryOp { opcode, overflow, operands: [lhs, rhs] }) + Span::new(span!(source_id, l, r), InstType::BinaryOp { opcode, overflow, operands: [lhs, rhs] }) }, "(" ")" => { - Span::new(span!(l, r), InstType::PrimOp { opcode, operands }) + Span::new(span!(source_id, l, r), InstType::PrimOp { opcode, operands }) } } Successor: Successor = { "(" "block" ")" - => Successor { span: span!(l, r), id: crate::Block::from_u32(id), args }, + => Successor { span: span!(source_id, l, r), id: crate::Block::from_u32(id), args }, } SwitchArm: (Option, Successor, SourceSpan) = { - "(" "." ")" => (Some(value), successor, span!(l, r)), - "(" "_" "." ")" => (None, successor, span!(l, r)), + "(" "." ")" => (Some(value), successor, span!(source_id, l, r)), + "(" "_" "." ")" => (None, successor, span!(source_id, l, r)), } UnaryOpcode: (Opcode, Option) = { @@ -563,23 +562,23 @@ PrimOpOpcode: Opcode = { } Operand: Operand = { - => Operand::Value(Span::new(span!(l, r), v)), - => Operand::Int(Span::new(span!(l, r), i)), - => Operand::BigInt(Span::new(span!(l, r), i)), + => Operand::Value(Span::new(span!(source_id, l, r), v)), + => Operand::Int(Span::new(span!(source_id, l, r), i)), + => Operand::BigInt(Span::new(span!(source_id, l, r), i)), } GlobalValueExpr: GlobalValueExpr = { "(" "global.symbol" ")" => { - GlobalValueExpr::Symbol { symbol, offset: offset.unwrap_or(0), span: span!(l, r) } + GlobalValueExpr::Symbol { symbol, offset: offset.unwrap_or(0), span: span!(source_id, l, r) } }, "(" "global.load" ")" => { let offset = offset.unwrap_or(0); - GlobalValueExpr::Load { base: Box::new(base), offset, ty: Some(ty), span: span!(l, r) } + GlobalValueExpr::Load { base: Box::new(base), offset, ty: Some(ty), span: span!(source_id, l, r) } }, "(" "global.iadd" "(" "offset" "." ")" ")" => { - GlobalValueExpr::IAddImm { base: Box::new(base), offset, ty, span: span!(l, r) } + GlobalValueExpr::IAddImm { base: Box::new(base), offset, ty, span: span!(source_id, l, r) } } } @@ -605,9 +604,9 @@ Index: u32 = { Align: NonZeroU16 = { =>? { match u16::try_from(i) { - Ok(0) => Err(ParseError::InvalidAlignment { span: span!(l, r), value: i }.into()), + Ok(0) => Err(ParseError::InvalidAlignment { span: span!(source_id, l, r), value: i }.into()), Ok(v) => Ok(unsafe { NonZeroU16::new_unchecked(v) }), - Err(_) => Err(ParseError::InvalidAlignment { span: span!(l, r), value: i }.into()), + Err(_) => Err(ParseError::InvalidAlignment { span: span!(source_id, l, r), value: i }.into()), } } } @@ -616,7 +615,7 @@ U32: u32 = { =>? { match u32::try_from(i) { Ok(v) => Ok(v), - Err(_) => Err(ParseError::InvalidU32 { span: span!(l, r), value: i }.into()), + Err(_) => Err(ParseError::InvalidU32 { span: span!(source_id, l, r), value: i }.into()), } } } @@ -625,17 +624,17 @@ RawOffset: i32 = { =>? { match i32::try_from(i) { Ok(v) => Ok(v), - Err(_) => Err(ParseError::InvalidOffset { span: span!(l, r), value: i }.into()), + Err(_) => Err(ParseError::InvalidOffset { span: span!(source_id, l, r), value: i }.into()), } } } Name: Ident = { - => Ident::new(id, span!(l, r)), + => Ident::new(id, span!(source_id, l, r)), } Id: Ident = { - => Ident::new(id, span!(l, r)), + => Ident::new(id, span!(source_id, l, r)), } NameOrId: Ident = { @@ -649,7 +648,7 @@ CalleeId: Either = { } SpannedValueId: Span = { - => Span::new(span!(l, r), v), + => Span::new(span!(source_id, l, r), v), } ValueId: crate::Value = { @@ -661,7 +660,7 @@ ValueId: crate::Value = { extern { type Error = ParseError; - type Location = miden_diagnostics::SourceIndex; + type Location = ByteIndex; enum Token { ident => Token::Ident(), diff --git a/hir/src/parser/lexer/error.rs b/hir/src/parser/lexer/error.rs index 9a3413e43..55a2319c0 100644 --- a/hir/src/parser/lexer/error.rs +++ b/hir/src/parser/lexer/error.rs @@ -1,6 +1,6 @@ use core::{fmt, num::IntErrorKind}; -use miden_diagnostics::{Diagnostic, SourceIndex, SourceSpan, ToDiagnostic}; +use crate::diagnostics::{miette, Diagnostic, SourceSpan}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum InvalidEscapeKind { @@ -19,31 +19,57 @@ impl fmt::Display for InvalidEscapeKind { } /// Errors that may occur during lexing of the source -#[derive(Clone, Debug, thiserror::Error)] +#[derive(Clone, Debug, thiserror::Error, Diagnostic)] pub enum LexicalError { #[error("invalid integer value: {}", DisplayIntErrorKind(reason))] + #[diagnostic()] InvalidInt { + #[label] span: SourceSpan, reason: IntErrorKind, }, #[error("encountered unexpected character '{found}'")] - UnexpectedCharacter { start: SourceIndex, found: char }, + #[diagnostic()] + UnexpectedCharacter { + #[label] + start: SourceSpan, + found: char, + }, #[error("unclosed string")] - UnclosedString { span: SourceSpan }, + #[diagnostic()] + UnclosedString { + #[label] + span: SourceSpan, + }, #[error("invalid unicode escape: {kind}")] + #[diagnostic()] InvalidUnicodeEscape { + #[label] span: SourceSpan, kind: InvalidEscapeKind, }, #[error("invalid hex escape: {kind}")] + #[diagnostic()] InvalidHexEscape { + #[label] span: SourceSpan, kind: InvalidEscapeKind, }, #[error("invalid module identifier")] - InvalidModuleIdentifier { span: SourceSpan }, + #[diagnostic(help( + "module names must be non-empty, start with 'a-z', and only contain ascii alpha-numeric \ + characters, '_', or '::' as a namespacing operator", + ))] + InvalidModuleIdentifier { + #[label] + span: SourceSpan, + }, #[error("invalid function identifier")] - InvalidFunctionIdentifier { span: SourceSpan }, + #[diagnostic(help("function names must be non-empty, and start with '_' or 'a-z'"))] + InvalidFunctionIdentifier { + #[label] + span: SourceSpan, + }, } impl PartialEq for LexicalError { fn eq(&self, other: &Self) -> bool { @@ -71,47 +97,6 @@ impl PartialEq for LexicalError { } } } -impl ToDiagnostic for LexicalError { - fn to_diagnostic(self) -> Diagnostic { - use miden_diagnostics::Label; - - match self { - Self::InvalidInt { span, ref reason } => Diagnostic::error() - .with_message("invalid integer literal") - .with_labels(vec![Label::primary(span.source_id(), span) - .with_message(format!("{}", DisplayIntErrorKind(reason)))]), - Self::UnexpectedCharacter { start, .. } => { - Diagnostic::error().with_message("unexpected character").with_labels(vec![ - Label::primary(start.source_id(), SourceSpan::new(start, start)), - ]) - } - Self::UnclosedString { span, .. } => Diagnostic::error() - .with_message("unclosed string") - .with_labels(vec![Label::primary(span.source_id(), span)]), - Self::InvalidUnicodeEscape { span, kind } => { - Diagnostic::error().with_message("invalid unicode escape").with_labels(vec![ - Label::primary(span.source_id(), span).with_message(kind.to_string()), - ]) - } - Self::InvalidHexEscape { span, kind } => { - Diagnostic::error().with_message("invalid hex escape").with_labels(vec![ - Label::primary(span.source_id(), span).with_message(kind.to_string()), - ]) - } - Self::InvalidModuleIdentifier { span, .. } => Diagnostic::error() - .with_message("invalid module identifier") - .with_labels(vec![Label::primary(span.source_id(), span).with_message( - "module names must be non-empty, start with 'a-z', and only contain ascii \ - alpha-numeric characters, '_', or '::' as a namespacing operator", - )]), - Self::InvalidFunctionIdentifier { span, .. } => Diagnostic::error() - .with_message("invalid function identifier") - .with_labels(vec![Label::primary(span.source_id(), span).with_message( - "function names must be non-empty, and start with '_' or 'a-z'", - )]), - } - } -} struct DisplayIntErrorKind<'a>(&'a IntErrorKind); impl<'a> fmt::Display for DisplayIntErrorKind<'a> { diff --git a/hir/src/parser/lexer/mod.rs b/hir/src/parser/lexer/mod.rs index c23f3483a..4b2923df5 100644 --- a/hir/src/parser/lexer/mod.rs +++ b/hir/src/parser/lexer/mod.rs @@ -1,20 +1,24 @@ mod error; +mod scanner; mod token; use core::{num::IntErrorKind, ops::Range}; -use miden_diagnostics::{SourceIndex, SourceSpan}; -use miden_parsing::{Scanner, Source}; use num_traits::Num; pub use self::{ error::{InvalidEscapeKind, LexicalError}, + scanner::Scanner, token::Token, }; -use crate::{parser::ParseError, Symbol, Value}; +use crate::{ + diagnostics::{ByteIndex, ByteOffset, SourceId, SourceSpan}, + parser::ParseError, + Symbol, Value, +}; /// The value produced by the [Lexer] when iterated -pub type Lexed = Result<(SourceIndex, Token, SourceIndex), ParseError>; +pub type Lexed = Result<(ByteIndex, Token, ByteIndex), ParseError>; /// Pops a single token from the [Lexer] macro_rules! pop { @@ -53,10 +57,12 @@ macro_rules! pop2 { /// If an error is unrecoverable, the lexer will continue to produce tokens, but there is no /// guarantee that parsing them will produce meaningful results, it is primarily to assist in /// gathering as many errors as possible. -pub struct Lexer { +pub struct Lexer<'a> { + source_id: SourceId, + /// The scanner produces a sequence of chars + location, and can be controlled /// The location type is SourceIndex - scanner: Scanner, + scanner: Scanner<'a>, /// The most recent token to be lexed. /// At the start and end, this should be Token::Eof @@ -64,32 +70,28 @@ pub struct Lexer { /// The position in the input where the current token starts /// At the start this will be the byte index of the beginning of the input - token_start: SourceIndex, + token_start: ByteIndex, /// The position in the input where the current token ends /// At the start this will be the byte index of the beginning of the input - token_end: SourceIndex, + token_end: ByteIndex, /// When we have reached true Eof, this gets set to true, and the only token /// produced after that point is Token::Eof, or None, depending on how you are /// consuming the lexer eof: bool, } -impl Lexer -where - S: Source, -{ +impl<'a> Lexer<'a> { /// Produces an instance of the lexer with the lexical analysis to be performed on the `input` /// string. Note that no lexical analysis occurs until the lexer has been iterated over. - pub fn new(scanner: Scanner) -> Self { - use miden_diagnostics::ByteOffset; - - let start = scanner.start(); + pub fn new(source_id: SourceId, source: &'a str) -> Self { + let scanner = Scanner::new(source); let mut lexer = Lexer { + source_id, scanner, token: Token::Eof, - token_start: start + ByteOffset(0), - token_end: start + ByteOffset(0), + token_start: 0.into(), + token_end: 0.into(), eof: false, }; lexer.advance(); @@ -118,9 +120,9 @@ where #[inline] fn advance_start(&mut self) { - let mut position: SourceIndex; + let mut position: ByteIndex = self.scanner.position(); loop { - let (pos, c) = self.scanner.read(); + let (pos, c) = self.scanner.read().unwrap_or((position, '\0')); position = pos; @@ -130,7 +132,7 @@ where } if c.is_whitespace() { - self.scanner.advance(); + self.scanner.next(); continue; } @@ -142,26 +144,22 @@ where #[inline] fn pop(&mut self) -> char { - use miden_diagnostics::ByteOffset; - - let (pos, c) = self.scanner.pop(); + let (pos, c) = self.scanner.next().unwrap_or((self.token_start, '\0')); self.token_end = pos + ByteOffset::from_char_len(c); c } #[inline] fn peek(&mut self) -> char { - let (_, c) = self.scanner.peek(); - c + self.scanner.peek().map(|(_, c)| c).unwrap_or('\0') } #[inline] fn read(&mut self) -> char { - let (_, c) = self.scanner.read(); - c + self.scanner.read().map(|(_, c)| c).unwrap_or('\0') } - #[inline] + #[inline(always)] fn skip(&mut self) { self.pop(); } @@ -169,7 +167,7 @@ where /// Get the span for the current token in `Source`. #[inline] fn span(&self) -> SourceSpan { - SourceSpan::new(self.token_start, self.token_end) + SourceSpan::new(self.source_id, self.token_start..self.token_end) } #[inline] @@ -239,7 +237,7 @@ where 'a'..='z' => self.lex_keyword_or_special_ident(), '_' => pop!(self, Token::Underscore), c => Token::Error(LexicalError::UnexpectedCharacter { - start: self.span().start(), + start: self.span(), found: c, }), } @@ -349,7 +347,10 @@ where Some(escaped) => buf.push(escaped), None => { break Token::Error(LexicalError::InvalidHexEscape { - span: SourceSpan::new(start, self.token_end), + span: SourceSpan::new( + self.source_id, + start..self.token_end, + ), kind: InvalidEscapeKind::Invalid, }); } @@ -361,7 +362,7 @@ where self.skip(); if self.read() == '}' { break Token::Error(LexicalError::InvalidUnicodeEscape { - span: SourceSpan::new(start, self.token_end), + span: SourceSpan::new(self.source_id, start..self.token_end), kind: InvalidEscapeKind::Empty, }); } @@ -381,8 +382,8 @@ where return Token::Error( LexicalError::InvalidUnicodeEscape { span: SourceSpan::new( - self.token_end - 1, - self.token_end, + self.source_id, + (self.token_end - 1)..self.token_end, ), kind: InvalidEscapeKind::InvalidChars, }, @@ -398,7 +399,10 @@ where Some(escaped) => buf.push(escaped), None => { break Token::Error(LexicalError::InvalidUnicodeEscape { - span: SourceSpan::new(start, self.token_end), + span: SourceSpan::new( + self.source_id, + start..self.token_end, + ), kind: InvalidEscapeKind::Invalid, }); } @@ -406,7 +410,7 @@ where } _ => { break Token::Error(LexicalError::InvalidHexEscape { - span: SourceSpan::new(start, self.token_end), + span: SourceSpan::new(self.source_id, start..self.token_end), kind: InvalidEscapeKind::InvalidChars, }); } @@ -494,10 +498,7 @@ where } } -impl Iterator for Lexer -where - S: Source, -{ +impl<'a> Iterator for Lexer<'a> { type Item = Lexed; fn next(&mut self) -> Option { diff --git a/hir/src/parser/lexer/scanner.rs b/hir/src/parser/lexer/scanner.rs new file mode 100644 index 000000000..05038835f --- /dev/null +++ b/hir/src/parser/lexer/scanner.rs @@ -0,0 +1,85 @@ +use core::{ + iter::{FusedIterator, Peekable}, + ops::Range, +}; + +use crate::diagnostics::{ByteIndex, ByteOffset}; + +/// A simple raw character source for [super::Lexer]; +pub struct Scanner<'a> { + src: &'a str, + buf: Peekable>, + next: Option<(ByteIndex, char)>, + pos: ByteIndex, + eof: bool, +} +impl<'a> Scanner<'a> { + pub fn new(src: &'a str) -> Self { + let eof = src.is_empty(); + let mut buf = src.char_indices().peekable(); + let next = buf.next().map(|(i, c)| (ByteIndex::from(i as u32), c)); + Self { + src, + buf, + next, + pos: next.map(|(i, c)| i + ByteOffset::from_char_len(c)).unwrap_or_default(), + eof, + } + } + + #[inline(always)] + pub const fn read(&self) -> Option<(ByteIndex, char)> { + self.next + } + + pub fn peek(&mut self) -> Option<(ByteIndex, char)> { + self.buf.peek().and_then(|&(i, c)| match u32::try_from(i) { + Ok(i) => Some((ByteIndex::from(i), c)), + Err(_) => None, + }) + } + + #[inline] + fn advance(&mut self) { + match self.buf.next() { + Some((i, c)) if i < u32::MAX as usize => { + let i = ByteIndex::from(i as u32); + self.pos = i + ByteOffset::from_char_len(c); + self.next = Some((i, c)); + } + Some(_) => { + panic!("invalid source file: only files smaller than 2^32 bytes are supported") + } + None => { + self.eof = true; + self.next = None; + } + } + } + + pub fn position(&self) -> ByteIndex { + self.pos + } + + #[inline(always)] + pub fn slice(&self, span: impl Into>) -> &str { + &self.src[span.into()] + } +} + +impl<'a> Iterator for Scanner<'a> { + type Item = (ByteIndex, char); + + #[inline] + fn next(&mut self) -> Option { + if self.eof { + return None; + } + + let current = self.next.take(); + self.advance(); + current + } +} + +impl<'a> FusedIterator for Scanner<'a> {} diff --git a/hir/src/parser/mod.rs b/hir/src/parser/mod.rs index e43080efa..4ae9442c1 100644 --- a/hir/src/parser/mod.rs +++ b/hir/src/parser/mod.rs @@ -1,10 +1,10 @@ /// Simple macro used in the grammar definition for constructing spans macro_rules! span { - ($l:expr, $r:expr) => { - miden_diagnostics::SourceSpan::new($l, $r) + ($source_id:expr, $l:expr, $r:expr) => { + SourceSpan::new($source_id, $l..$r) }; - ($i:expr) => { - miden_diagnostics::SourceSpan::new($i, $i) + ($source_id:expr, $i:expr) => { + SourceSpan::at($source_id, $i) }; } @@ -20,18 +20,17 @@ lalrpop_mod!( "/parser/grammar.rs" ); -use std::{path::Path, sync::Arc}; - -use miden_diagnostics::SourceFile; -use miden_parsing::{FileMapSource, Scanner, Source}; +use alloc::sync::Arc; +use std::path::Path; pub use self::error::ParseError; use self::{ ast::ConvertAstToHir, lexer::{Lexed, Lexer}, }; +use crate::diagnostics::{Report, SourceFile, SourceManagerExt}; -pub type ParseResult = Result; +pub type ParseResult = Result; /// This is the parser for HIR text format pub struct Parser<'a> { @@ -48,7 +47,7 @@ impl<'a> Parser<'a> { where T: Parse, { - ::parse(self, FileMapSource::new(source)) + ::parse(self, source) } /// Parse a `T` from a string @@ -56,8 +55,7 @@ impl<'a> Parser<'a> { where T: Parse, { - let id = self.session.codemap.add("nofile", source.as_ref().to_string()); - let file = self.session.codemap.get(id).unwrap(); + let file = self.session.source_manager.load("nofile", source.as_ref().to_string()); self.parse(file) } @@ -67,12 +65,9 @@ impl<'a> Parser<'a> { T: Parse, { let path = path.as_ref(); - let id = self - .session - .codemap - .add_file(path) - .map_err(|err| parse_file_error(err, path.to_owned()))?; - let file = self.session.codemap.get(id).unwrap(); + let file = self.session.source_manager.load_file(path).map_err(|err| { + Report::msg(err).wrap_err(format!("failed to load '{}' from disk", path.display())) + })?; self.parse(file) } } @@ -80,72 +75,71 @@ impl<'a> Parser<'a> { pub trait Parse: Sized { type Grammar; - fn parse(parser: &Parser, source: impl Source) -> ParseResult { - let scanner = Scanner::new(source); - let lexer = Lexer::new(scanner); + fn parse(parser: &Parser, source: Arc) -> ParseResult { + let lexer = Lexer::new(source.id(), source.as_str()); - Self::parse_tokens(parser, lexer) + Self::parse_tokens(parser, source.clone(), lexer) } - fn parse_tokens(parser: &Parser, tokens: impl IntoIterator) -> ParseResult; + fn parse_tokens( + parser: &Parser, + source: Arc, + tokens: impl IntoIterator, + ) -> ParseResult; } impl Parse for ast::Module { type Grammar = grammar::ModuleParser; - fn parse_tokens(parser: &Parser, tokens: impl IntoIterator) -> ParseResult { + fn parse_tokens( + _parser: &Parser, + source: Arc, + tokens: impl IntoIterator, + ) -> ParseResult { + let source_id = source.id(); let mut next_var = 0; - let result = ::Grammar::new().parse( - &parser.session.diagnostics, - &parser.session.codemap, - &mut next_var, - tokens, - ); + let result = ::Grammar::new().parse(source_id, &mut next_var, tokens); match result { - Ok(ast) => { - if parser.session.diagnostics.has_errors() { - return Err(ParseError::Failed); - } - Ok(ast) + Ok(ast) => Ok(ast), + Err(lalrpop_util::ParseError::User { error }) => { + Err(Report::from(error).with_source_code(source)) + } + Err(err) => { + let error = ParseError::from(err); + Err(Report::from(error).with_source_code(source)) } - Err(lalrpop_util::ParseError::User { error }) => Err(error), - Err(err) => Err(err.into()), } } } impl Parse for crate::Module { type Grammar = grammar::ModuleParser; - fn parse_tokens(parser: &Parser, tokens: impl IntoIterator) -> ParseResult { - use crate::pass::{AnalysisManager, ConversionError, ConversionPass}; + fn parse_tokens( + parser: &Parser, + source: Arc, + tokens: impl IntoIterator, + ) -> ParseResult { + use crate::pass::{AnalysisManager, ConversionPass}; + let source_id = source.id(); let mut next_var = 0; let result = ::Grammar::new() - .parse(&parser.session.diagnostics, &parser.session.codemap, &mut next_var, tokens) + .parse(source_id, &mut next_var, tokens) .map(Box::new); match result { Ok(ast) => { - if parser.session.diagnostics.has_errors() { - return Err(ParseError::Failed); - } let mut analyses = AnalysisManager::new(); let mut convert_to_hir = ConvertAstToHir; - convert_to_hir.convert(ast, &mut analyses, parser.session).map_err( - |err| match err { - ConversionError::Failed(err) => match err.downcast::() { - Ok(err) => err, - Err(_) => ParseError::InvalidModule, - }, - _ => ParseError::InvalidModule, - }, - ) + convert_to_hir + .convert(ast, &mut analyses, parser.session) + .map_err(|err| err.with_source_code(source)) + } + Err(lalrpop_util::ParseError::User { error }) => { + Err(Report::from(error).with_source_code(source)) + } + Err(err) => { + let error = ParseError::from(err); + Err(Report::from(error).with_source_code(source)) } - Err(lalrpop_util::ParseError::User { error }) => Err(error), - Err(err) => Err(err.into()), } } } - -#[inline] -fn parse_file_error(source: std::io::Error, path: std::path::PathBuf) -> ParseError { - ParseError::FileError { source, path } -} diff --git a/hir/src/parser/tests/mod.rs b/hir/src/parser/tests/mod.rs index 7ad8c9b55..c99ed3af5 100644 --- a/hir/src/parser/tests/mod.rs +++ b/hir/src/parser/tests/mod.rs @@ -1,14 +1,15 @@ -use miden_diagnostics::{SourceSpan, Span}; use pretty_assertions::assert_eq; use crate::{ - parser::ast::*, AbiParam, ArgumentExtension, ArgumentPurpose, CallConv, ExternalFunction, - FunctionIdent, Ident, Linkage, Opcode, Overflow, Signature, StructType, Type, + diagnostics::{SourceSpan, Span}, + parser::ast::*, + AbiParam, ArgumentExtension, ArgumentPurpose, CallConv, ExternalFunction, FunctionIdent, Ident, + Linkage, Opcode, Overflow, Signature, StructType, Type, }; macro_rules! ident { ($name:ident) => { - Ident::new(crate::Symbol::intern(stringify!($name)), miden_diagnostics::SourceSpan::UNKNOWN) + Ident::new(crate::Symbol::intern(stringify!($name)), SourceSpan::UNKNOWN) }; } diff --git a/hir/src/parser/tests/utils.rs b/hir/src/parser/tests/utils.rs index 54cb988a3..8d4bc88f0 100644 --- a/hir/src/parser/tests/utils.rs +++ b/hir/src/parser/tests/utils.rs @@ -1,26 +1,26 @@ use std::{path::Path, sync::Arc}; -use miden_diagnostics::{Emitter, Verbosity}; -use midenc_session::{Options, Warnings}; +use midenc_session::{Options, Verbosity, Warnings}; use pretty_assertions::assert_eq; use crate::{ - parser::{ast::Module, ParseError, Parser}, + diagnostics::{self, CaptureEmitter, DefaultEmitter, Emitter, EmitterBuffer, Report}, + parser::{ast::Module, Parser}, testing::TestContext, }; struct SplitEmitter { - capture: miden_diagnostics::CaptureEmitter, - default: miden_diagnostics::DefaultEmitter, + capture: CaptureEmitter, + default: DefaultEmitter, } impl SplitEmitter { #[inline] pub fn new() -> Self { - use miden_diagnostics::term::termcolor::ColorChoice; + use diagnostics::ColorChoice; Self { capture: Default::default(), - default: miden_diagnostics::DefaultEmitter::new(ColorChoice::Auto), + default: DefaultEmitter::new(ColorChoice::Auto), } } @@ -31,12 +31,12 @@ impl SplitEmitter { } impl Emitter for SplitEmitter { #[inline] - fn buffer(&self) -> miden_diagnostics::term::termcolor::Buffer { + fn buffer(&self) -> EmitterBuffer { self.capture.buffer() } #[inline] - fn print(&self, buffer: miden_diagnostics::term::termcolor::Buffer) -> std::io::Result<()> { + fn print(&self, buffer: EmitterBuffer) -> std::io::Result<()> { use std::io::Write; let mut copy = self.capture.buffer(); @@ -61,10 +61,17 @@ pub struct ParseTest { impl ParseTest { /// Creates a new test, from the source string. pub fn new() -> Self { + use midenc_session::{ProjectType, TargetEnv}; + let emitter = Arc::new(SplitEmitter::new()); - let options = Options::new(std::env::current_dir().unwrap()) - .with_verbosity(Verbosity::Warning) - .with_warnings(Warnings::Error); + let options = Options::new( + TargetEnv::Base, + ProjectType::Library, + std::env::current_dir().unwrap(), + None, + ) + .with_verbosity(Verbosity::Warning) + .with_warnings(Warnings::Error); let context = TestContext::default_with_opts_and_emitter(options, Some(emitter.clone())); Self { context, emitter } } @@ -75,31 +82,28 @@ impl ParseTest { /// disk #[allow(unused)] pub fn add_virtual_file>(&self, name: P, content: String) { - self.context.session.codemap.add(name.as_ref(), content); + use diagnostics::SourceManager; + + let name = name.as_ref().to_str().unwrap(); + self.context.session.source_manager.load(name, content); } - pub fn parse_module_ast_from_file>( - &self, - path: P, - ) -> Result { + pub fn parse_module_ast_from_file>(&self, path: P) -> Result { let parser = Parser::new(&self.context.session); parser.parse_file::(path) } - pub fn parse_module_ast(&self, source: &str) -> Result { + pub fn parse_module_ast(&self, source: &str) -> Result { let parser = Parser::new(&self.context.session); parser.parse_str::(source) } - pub fn parse_module_from_file>( - &self, - path: P, - ) -> Result { + pub fn parse_module_from_file>(&self, path: P) -> Result { let parser = Parser::new(&self.context.session); parser.parse_file::(path) } - pub fn parse_module(&self, source: &str) -> Result { + pub fn parse_module(&self, source: &str) -> Result { let parser = Parser::new(&self.context.session); parser.parse_str::(source) } diff --git a/hir/src/pass/analysis.rs b/hir/src/pass/analysis.rs index 3523728c6..ab5a062b2 100644 --- a/hir/src/pass/analysis.rs +++ b/hir/src/pass/analysis.rs @@ -1,24 +1,18 @@ -use std::{ +use alloc::rc::Rc; +use core::{ any::{Any, TypeId}, hash::Hash, - rc::Rc, }; use midenc_session::Session; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; -type BuildFxHasher = std::hash::BuildHasherDefault; +use crate::diagnostics::Report; -/// This error type is produced when an [Analysis] fails -#[derive(Debug, thiserror::Error)] -pub enum AnalysisError { - /// The analysis failed for an unexpected reason - #[error(transparent)] - Failed(#[from] anyhow::Error), -} +type BuildFxHasher = std::hash::BuildHasherDefault; /// A convenient type alias for `Result` -pub type AnalysisResult = Result; +pub type AnalysisResult = Result; #[doc(hidden)] pub trait PreservableAnalysis: Any { diff --git a/hir/src/pass/conversion.rs b/hir/src/pass/conversion.rs index 44f45e1bf..b3f43ec9d 100644 --- a/hir/src/pass/conversion.rs +++ b/hir/src/pass/conversion.rs @@ -1,20 +1,10 @@ use midenc_session::Session; -use super::{AnalysisError, AnalysisManager, Chain, PassInfo}; - -/// This error is produced when a [ConversionPass] fails -#[derive(Debug, thiserror::Error)] -pub enum ConversionError { - /// Conversion failed due to an analysis error - #[error(transparent)] - Analysis(#[from] AnalysisError), - /// An unexpected error occurred during conversion - #[error(transparent)] - Failed(#[from] anyhow::Error), -} +use super::{AnalysisManager, Chain, PassInfo}; +use crate::diagnostics::Report; /// A convenient type alias for `Result` -pub type ConversionResult = Result; +pub type ConversionResult = Result; /// This is a marker trait for [ConversionPass] impls which also implement [PassInfo] /// diff --git a/hir/src/pass/mod.rs b/hir/src/pass/mod.rs index 3350a211b..f91d6d02a 100644 --- a/hir/src/pass/mod.rs +++ b/hir/src/pass/mod.rs @@ -12,7 +12,7 @@ macro_rules! register_function_rewrite { impl $ty { fn new( _options: std::sync::Arc, - _diagnostics: std::sync::Arc, + _diagnostics: std::sync::Arc, ) -> Box { Box::new(crate::ModuleRewritePassAdapter(Self)) } diff --git a/hir/src/pass/rewrite.rs b/hir/src/pass/rewrite.rs index ecb5e899d..ffdcae5eb 100644 --- a/hir/src/pass/rewrite.rs +++ b/hir/src/pass/rewrite.rs @@ -1,20 +1,10 @@ use midenc_session::Session; -use super::{AnalysisError, AnalysisKey, AnalysisManager, PassInfo}; - -/// This error is produced when an error occurs when applying a rewrite rule -#[derive(Debug, thiserror::Error)] -pub enum RewriteError { - /// The rewrite failed due to an analysis error - #[error(transparent)] - Analysis(#[from] AnalysisError), - /// An unexpected error occurred during this rewrite - #[error(transparent)] - Failed(#[from] anyhow::Error), -} +use super::{AnalysisKey, AnalysisManager, PassInfo}; +use crate::diagnostics::Report; -/// A convenient type alias for `Result<(), RewriteError>` -pub type RewriteResult = Result<(), RewriteError>; +/// A convenient type alias for `Result<(), Report>` +pub type RewriteResult = Result<(), Report>; /// A convenient type alias for closures which can be used as rewrite passes pub type RewriteFn = dyn FnMut(&mut T, &mut AnalysisManager, &Session) -> RewriteResult; diff --git a/hir/src/program/linker.rs b/hir/src/program/linker.rs index 5b37fb61a..9400fa854 100644 --- a/hir/src/program/linker.rs +++ b/hir/src/program/linker.rs @@ -1,90 +1,15 @@ +use std::{ + borrow::Cow, + collections::{BTreeMap, BTreeSet}, +}; + +use miden_assembly::library::CompiledLibrary; use petgraph::{prelude::DiGraphMap, Direction}; -use rustc_hash::FxHashMap; - -use crate::*; - -/// This represents the various types of errors which may be raised by a [Linker] -#[derive(Debug, thiserror::Error)] -pub enum LinkerError { - /// The given module has already been declared - #[error("duplicate module declaration for '{0}'")] - ModuleConflict(Ident), - /// The given identifier references a [Module] which is not present in the set of - /// modules to link. - #[error("encountered reference to undefined module '{0}'")] - MissingModule(Ident), - /// The given identifier references a [Function] which is not defined in any of the - /// modules being linked, and is not a standard library function whose definition is - /// expected to be provided by the Miden VM. - #[error("encountered reference to undefined function '{0}'")] - MissingFunction(FunctionIdent), - /// The given identifier references [GlobalVariableData] which has not been defined - /// in any of the modules being linked. - #[error("encountered reference to undefined global '{0}'")] - MissingGlobal(Ident), - /// The given function is referenced by an external declaration, but the actual definition - /// of that function has a different signature than was expected by the external reference. - /// - /// The types of mismatches that will cause this error are: - /// - /// * The calling convention is different - /// * The number and/or types of the arguments and results are not the same - /// * A special purpose parameter is declared in one signature but not the other - /// * Argument extension conflicts, i.e. one signature says a parameter is zero-extended, the - /// other sign-extended - #[error( - "signature mismatch for '{0}': external function declaration does not match definition" - )] - SignatureMismatch(FunctionIdent), - /// An external declaration for the given function was found in a different module than the - /// one in which the function is defined, and the actual definition does not have external - /// linkage. - /// - /// This error is a variant of `SignatureMismatch`, but occurs when the signature is otherwise - /// correct, but is ultimately an invalid declaration because the function should not be - /// visible outside its containing module. - #[error( - "invalid reference to '{0}': only functions with external linkage can be referenced from \ - other modules" - )] - LinkageMismatch(FunctionIdent), - /// A cycle in the call graph was found starting at the given function. - /// - /// This occurs due to recursion (self or mutual), and is not supported by Miden. - #[error( - "encountered an invalid cycle in the call graph caused by a call from '{caller}' to \ - '{callee}'" - )] - InvalidCycle { - caller: FunctionIdent, - callee: FunctionIdent, - }, - /// Occurs when the declared entrypoint does not have external linkage - #[error("invalid entrypoint '{0}': must have external linkage")] - InvalidEntryLinkage(FunctionIdent), - /// Occurs when attempting to set the program entrypoint when it has already been set - #[error( - "conflicting entrypoints: '{current}' conflicts with previously declared entrypoint \ - '{prev}'" - )] - InvalidMultipleEntry { - current: FunctionIdent, - prev: FunctionIdent, - }, - /// An error occurred when attempting to link segments declared by a module into the - /// set of segments already declared in the program. A segment might be valid in the - /// context of a single module, but invalid in the context of a whole program, either - /// due to conflicts, or an inability to allocate all segments without running out of - /// available heap memory. - #[error(transparent)] - SegmentError(#[from] DataSegmentError), - /// A conflict between two global variables with the same symbol was detected. - /// - /// When this occurs, the definitions must have been in separate modules, with external - /// linkage, and they disagree on the type of the value or its initializer. - #[error(transparent)] - GlobalVariableError(#[from] GlobalVariableError), -} + +use crate::{ + diagnostics::{DiagnosticsHandler, Report, Severity, Spanned}, + *, +}; /// Represents a node in the global variable dependency graph #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -95,6 +20,50 @@ enum Node { Function(FunctionIdent), } +/// Represents an object input to the [Linker] +pub enum Object { + /// The object is an HIR module + Hir(Box), + /// The object is a compiled Miden Assembly module + Masm { name: Ident, exports: Vec }, +} +impl Object { + /// Return the identifier associated with this object + pub fn id(&self) -> Ident { + match self { + Self::Hir(module) => module.name, + Self::Masm { name, .. } => *name, + } + } + + /// Return the set of exported functions/procedures from this object + pub fn exports(&self) -> Box<(dyn Iterator + '_)> { + match self { + Self::Hir(module) => Box::new(module.functions().map(|f| f.id)), + Self::Masm { name, ref exports } => { + let name = *name; + Box::new(exports.iter().copied().map(move |function| FunctionIdent { + module: name, + function, + })) + } + } + } +} +impl From> for Object { + fn from(module: Box) -> Self { + Self::Hir(module) + } +} +impl From<(Ident, Vec)> for Object { + fn from(module: (Ident, Vec)) -> Self { + Self::Masm { + name: module.0, + exports: module.1, + } + } +} + /// The [Linker] performs a similar role in conjunction with the Miden compiler, as the system /// linker does (e.g. `ld`) when used with compilers like `clang` or `rustc`. /// @@ -149,11 +118,19 @@ enum Node { /// context, and we do not provide instructions for executing calls in another context. However, we /// will eventually be linking programs which have a potentially unbounded number of address spaces, /// which is an additional complication that your typical linker doesn't have to deal with -pub struct Linker { +pub struct Linker<'a> { + diagnostics: &'a DiagnosticsHandler, /// This is the program being constructed by the linker program: Box, - /// This is the set of modules which have yet to be linked - pending: FxHashMap>, + /// This is the set of named objects which have yet to be linked + pending: BTreeMap, + /// This is the set of patterns that symbol names will be matched against when determining + /// whether or not to raise an error when a reference to any symbol whose name starts with + /// that pattern cannot be found. + /// + /// In practice, this is used to allow certain library modules to be referenced without + /// requiring them to be loaded into the linker. + allow_missing: BTreeSet>, /// This is the dependency graph for all functions in the program. /// /// This graph is used to obtain a topological ordering of the @@ -179,10 +156,11 @@ pub struct Linker { /// The set of renamed global symbols for a single module. /// /// This is only used when preprocessing a module, and is reset on each call to `add` - renamed: FxHashMap, + renamed: BTreeMap, } -impl Default for Linker { - fn default() -> Self { +impl<'a> Linker<'a> { + /// Create a [Linker] for a new, empty [Program]. + pub fn new(diagnostics: &'a DiagnosticsHandler) -> Self { let mut program = Box::new(Program::new()); // We reserve the first page of memory for the shadow stack @@ -192,28 +170,33 @@ impl Default for Linker { .expect("unexpected error declaring shadow stack segment"); Self { + diagnostics, program, pending: Default::default(), + allow_missing: BTreeSet::from_iter(["std::".into(), "intrinsics::".into()]), callgraph: DiGraphMap::new(), local_callgraph: DiGraphMap::new(), globals: DiGraphMap::new(), renamed: Default::default(), } } -} -impl Linker { - /// Create a [Linker] for a new, empty [Program]. - pub fn new() -> Self { - Self::default() - } /// Set the entrypoint for the linked program /// - /// Returns a [LinkerError] if a different entrypoint was already declared. - pub fn with_entrypoint(&mut self, id: FunctionIdent) -> Result<(), LinkerError> { + /// Returns a [Report] if a different entrypoint was already declared. + pub fn with_entrypoint(&mut self, id: FunctionIdent) -> Result<(), Report> { if let Some(prev) = self.program.entrypoint() { if prev != id { - return Err(LinkerError::InvalidMultipleEntry { current: id, prev }); + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.function.span, + "this entrypoint conflicts with a previously declared entrypoint", + ) + .with_secondary_label(prev.function.span, "previous entrypoint declared here") + .into_report()); } } @@ -222,7 +205,82 @@ impl Linker { Ok(()) } - /// Add `module` to the set of modules to be linked + /// Specify a pattern that will be matched against undefined symbols that determines whether or + /// or not it should be treated as an error. It is assumed that the referenced symbol will be + /// resolved during assembly to MAST. + pub fn allow_missing(&mut self, name: impl Into>) { + self.allow_missing.insert(name.into()); + } + + /// Add a compiled library to the set of libraries to link against + pub fn add_library(&mut self, lib: CompiledLibrary) { + // Add all of the exported objects to the callgraph + for export in lib.exports() { + let module = Ident::with_empty_span(Symbol::intern(export.module.path())); + let name: &str = export.name.as_ref(); + let function = Ident::with_empty_span(Symbol::intern(name)); + self.callgraph.add_node(FunctionIdent { module, function }); + } + self.program.add_library(lib); + } + + /// Add multiple libraries to the set of libraries to link against + pub fn add_libraries(&mut self, libs: I) + where + I: IntoIterator, + { + for lib in libs { + self.add_library(lib); + } + } + + /// Add an object to link as part of the resulting [Program]. + /// + /// There are different types of objects, see [Object] for details. + /// + /// # Errors + /// + /// The following conditions can cause an error to be raised, if applicable to the object given: + /// + /// * The object is invalid + /// * The object introduces recursion into the call graph + /// * Two or more objects export a module with the same name + /// * Two or more objects contain conflicting data segment declarations + /// * Two or more objects contain conflicting global variable declarations + pub fn add_object(&mut self, object: impl Into) -> Result<(), Report> { + let object = object.into(); + let id = object.id(); + + // Raise an error if we've already got a module by this name pending + if self.pending.contains_key(&id) { + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.span, + "this module conflicts with a previous module of the same name", + ) + .into_report()); + } + + // Register functions in the callgraph + for export in object.exports() { + self.callgraph.add_node(export); + } + + match object { + Object::Hir(module) => self.add_hir_object(module), + object @ Object::Masm { .. } => { + // We're done preprocessing, so add the module to the pending set + self.pending.insert(object.id(), object); + + Ok(()) + } + } + } + + /// Add `module` to the set of objects to be linked /// /// This preprocesses the module for the linker, and will catch the following issues: /// @@ -231,8 +289,8 @@ impl Linker { /// * Conflicting global variable declarations /// * Recursion in the local call graph of the module (global analysis comes later) /// - /// If any of the above errors occurs, a [LinkerError] is returned. - pub fn add(&mut self, mut module: Box) -> Result<(), LinkerError> { + /// If any of the above errors occurs, a [Report] is returned. + fn add_hir_object(&mut self, mut module: Box) -> Result<(), Report> { let id = module.name; // Reset the auxiliary data structures used for preprocessing @@ -241,7 +299,15 @@ impl Linker { // Raise an error if we've already got a module by this name pending if self.pending.contains_key(&id) { - return Err(LinkerError::ModuleConflict(id)); + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.span, + "this module conflicts with a previous module of the same name", + ) + .into_report()); } // Import all data segments @@ -290,7 +356,7 @@ impl Linker { // the callee for our error diagnostics. To get it, we call the // call graph validation routine which does a traversal specifically // designed to obtain that information. - validate_callgraph(&self.local_callgraph) + validate_callgraph(&self.local_callgraph, &self.diagnostics) .expect_err("expected call graph to contain a cycle") })?; @@ -339,14 +405,14 @@ impl Linker { } // We're done preprocessing, so add the module to the pending set - self.pending.insert(id, module); + self.pending.insert(id, Object::Hir(module)); Ok(()) } /// Links all of the modules which were added, producing a [Program] if no issues are found. /// - /// Returns a [LinkerError] if the link fails for any reason. + /// Returns a [Report] if the link fails for any reason. /// /// When called, all of the added modules have been preprocessed, and what remains are the /// following tasks: @@ -361,75 +427,154 @@ impl Linker { /// * TODO: If linking an executable program, garbage collect unused modules/functions /// /// Once linked, a [Program] can be emitted to Miden Assembly using the code generation passes. - pub fn link(mut self) -> Result, LinkerError> { + pub fn link(mut self) -> Result, Report> { // Ensure linker-defined globals and intrinsics are present self.populate_builtins(); // Look for cycles in the call graph - validate_callgraph(&self.callgraph)?; + validate_callgraph(&self.callgraph, &self.diagnostics)?; // Verify the entrypoint, if declared if let Some(entry) = self.program.entrypoint() { - let is_linked = self.pending.contains_key(&entry.module); - if !is_linked { - return Err(LinkerError::MissingModule(entry.module)); - } - - let module = &self.pending[&entry.module]; - let function = - module.function(entry.function).ok_or(LinkerError::MissingFunction(entry))?; - if !function.is_public() { - return Err(LinkerError::InvalidEntryLinkage(entry)); + // NOTE(pauls): Currently, we always raise an error here, but since we do allow + // missing symbols in other situations, perhaps we should allow it here as well. + // For now though, we assume this is a mistake, since presumably you are compiling + // the code that contains the entrypoint. + let object = self.pending.get(&entry.module).ok_or_else(|| { + self.diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined module '{}'", &entry.module)) + .into_report() + })?; + match object { + Object::Hir(module) => { + let function = module.function(entry.function).ok_or_else(|| { + self.diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined function '{}'", &entry)) + .into_report() + })?; + if !function.is_public() { + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + entry.function.span, + "entrypoint must have external linkage", + ) + .into_report()); + } + } + Object::Masm { ref exports, .. } => { + if !exports.contains(&entry.function) { + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined function '{}'", &entry)) + .into_report()); + } + } } } // Verify module/function references for node in self.callgraph.nodes() { // If the module is pending, it is being linked - let is_linked = self.pending.contains_key(&node.module); - let is_stdlib = node.module.as_str().starts_with("std::"); - let is_intrinsic = node.module.as_str().starts_with("intrinsics::"); - - // If a referenced module is not being linked, raise an error - if !is_linked { - // However we ignore standard library/intrinsic modules in this check, - // as they are known to be provided at runtime. - // - // TODO: We need to validate that the given module/function - // is actually in the standard library though, and that the - // signature matches what is expected. - if is_stdlib || is_intrinsic { + let object = self.pending.get(&node.module); + let is_allowed_missing = self + .allow_missing + .iter() + .any(|pattern| node.module.as_str().starts_with(pattern.as_ref())); + + // If a referenced module is not present for the link, raise an error, unless it is + // specifically allowed to be missing at this point. + if object.is_none() { + if is_allowed_missing { continue; } - return Err(LinkerError::MissingModule(node.module)); + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined module '{}'", &node.module)) + .into_report()); } // The module is present, so we must verify that the function is defined in that module - let module = &self.pending[&node.module]; - let function = - module.function(node.function).ok_or(LinkerError::MissingFunction(node))?; - let is_externally_linkable = function.is_public(); + let object = unsafe { object.unwrap_unchecked() }; + let (is_externally_linkable, signature) = match object { + Object::Hir(ref module) => match module.function(node.function) { + Some(function) => (function.is_public(), Some(&function.signature)), + None if is_allowed_missing => (true, None), + None => { + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined function '{}'", &node)) + .into_report()) + } + }, + Object::Masm { ref exports, .. } => { + if !exports.contains(&node.function) && !is_allowed_missing { + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined function '{}'", &node)) + .into_report()); + } + (true, None) + } + }; // Next, visit all of the dependent functions, and ensure their signatures match for dependent_id in self.callgraph.neighbors_directed(node, Direction::Incoming) { // If the dependent is in another module, but the function has internal linkage, // raise an error if dependent_id.module != node.module && !is_externally_linkable { - return Err(LinkerError::LinkageMismatch(node)); + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + dependent_id.function.span, + format!( + "this function contains an invalid reference to '{}'", + &node.function + ), + ) + .with_help( + "Only functions with external linkage can be referenced across modules", + ) + .into_report()); + } + // Otherwise, make sure the signatures match (if we have signatures available) + let dependent_object = &self.pending[&dependent_id.module]; + match (signature, dependent_object) { + (Some(signature), Object::Hir(ref dependent_module)) => { + let dependent_function = dependent_module + .function(dependent_id.function) + .expect("dependency graph is outdated"); + let external_ref = dependent_function + .dfg + .get_import(&node) + .expect("dependency graph is outdated"); + let external_span = external_ref.id.span(); + verify_matching_signature( + node, + external_span, + signature, + &external_ref.signature, + &self.diagnostics, + )?; + } + // If we don't have a signature for the dependency, we presume it matches the + // dependent + (None, Object::Hir(_)) => (), + // If the dependent is MASM, we don't know what signature it used, so we + // presume it is correct + (_, Object::Masm { .. }) => (), } - // Otherwise, make sure the signatures match - let dependent_module = &self.pending[&dependent_id.module]; - let dependent_function = dependent_module - .function(dependent_id.function) - .expect("dependency graph is outdated"); - let external_ref = - dependent_function.dfg.get_import(&node).expect("dependency graph is outdated"); - verify_matching_signature( - function.id, - &function.signature, - &external_ref.signature, - )?; } } @@ -454,7 +599,11 @@ impl Linker { // If it has dependents, but isn't defined anywhere, raise an error if !self.program.globals.exists(name) { - return Err(LinkerError::MissingGlobal(name)); + return Err(self + .diagnostics + .diagnostic(Severity::Error) + .with_message(format!("linker error: undefined global variable '{name}'")) + .into_report()); } } @@ -462,8 +611,16 @@ impl Linker { self.garbage_collect(); // We're finished processing all pending modules, so add them to the program - for module in self.pending.into_values() { - self.program.modules.insert(module); + for object in self.pending.into_values() { + match object { + Object::Hir(module) => { + self.program.modules.insert(module); + } + Object::Masm { .. } => { + // These objects are provided to the assembler directly + continue; + } + } } Ok(self.program) @@ -532,18 +689,37 @@ impl Linker { /// caller. fn verify_matching_signature( id: FunctionIdent, + expected_span: SourceSpan, actual: &Signature, expected: &Signature, -) -> Result<(), LinkerError> { + diagnostics: &DiagnosticsHandler, +) -> Result<(), Report> { // If the number of parameters differs, raise an error if expected.arity() != actual.arity() { - return Err(LinkerError::SignatureMismatch(id)); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label(id.span(), "the arity of this function declaration is incorrect") + .with_secondary_label( + expected_span, + format!("the actual arity of the definition is {}", expected.arity()), + ) + .into_report()); } // If the type or specification of any parameters differs, raise an error - for (ep, ap) in expected.params().iter().zip(actual.params().iter()) { + for (i, (ep, ap)) in expected.params().iter().zip(actual.params().iter()).enumerate() { if !is_matching_param(ep, ap) { - return Err(LinkerError::SignatureMismatch(id)); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.span(), + "the type signature of this function declaration is incorrect", + ) + .with_secondary_label(expected_span, "it does not match the signature defined here") + .with_help(format!("The parameter at index {i} is defined as {}", &ep.ty)) + .into_report()); } } @@ -551,13 +727,33 @@ fn verify_matching_signature( let expected_results = expected.results(); let actual_results = actual.results(); if expected_results.len() != actual_results.len() { - return Err(LinkerError::SignatureMismatch(id)); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.span(), + "the return arity of this function declaration is incorrect", + ) + .with_secondary_label( + expected_span, + format!("the actual number of return values is {}", expected_results.len()), + ) + .into_report()); } // If the type of results differs, raise an error - for (er, ar) in expected_results.iter().zip(actual_results.iter()) { + for (i, (er, ar)) in expected_results.iter().zip(actual_results.iter()).enumerate() { if !is_matching_param(er, ar) { - return Err(LinkerError::SignatureMismatch(id)); + return Err(diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label( + id.span(), + "the type signature of this function declaration is incorrect", + ) + .with_secondary_label(expected_span, "it does not match the signature defined here") + .with_help(format!("The result at index {i} is defined as {}", &er.ty)) + .into_report()); } } @@ -583,12 +779,24 @@ fn is_matching_param(expected: &AbiParam, actual: &AbiParam) -> bool { /// Validate the given call graph by looking for cycles caused by recursion. /// -/// Returns a [LinkerError] if a cycle is found. -fn validate_callgraph(callgraph: &DiGraphMap) -> Result<(), LinkerError> { +/// Returns a [Report] if a cycle is found. +fn validate_callgraph( + callgraph: &DiGraphMap, + diagnostics: &DiagnosticsHandler, +) -> Result<(), Report> { use petgraph::visit::{depth_first_search, DfsEvent, IntoNodeIdentifiers}; depth_first_search(callgraph, callgraph.node_identifiers(), |event| match event { - DfsEvent::BackEdge(caller, callee) => Err(LinkerError::InvalidCycle { caller, callee }), + DfsEvent::BackEdge(caller, callee) => Err(diagnostics + .diagnostic(Severity::Error) + .with_message("linker error") + .with_primary_label(caller.span(), "this function contains recursion") + .with_secondary_label(callee.span(), "due to one or more calls to this function") + .with_help( + "If you need to make the call recursive, you may need to use indirect calls to \ + acheive this", + ) + .into_report()), _ => Ok(()), }) } diff --git a/hir/src/program/mod.rs b/hir/src/program/mod.rs index 1dd2c0b69..d0874dd75 100644 --- a/hir/src/program/mod.rs +++ b/hir/src/program/mod.rs @@ -1,11 +1,17 @@ mod linker; +use alloc::collections::BTreeMap; use core::ops::{Deref, DerefMut}; use intrusive_collections::RBTree; +use miden_assembly::library::CompiledLibrary; +use miden_core::crypto::hash::RpoDigest; -pub use self::linker::{Linker, LinkerError}; -use super::*; +pub use self::linker::Linker; +use crate::{ + diagnostics::{DiagnosticsHandler, Report}, + *, +}; /// A [Program] is a collection of [Module]s that are being compiled together as a package. /// @@ -27,6 +33,8 @@ use super::*; pub struct Program { /// This tree stores all of the modules being compiled as part of the current program. modules: RBTree, + /// The set of compiled libraries this program links against + libraries: BTreeMap, /// If set, this field is used to determine which function is the entrypoint for the program. /// /// When generating Miden Assembly, this will determine whether or not we're emitting @@ -52,9 +60,14 @@ impl Program { Self::default() } + /// Add to the set of libraries this [Program] will be assembled with + pub fn add_library(&mut self, lib: CompiledLibrary) { + self.libraries.insert(*lib.digest(), lib); + } + /// Returns true if this program has a defined entrypoint pub fn has_entrypoint(&self) -> bool { - self.entrypoint().is_none() + self.entrypoint().is_some() } /// Returns true if this program is executable. @@ -80,6 +93,16 @@ impl Program { &mut self.modules } + /// Return the set of libraries this program links against + pub fn libraries(&self) -> &BTreeMap { + &self.libraries + } + + /// Return the set of libraries this program links against as a mutable reference + pub fn libraries_mut(&mut self) -> &mut BTreeMap { + &mut self.libraries + } + /// Return a reference to the data segment table for this program pub fn segments(&self) -> &DataSegmentTable { &self.segments @@ -123,14 +146,21 @@ impl crate::pass::AnalysisKey for Program { /// Simply create the builder, add/build one or more modules, then call `link` to obtain a /// [Program]. pub struct ProgramBuilder<'a> { - modules: std::collections::BTreeMap>, + /// The set of HIR modules to link into the program + modules: BTreeMap>, + /// The set of modules defined externally, which will be linked during assembly + extern_modules: BTreeMap>, + /// The set of libraries we're linking against + libraries: BTreeMap, entry: Option, - diagnostics: &'a miden_diagnostics::DiagnosticsHandler, + diagnostics: &'a DiagnosticsHandler, } impl<'a> ProgramBuilder<'a> { - pub fn new(diagnostics: &'a miden_diagnostics::DiagnosticsHandler) -> Self { + pub fn new(diagnostics: &'a DiagnosticsHandler) -> Self { Self { modules: Default::default(), + extern_modules: Default::default(), + libraries: Default::default(), entry: None, diagnostics, } @@ -158,8 +188,9 @@ impl<'a> ProgramBuilder<'a> { /// Returns `Err` if a module with the same name already exists pub fn add_module(&mut self, module: Box) -> Result<(), ModuleConflictError> { let module_name = module.name; - if self.modules.contains_key(&module_name) { - return Err(ModuleConflictError(module_name)); + if self.modules.contains_key(&module_name) || self.extern_modules.contains_key(&module_name) + { + return Err(ModuleConflictError::new(module_name)); } self.modules.insert(module_name, module); @@ -167,6 +198,34 @@ impl<'a> ProgramBuilder<'a> { Ok(()) } + /// Make the linker aware that `module` (with the given set of exports), is available to be + /// linked against, but is already compiled to Miden Assembly, so has no HIR representation. + /// + /// Returns `Err` if a module with the same name already exists + pub fn add_extern_module( + &mut self, + module: Ident, + exports: E, + ) -> Result<(), ModuleConflictError> + where + E: IntoIterator, + { + if self.modules.contains_key(&module) || self.extern_modules.contains_key(&module) { + return Err(ModuleConflictError::new(module)); + } + + self.extern_modules.insert(module, exports.into_iter().collect()); + + Ok(()) + } + + /// Make the linker aware of the objects contained in the given library. + /// + /// Duplicate libraries/objects are ignored. + pub fn add_library(&mut self, library: CompiledLibrary) { + self.libraries.insert(*library.digest(), library); + } + /// Start building a [Module] with the given name. /// /// When the builder is done, the resulting [Module] will be inserted @@ -184,16 +243,18 @@ impl<'a> ProgramBuilder<'a> { } /// Link a [Program] from the current [ProgramBuilder] state - pub fn link(self) -> Result, LinkerError> { - let mut linker = Linker::new(); + pub fn link(self) -> Result, Report> { + let mut linker = Linker::new(self.diagnostics); + let entrypoint = self.entry.or_else(|| self.modules.values().find_map(|m| m.entrypoint())); if let Some(entry) = entrypoint { linker.with_entrypoint(entry)?; } - for (_, module) in self.modules.into_iter() { - linker.add(module)?; - } + linker.add_libraries(self.libraries.into_values()); + + self.extern_modules.into_iter().try_for_each(|obj| linker.add_object(obj))?; + self.modules.into_values().try_for_each(|obj| linker.add_object(obj))?; linker.link() } @@ -260,15 +321,15 @@ impl<'a, 'b: 'a> AsMut for ProgramModuleBuilder<'a, 'b> { /// This is used to build a [Function] from a [ProgramModuleBuilder]. /// /// It is basically just a wrapper around [ModuleFunctionBuilder], but overrides -/// `build` to use the [miden_diagnostics::DiagnosticsHandler] of the parent +/// `build` to use the [DiagnosticsHandler] of the parent /// [ProgramBuilder]. pub struct ProgramFunctionBuilder<'a, 'b: 'a> { - diagnostics: &'b miden_diagnostics::DiagnosticsHandler, + diagnostics: &'b DiagnosticsHandler, fb: ModuleFunctionBuilder<'a>, } impl<'a, 'b: 'a> ProgramFunctionBuilder<'a, 'b> { /// Build the current function - pub fn build(self) -> Result { + pub fn build(self) -> Result { let diagnostics = self.diagnostics; self.fb.build(diagnostics) } diff --git a/hir/src/segments.rs b/hir/src/segments.rs index dee52e846..7c709a34a 100644 --- a/hir/src/segments.rs +++ b/hir/src/segments.rs @@ -7,17 +7,21 @@ use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink, Unsaf intrusive_adapter!(pub DataSegmentAdapter = UnsafeRef: DataSegment { link: LinkedListLink }); -use crate::{formatter, Alignable, ConstantData, Offset}; +use crate::{ + diagnostics::{miette, Diagnostic}, + formatter, Alignable, ConstantData, Offset, +}; /// This error is raised when attempting to declare a [DataSegment] /// that in some way conflicts with previously declared data segments. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, Diagnostic)] pub enum DataSegmentError { /// The current segment overlaps with a previously allocated segment #[error( "invalid data segment: segment of {size1} bytes at {offset1:#x} overlaps with segment of \ {size2} bytes at {offset2:#x}" )] + #[diagnostic()] OverlappingSegments { offset1: Offset, size1: u32, @@ -31,6 +35,7 @@ pub enum DataSegmentError { "invalid data segment: segment at {0:#x} conflicts with a previous segment declaration at \ this address" )] + #[diagnostic()] Mismatch(Offset), /// The current segment and size do not fall in the boundaries of the heap /// which is allocatable to globals and other heap allocations. @@ -42,18 +47,21 @@ pub enum DataSegmentError { "invalid data segment: segment of {size} bytes at {offset:#x} would extend beyond the end \ of the usable heap" )] + #[diagnostic()] OutOfBounds { offset: Offset, size: u32 }, /// The initializer for the current segment has a size greater than `u32::MAX` bytes #[error( "invalid data segment: segment at {0:#x} was declared with an initializer larger than \ 2^32 bytes" )] + #[diagnostic()] InitTooLarge(Offset), /// The initializer for the current segment has a size greater than the declared segment size #[error( "invalid data segment: segment of {size} bytes at {offset:#x} has an initializer of \ {actual} bytes" )] + #[diagnostic()] InitOutOfBounds { offset: Offset, size: u32, @@ -210,6 +218,8 @@ pub struct DataSegment { init: ConstantData, /// Whether or not this segment is intended to be read-only data readonly: bool, + /// Whether or not this segment starts as all zeros + zeroed: bool, } impl fmt::Debug for DataSegment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -218,6 +228,7 @@ impl fmt::Debug for DataSegment { .field("size", &self.size) .field("init", &format_args!("{}", &self.init)) .field("readonly", &self.readonly) + .field("zeroed", &self.zeroed) .finish() } } @@ -288,12 +299,15 @@ impl DataSegment { let size = core::cmp::max(size, init_size); offset.checked_add(size).ok_or(DataSegmentError::OutOfBounds { offset, size })?; + let zeroed = init.is_empty() || init.as_slice().iter().all(|byte| byte == &0u8); + Ok(Self { link: Default::default(), offset, size, init, readonly, + zeroed, }) } @@ -316,6 +330,11 @@ impl DataSegment { pub const fn is_readonly(&self) -> bool { self.readonly } + + /// Returns true if this segment is zeroed at initialization + pub const fn is_zeroed(&self) -> bool { + self.zeroed + } } impl Eq for DataSegment {} impl PartialEq for DataSegment { diff --git a/hir/src/testing.rs b/hir/src/testing.rs index 3b6645ea6..eeb397a67 100644 --- a/hir/src/testing.rs +++ b/hir/src/testing.rs @@ -1,12 +1,27 @@ -use std::{mem, path::Path, slice, sync::Arc}; +use alloc::sync::Arc; +use core::{mem, slice}; +use std::path::Path; -use miden_diagnostics::Emitter; use midenc_session::{Options, Session}; -use super::*; +use crate::{ + diagnostics::{ + DefaultSourceManager, Emitter, SourceFile, SourceId, SourceManagerExt, SourceSpan, + }, + *, +}; const PAGE_SIZE: u32 = 64 * 1024; +fn setup_diagnostics() { + use crate::diagnostics::reporting::{self, ReportHandlerOpts}; + + let result = reporting::set_hook(Box::new(|_| Box::new(ReportHandlerOpts::new().build()))); + if result.is_ok() { + reporting::set_panic_hook(); + } +} + /// The base context used by all IR tests pub struct TestContext { pub session: Session, @@ -19,6 +34,8 @@ impl Default for TestContext { impl TestContext { /// Create a new test context with the given [Session] pub fn new(session: Session) -> Self { + setup_diagnostics(); + Self { session } } @@ -32,33 +49,41 @@ impl TestContext { ) -> Self { use midenc_session::InputFile; + setup_diagnostics(); + + let source_manager = Arc::new(DefaultSourceManager::default()); let session = Session::new( - Default::default(), InputFile::from_path("test.hir").unwrap(), None, None, None, options, emitter, + source_manager, ); Self { session } } /// Add a source file to this context - pub fn add>(&mut self, path: P) -> miden_diagnostics::SourceId { - self.session.codemap.add_file(path).expect("invalid source file") + pub fn add>(&self, path: P) -> Arc { + self.session + .source_manager + .load_file(path.as_ref()) + .expect("invalid source file") } /// Get a [SourceSpan] corresponding to the callsite of this function #[track_caller] #[inline(never)] - pub fn current_span(&self) -> miden_diagnostics::SourceSpan { + pub fn current_span(&self) -> SourceSpan { let caller = core::panic::Location::caller(); let caller_file = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().join(caller.file()); - let source_id = self.session.codemap.add_file(caller_file).expect("invalid source file"); - self.span(source_id, caller.line(), caller.column()) + let source_file = self.add(caller_file); + source_file + .line_column_to_span(caller.line(), caller.column()) + .expect("could not resolve source location") } /// Get a [SourceSpan] representing the location in the given source file (by id), line and @@ -66,16 +91,13 @@ impl TestContext { /// /// It is expected that line and column are 1-indexed, so they will be shifted to be 0-indexed, /// make sure to add 1 if you already have a 0-indexed line/column on hand - pub fn span( - &self, - source_id: miden_diagnostics::SourceId, - line: u32, - column: u32, - ) -> miden_diagnostics::SourceSpan { + pub fn span(&self, source_id: SourceId, line: u32, column: u32) -> SourceSpan { self.session - .codemap - .line_column_to_span(source_id, line - 1, column - 1) - .expect("invalid source location") + .source_manager + .get(source_id) + .ok() + .and_then(|file| file.line_column_to_span(line - 1, column - 1)) + .unwrap_or_default() } } @@ -88,8 +110,12 @@ macro_rules! current_file { #[macro_export] macro_rules! span { - ($codemap:ident, $src:ident) => { - $codemap.line_column_to_span($src, line!() - 1, column!() - 1).unwrap() + ($source_manager:ident, $src:ident) => { + $source_manager + .get($src) + .ok() + .and_then(|file| file.line_column_to_span(line!() - 1, column!() - 1)) + .unwrap() }; } diff --git a/hir/src/tests.rs b/hir/src/tests.rs index c043725ba..2e1d44bfd 100644 --- a/hir/src/tests.rs +++ b/hir/src/tests.rs @@ -38,68 +38,68 @@ fn inline_asm_builders_test() { }; let mut asm_builder = fb.ins().inline_asm(&[ptr, len], [Type::Felt], SourceSpan::UNKNOWN); - asm_builder.ins().push(Felt::ZERO); // [sum, ptr, len] - asm_builder.ins().push_u32(0); // [i, sum, ptr, len] - asm_builder.ins().dup(0); // [i, i, sum, ptr, len] - asm_builder.ins().dup(4); // [len, i, i, sum, ptr, len] - asm_builder.ins().lt_u32(); // [i < len, i, sum, ptr, len] + asm_builder.ins().push(Felt::ZERO, SourceSpan::UNKNOWN); // [sum, ptr, len] + asm_builder.ins().push_u32(0, SourceSpan::UNKNOWN); // [i, sum, ptr, len] + asm_builder.ins().dup(0, SourceSpan::UNKNOWN); // [i, i, sum, ptr, len] + asm_builder.ins().dup(4, SourceSpan::UNKNOWN); // [len, i, i, sum, ptr, len] + asm_builder.ins().lt_u32(SourceSpan::UNKNOWN); // [i < len, i, sum, ptr, len] // Now, build the loop body // // The state of the stack on entry is: [i, sum, ptr, len] - let mut lb = asm_builder.ins().while_true(); + let mut lb = asm_builder.ins().while_true(SourceSpan::UNKNOWN); // Calculate `i / 4` - lb.ins().dup(0); // [i, i, sum, ptr, len] - lb.ins().div_imm_u32(4); // [word_offset, i, sum, ptr, len] + lb.ins().dup(0, SourceSpan::UNKNOWN); // [i, i, sum, ptr, len] + lb.ins().div_imm_u32(4, SourceSpan::UNKNOWN); // [word_offset, i, sum, ptr, len] // Calculate the address for `array[i / 4]` - lb.ins().dup(3); // [ptr, word_offset, ..] - lb.ins().swap(1); - lb.ins().add_u32(Overflow::Checked); // [ptr + word_offset, i, sum, ptr, len] + lb.ins().dup(3, SourceSpan::UNKNOWN); // [ptr, word_offset, ..] + lb.ins().swap(1, SourceSpan::UNKNOWN); + lb.ins().add_u32(Overflow::Checked, SourceSpan::UNKNOWN); // [ptr + word_offset, i, sum, ptr, len] // Calculate the `i % 4` - lb.ins().dup(1); // [i, ptr + word_offset, i, sum, ptr, len] - lb.ins().mod_imm_u32(4); // [element_offset, ptr + word_offset, ..] + lb.ins().dup(1, SourceSpan::UNKNOWN); // [i, ptr + word_offset, i, sum, ptr, len] + lb.ins().mod_imm_u32(4, SourceSpan::UNKNOWN); // [element_offset, ptr + word_offset, ..] // Precalculate what elements of the word to drop, so that // we are only left with the specific element we wanted - lb.ins().push_u32(4); // [n, element_offset, ..] - let mut rb = lb.ins().repeat(3); - rb.ins().sub_imm_u32(1, Overflow::Checked); // [n = n - 1, element_offset] - rb.ins().dup(1); // [element_offset, n, element_offset, ..] - rb.ins().dup(1); // [n, element_offset, n, element_offset, ..] - rb.ins().lt_u32(); // [element_offset < n, n, element_offset, ..] - rb.ins().movdn(2); // [n, element_offset, element_offset < n] + lb.ins().push_u32(4, SourceSpan::UNKNOWN); // [n, element_offset, ..] + let mut rb = lb.ins().repeat(3, SourceSpan::UNKNOWN); + rb.ins().sub_imm_u32(1, Overflow::Checked, SourceSpan::UNKNOWN); // [n = n - 1, element_offset] + rb.ins().dup(1, SourceSpan::UNKNOWN); // [element_offset, n, element_offset, ..] + rb.ins().dup(1, SourceSpan::UNKNOWN); // [n, element_offset, n, element_offset, ..] + rb.ins().lt_u32(SourceSpan::UNKNOWN); // [element_offset < n, n, element_offset, ..] + rb.ins().movdn(2, SourceSpan::UNKNOWN); // [n, element_offset, element_offset < n] rb.build(); // [0, element_offset, element_offset < 1, element_offset < 2, ..] // Clean up the now unused operands we used to calculate which element we want - lb.ins().drop(); // [element_offset, ..] - lb.ins().drop(); // [element_offset < 1, ..] + lb.ins().drop(SourceSpan::UNKNOWN); // [element_offset, ..] + lb.ins().drop(SourceSpan::UNKNOWN); // [element_offset < 1, ..] // Load the word - lb.ins().movup(3); // [ptr + word_offset, element_offset < 1] - lb.ins().loadw(); // [word[0], word[1], word[2], word[3], element_offset < 1] + lb.ins().movup(3, SourceSpan::UNKNOWN); // [ptr + word_offset, element_offset < 1] + lb.ins().loadw(SourceSpan::UNKNOWN); // [word[0], word[1], word[2], word[3], element_offset < 1] // Select the element, `E`, that we want by conditionally dropping // elements on the operand stack with a carefully chosen sequence // of conditionals: E < N forall N in 0..=3 - lb.ins().movup(4); // [element_offset < 1, word[0], ..] - lb.ins().cdrop(); // [word[0 or 1], word[2], word[3], element_offset < 2] - lb.ins().movup(3); // [element_offset < 2, word[0 or 1], ..] - lb.ins().cdrop(); // [word[0 or 1 or 2], word[3], element_offset < 3] - lb.ins().movup(2); // [element_offset < 3, ..] - lb.ins().cdrop(); // [array[i], i, sum, ptr, len] - lb.ins().movup(2); // [sum, array[i], i, ptr, len] - lb.ins().add(); // [sum + array[i], i, ptr, len] - lb.ins().swap(1); // [i, sum + array[i], ptr, len] + lb.ins().movup(4, SourceSpan::UNKNOWN); // [element_offset < 1, word[0], ..] + lb.ins().cdrop(SourceSpan::UNKNOWN); // [word[0 or 1], word[2], word[3], element_offset < 2] + lb.ins().movup(3, SourceSpan::UNKNOWN); // [element_offset < 2, word[0 or 1], ..] + lb.ins().cdrop(SourceSpan::UNKNOWN); // [word[0 or 1 or 2], word[3], element_offset < 3] + lb.ins().movup(2, SourceSpan::UNKNOWN); // [element_offset < 3, ..] + lb.ins().cdrop(SourceSpan::UNKNOWN); // [array[i], i, sum, ptr, len] + lb.ins().movup(2, SourceSpan::UNKNOWN); // [sum, array[i], i, ptr, len] + lb.ins().add(SourceSpan::UNKNOWN); // [sum + array[i], i, ptr, len] + lb.ins().swap(1, SourceSpan::UNKNOWN); // [i, sum + array[i], ptr, len] // We've reached the end of the loop, but we need a copy of the // loop header here in order to use the expression `i < len` as // the condition for the loop - lb.ins().dup(0); // [i, i, sum + array[i], ptr, len] - lb.ins().dup(4); // [len, i, i, sum + array[i], ptr, len] - lb.ins().lt_u32(); // [i < len, i, sum + array[i], ptr, len] + lb.ins().dup(0, SourceSpan::UNKNOWN); // [i, i, sum + array[i], ptr, len] + lb.ins().dup(4, SourceSpan::UNKNOWN); // [len, i, i, sum + array[i], ptr, len] + lb.ins().lt_u32(SourceSpan::UNKNOWN); // [i < len, i, sum + array[i], ptr, len] // Finalize, it is at this point that validation will occur lb.build(); @@ -107,10 +107,10 @@ fn inline_asm_builders_test() { // Clean up the operand stack and return the sum // // The stack here is: [i, sum, ptr, len] - asm_builder.ins().swap(1); // [sum, i, ptr, len] - asm_builder.ins().movdn(3); // [i, ptr, len, sum] - let mut rb = asm_builder.ins().repeat(3); - rb.ins().drop(); + asm_builder.ins().swap(1, SourceSpan::UNKNOWN); // [sum, i, ptr, len] + asm_builder.ins().movdn(3, SourceSpan::UNKNOWN); // [i, ptr, len, sum] + let mut rb = asm_builder.ins().repeat(3, SourceSpan::UNKNOWN); + rb.ins().drop(SourceSpan::UNKNOWN); rb.build(); // [sum] // Finish the inline assembly block diff --git a/hir/src/value.rs b/hir/src/value.rs index eec14516a..24ffa6090 100644 --- a/hir/src/value.rs +++ b/hir/src/value.rs @@ -1,7 +1,6 @@ use cranelift_entity::{self as entity, entity_impl}; -use miden_diagnostics::SourceSpan; -use super::{Block, Inst, Type}; +use crate::{diagnostics::SourceSpan, Block, Inst, Type}; pub type ValueList = entity::EntityList; pub type ValueListPool = entity::ListPool; diff --git a/midenc-compile/Cargo.toml b/midenc-compile/Cargo.toml index 9d44463aa..5dc96c5f7 100644 --- a/midenc-compile/Cargo.toml +++ b/midenc-compile/Cargo.toml @@ -14,18 +14,16 @@ readme.workspace = true edition.workspace = true [dependencies] -anyhow.workspace = true clap.workspace = true log.workspace = true inventory.workspace = true -miden-assembly.workspace = true midenc-codegen-masm.workspace = true +miden-assembly = { workspace = true, features = ["std"] } miden-diagnostics.workspace = true midenc-frontend-wasm.workspace = true midenc-hir.workspace = true midenc-hir-analysis.workspace = true midenc-hir-transform.workspace = true midenc-session.workspace = true -rustc-hash.workspace = true thiserror.workspace = true wat.workspace = true diff --git a/midenc-compile/src/compiler.rs b/midenc-compile/src/compiler.rs index b701d28c1..7e9203e50 100644 --- a/midenc-compile/src/compiler.rs +++ b/midenc-compile/src/compiler.rs @@ -1,10 +1,10 @@ use std::{path::PathBuf, sync::Arc}; -use clap::{Args, ColorChoice}; -use miden_diagnostics::{term::termcolor::ColorChoice as MDColorChoice, Emitter}; +use clap::{builder::ArgPredicate, Args, ColorChoice, Parser}; use midenc_session::{ - InputFile, Options, OutputFile, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, - TargetEnv, VerbosityFlag, Warnings, + diagnostics::{ColorChoice as MDColorChoice, DefaultSourceManager, Emitter}, + DebugInfo, InputFile, LinkLibrary, OptLevel, Options, OutputFile, OutputType, OutputTypeSpec, + OutputTypes, ProjectType, Session, TargetEnv, Verbosity, Warnings, }; /// Compile a program from WebAssembly or Miden IR, to Miden Assembly. @@ -14,110 +14,254 @@ pub struct Compiler { /// /// You may specify `-` to read from stdin, otherwise you must provide a path #[arg(required(true), value_name = "FILE")] - input: InputFile, + pub input: InputFile, + /// Write all intermediate compiler artifacts to `` + /// + /// Defaults to a directory named `target` in the current working directory + #[arg( + hide(true), + long, + value_name = "DIR", + env = "MIDENC_TARGET_DIR", + help_heading = "Output" + )] + pub target_dir: Option, + /// The working directory for the compiler + /// + /// By default this will be the working directory the compiler is executed from + #[arg(long, value_name = "DIR", help_heading = "Output")] + pub working_dir: Option, + /// The path to the root directory of the Miden toolchain libraries + /// + /// By default this is assumed to be ~/.miden/toolchains/ + #[arg( + long, + value_name = "DIR", + env = "MIDENC_SYSROOT", + help_heading = "Compiler" + )] + pub sysroot: Option, + /// Write output to compiler-chosen filename in `` + #[arg( + long, + value_name = "DIR", + env = "MIDENC_OUT_DIR", + help_heading = "Output" + )] + pub output_dir: Option, + /// Write output to `` + #[arg(long, short = 'o', value_name = "FILENAME", help_heading = "Output")] + pub output_file: Option, + /// Write output to stdout + #[arg(long, conflicts_with("output_file"), help_heading = "Output")] + pub stdout: bool, + #[command(flatten)] + pub options: CompilerOptions, +} + +/// Used to parse `CompilerOptions` for tests +#[derive(Debug, Parser)] +#[command(name = "midenc")] +pub struct TestCompiler { + #[command(flatten)] + pub options: CompilerOptions, +} + +#[derive(Debug, Args)] +pub struct CompilerOptions { /// Specify what type and level of informational output to emit #[arg( long = "verbose", short = 'v', value_enum, value_name = "LEVEL", - next_line_help(true), - default_value_t = VerbosityFlag::Info, + default_value_t = Verbosity::Info, default_missing_value = "debug", + num_args(0..=1), help_heading = "Diagnostics" )] - verbosity: VerbosityFlag, + pub verbosity: Verbosity, /// Specify how warnings should be treated by the compiler. #[arg( long, short = 'W', value_enum, value_name = "LEVEL", - next_line_help(true), default_value_t = Warnings::All, default_missing_value = "all", + num_args(0..=1), help_heading = "Diagnostics" )] - warn: Warnings, + pub warn: Warnings, /// Whether, and how, to color terminal output - #[arg(long, value_enum, default_value_t = ColorChoice::Auto, default_missing_value = "auto", help_heading = "Diagnostics")] - color: ColorChoice, + #[arg( + long, + value_enum, + default_value_t = ColorChoice::Auto, + default_missing_value = "auto", + num_args(0..=1), + help_heading = "Diagnostics" + )] + pub color: ColorChoice, /// The target environment to compile for - #[arg(long, value_name = "TARGET", default_value_t = TargetEnv::Base, help_heading = "Compiler")] - target: TargetEnv, + #[arg( + long, + value_name = "TARGET", + default_value_t = TargetEnv::Base, + help_heading = "Compiler" + )] + pub target: TargetEnv, + /// Specify the function to call as the entrypoint for the program + #[arg(long, help_heading = "Compiler", hide(true))] + pub entrypoint: Option, /// Tells the compiler to produce an executable Miden program /// - /// When the target is `base` or `rollup`, this defaults to true + /// Implied by `--entrypoint`, defaults to true for non-rollup targets. #[arg( long = "exe", default_value_t = true, - default_value_if("target", "emu", Some("false")), - help_heading = "Compiler" + default_value_ifs([ + // When targeting the rollup, never build an executable + ("target", "rollup".into(), Some("false")), + // Setting the entrypoint implies building an executable in all other cases + ("entrypoint", ArgPredicate::IsPresent, Some("true")), + ]), + help_heading = "Linker" )] - is_program: bool, + pub is_program: bool, /// Tells the compiler to produce a Miden library /// - /// When the target is `emu`, this defaults to true + /// Implied by `--target rollup`, defaults to false. #[arg( long = "lib", conflicts_with("is_program"), + conflicts_with("entrypoint"), default_value_t = false, - default_value_if("target", "emu", Some("true")), - help_heading = "Compiler" + default_value_ifs([ + // When an entrypoint is specified, always set the default to false + ("entrypoint", ArgPredicate::IsPresent, Some("false")), + // When targeting the rollup, we always build as a library + ("target", "rollup".into(), Some("true")), + ]), + help_heading = "Linker" )] - is_library: bool, - /// Write all intermediate compiler artifacts to `` - /// - /// Defaults to a directory named `target` in the current working directory + pub is_library: bool, + /// Specify one or more search paths for link libraries requested via `-l` #[arg( - hide(true), - long, - value_name = "DIR", - env = "MIDENC_TARGET_DIR", - help_heading = "Output" + long = "search-path", + short = 'L', + value_name = "PATH", + help_heading = "Linker" )] - target_dir: Option, - /// The working directory for the compiler + pub search_path: Vec, + /// Link compiled projects to the specified library NAME. /// - /// By default this will be the working directory the compiler is executed from - #[arg(long, value_name = "DIR", help_heading = "Output")] - pub working_dir: Option, - /// Write output to compiler-chosen filename in `` + /// The optional KIND can be provided to indicate what type of library it is. + /// + /// NAME must either be an absolute path (with extension when applicable), or + /// a library namespace (no extension). The former will be used as the path + /// to load the library, without looking for it in the library search paths, + /// while the latter will be located in the search path based on its KIND. + /// + /// See below for valid KINDs: #[arg( - long, - value_name = "DIR", - env = "MIDENC_OUT_DIR", - help_heading = "Output" + long = "link-library", + short = 'l', + value_name = "[KIND=]NAME", + next_line_help(true), + help_heading = "Linker" )] - output_dir: Option, - /// Write output to `` - #[arg(long, short = 'o', value_name = "FILENAME", help_heading = "Output")] - output_file: Option, - /// Write output to stdout - #[arg(long, conflicts_with("output_file"), help_heading = "Output")] - stdout: bool, + pub link_libraries: Vec, /// Specify one or more output types for the compiler to emit + /// + /// The format for SPEC is `KIND[=PATH]`. You can specify multiple items at + /// once by separating each SPEC with a comma, you can also pass this flag + /// multiple times. + /// + /// PATH must be a directory in which to place the outputs, or `-` for stdout. #[arg( long = "emit", value_name = "SPEC", value_delimiter = ',', + next_line_help(true), + help_heading = "Output" + )] + pub output_types: Vec, + /// Specify what level of debug information to emit in compilation artifacts + #[arg( + long, + value_enum, + value_name = "LEVEL", + next_line_help(true), + default_value_t = DebugInfo::Full, + default_missing_value = "full", + num_args(0..=1), help_heading = "Output" )] - output_types: Vec, + pub debug: DebugInfo, + /// Specify what type, and to what degree, of optimizations to apply to code during + /// compilation. + #[arg( + long = "optimize", + value_enum, + value_name = "LEVEL", + next_line_help(true), + default_value_t = OptLevel::None, + default_missing_value = "balanced", + num_args(0..=1), + help_heading = "Output" + )] + pub opt_level: OptLevel, /// Print the IR after each pass is applied #[arg(long, default_value_t = false, help_heading = "Passes")] - print_ir_after_all: bool, + pub print_ir_after_all: bool, /// Print the IR after running a specific pass #[arg(long, value_name = "PASS", help_heading = "Passes")] - print_ir_after_pass: Option, + pub print_ir_after_pass: Option, } impl Compiler { /// Use this configuration to obtain a [Session] used for compilation pub fn into_session(self, emitter: Option>) -> Session { - let cwd = self - .working_dir - .unwrap_or_else(|| std::env::current_dir().expect("no working directory available")); + let source_manager = Arc::new(DefaultSourceManager::default()); let tmp_dir = self.target_dir.unwrap_or_else(std::env::temp_dir); + let output_file = match self.output_file { + Some(path) => Some(OutputFile::Real(path)), + None if self.stdout => Some(OutputFile::Stdout), + None => None, + }; + let cwd = self.working_dir; + let sysroot = self.sysroot; + let options = self.options.into_options(cwd, sysroot); + + Session::new( + self.input, + self.output_dir, + output_file, + Some(tmp_dir), + options, + emitter, + source_manager, + ) + } +} + +impl CompilerOptions { + pub fn parse_options(extra_args: &[&str]) -> midenc_session::Options { + let command = ::command(); + let command = crate::register_flags(command); + let mut matches = command.try_get_matches_from(extra_args).unwrap_or_else(|err| err.exit()); + let compile_matches = matches.clone(); + + let copts = ::from_arg_matches_mut(&mut matches) + .map_err(format_error::) + .unwrap_or_else(|err| err.exit()); + + copts.into_options(None, None).with_arg_matches(compile_matches) + } + + pub fn into_options(self, working_dir: Option, sysroot: Option) -> Options { + let cwd = working_dir + .unwrap_or_else(|| std::env::current_dir().expect("no working directory available")); let color = match self.color { ColorChoice::Auto => MDColorChoice::Auto, @@ -125,38 +269,33 @@ impl Compiler { ColorChoice::Never => MDColorChoice::Never, }; + let mut output_types = OutputTypes::new(self.output_types); + if output_types.is_empty() { + output_types.insert(OutputType::Mast, None); + } + let project_type = if self.is_program { ProjectType::Program } else { ProjectType::Library }; - let mut output_types = OutputTypes::new(self.output_types); - if output_types.is_empty() { - output_types.insert(OutputType::Mast, None); - } - let mut options = Options::new(cwd) + let mut options = Options::new(self.target, project_type, cwd, sysroot) .with_color(color) .with_verbosity(self.verbosity.into()) .with_warnings(self.warn) + .with_debug_info(self.debug) + .with_optimization(self.opt_level) .with_output_types(output_types); + options.search_paths = self.search_path; + options.link_libraries = self.link_libraries; + options.entrypoint = self.entrypoint; options.print_ir_after_all = self.print_ir_after_all; options.print_ir_after_pass = self.print_ir_after_pass; - - let output_file = match self.output_file { - Some(path) => Some(OutputFile::Real(path)), - None if self.stdout => Some(OutputFile::Stdout), - None => None, - }; - - Session::new( - self.target, - self.input, - self.output_dir, - output_file, - Some(tmp_dir), - options, - emitter, - ) - .with_project_type(project_type) + options } } + +fn format_error(err: clap::Error) -> clap::Error { + let mut cmd = I::command(); + err.format(&mut cmd) +} diff --git a/midenc-compile/src/lib.rs b/midenc-compile/src/lib.rs index 7264fe416..20b0bb205 100644 --- a/midenc-compile/src/lib.rs +++ b/midenc-compile/src/lib.rs @@ -5,65 +5,25 @@ mod stages; use std::sync::Arc; use midenc_codegen_masm as masm; -use midenc_hir::pass::AnalysisManager; +use midenc_hir::{ + diagnostics::{miette, Diagnostic, IntoDiagnostic, Report, WrapErr}, + pass::AnalysisManager, +}; use midenc_session::{OutputType, Session}; -pub use self::{compiler::Compiler, stages::Compiled}; +pub use self::compiler::{Compiler, CompilerOptions}; use self::{stage::Stage, stages::*}; -pub type CompilerResult = Result; +pub type CompilerResult = Result; -#[derive(Debug, thiserror::Error)] -pub enum CompilerError { - /// An error was raised due to invalid command-line arguments or argument validation - #[error(transparent)] - Clap(#[from] clap::Error), - /// The compilation pipeline was stopped early - #[error("compilation was canceled by user")] - Stopped, - /// An invalid input was given to the compiler - #[error(transparent)] - InvalidInput(#[from] midenc_session::InvalidInputError), - /// An error occurred while parsing/translating a Wasm module from binary - #[error(transparent)] - WasmError(#[from] midenc_frontend_wasm::WasmError), - /// An error occurred while parsing/translating a Wasm module from text - #[error(transparent)] - WatError(#[from] wat::Error), - /// An error occurred while parsing an HIR module - #[error(transparent)] - Parsing(#[from] midenc_hir::parser::ParseError), - /// An error occurred while running an analysis - #[error(transparent)] - Analysis(#[from] midenc_hir::pass::AnalysisError), - /// An error occurred while rewriting an IR entity - #[error(transparent)] - Rewriting(#[from] midenc_hir::pass::RewriteError), - /// An error occurred while converting from one dialect to another - #[error(transparent)] - Conversion(#[from] midenc_hir::pass::ConversionError), - /// An error occurred while linking a program - #[error(transparent)] - Linker(#[from] midenc_hir::LinkerError), - /// An error occurred when reading a file - #[error(transparent)] - Io(#[from] std::io::Error), - /// An error occurred while compiling a program - #[error(transparent)] - Failed(#[from] anyhow::Error), - /// An error was emitted as a diagnostic, so we don't need to emit info to stdout - #[error("exited due to error: see diagnostics for details")] - Reported, -} -impl From for CompilerError { - fn from(err: midenc_hir::ModuleConflictError) -> CompilerError { - Self::Linker(midenc_hir::LinkerError::ModuleConflict(err.0)) - } -} +/// The compilation pipeline was stopped early +#[derive(Debug, thiserror::Error, Diagnostic)] +#[error("compilation was canceled by user")] +#[diagnostic()] +pub struct CompilerStopped; /// Register dynamic flags to be shown via `midenc help compile` pub fn register_flags(cmd: clap::Command) -> clap::Command { - use midenc_hir::RewritePassRegistration; use midenc_session::CompileFlag; let cmd = inventory::iter::.into_iter().fold(cmd, |cmd, flag| { @@ -100,88 +60,71 @@ pub fn register_flags(cmd: clap::Command) -> clap::Command { } else { arg }; + let arg = if let Some(value) = flag.hide { + arg.hide(value) + } else { + arg + }; cmd.arg(arg) }); - inventory::iter::>.into_iter().fold( - cmd, - |cmd, rewrite| { - let name = rewrite.name(); - let arg = clap::Arg::new(name) - .long(name) - .action(clap::ArgAction::SetTrue) - .help(rewrite.summary()) - .help_heading("Transformations"); - cmd.arg(arg) - }, - ) + cmd } /// Run the compiler using the provided [Session] pub fn compile(session: Arc) -> CompilerResult<()> { - let inputs = vec![session.input.clone()]; let mut analyses = AnalysisManager::new(); - match compile_inputs(inputs, &mut analyses, &session) { - Ok(Compiled::Program(ref program)) => { - if let Some(path) = session.emit_to(OutputType::Mast, None) { - log::warn!( - "skipping emission of MAST to {} as output type is not fully supported yet", - path.display() - ); - } + match compile_inputs(session.inputs.clone(), &mut analyses, &session)? { + // No outputs, generally due to skipping codegen + None => return Ok(()), + Some(output) => { if session.should_emit(OutputType::Masm) { - for module in program.modules() { - session.emit(module)?; + for module in output.modules() { + session.emit(module).into_diagnostic()?; } } - } - Ok(Compiled::Modules(modules)) => { - let mut program = masm::Program::empty(); - for module in modules.into_iter() { - program.insert(module); - } if let Some(path) = session.emit_to(OutputType::Mast, None) { - log::warn!( - "skipping emission of MAST to {} as output type is not fully supported yet", - path.display() - ); - } - if session.should_emit(OutputType::Masm) { - for module in program.modules() { - session.emit(module)?; + match output { + masm::MasmArtifact::Executable(_) => { + log::warn!( + "skipping emission of MAST to {} as output type is not fully \ + supported yet", + path.display() + ); + } + masm::MasmArtifact::Library(ref library) => { + let mast = library.assemble(&session)?; + mast.write_to_file( + path.clone(), + miden_assembly::ast::AstSerdeOptions { + debug_info: session.options.emit_debug_decorators(), + ..Default::default() + }, + ) + .into_diagnostic() + .wrap_err_with(|| { + format!("failed to write MAST to '{}'", path.display()) + })?; + } } } } - Err(CompilerError::Stopped) => return Ok(()), - Err(CompilerError::Reported) => return Err(CompilerError::Reported), - Err(err) => { - session.diagnostics.error(err); - session.diagnostics.abort_if_errors(); - } } Ok(()) } /// Same as `compile`, but return compiled artifacts to the caller -pub fn compile_to_memory(session: Arc) -> CompilerResult { - let inputs = vec![session.input.clone()]; +pub fn compile_to_memory(session: Arc) -> CompilerResult> { let mut analyses = AnalysisManager::new(); - match compile_inputs(inputs, &mut analyses, &session) { - Ok(output) => Ok(output), - Err(err) => { - session.diagnostics.error(err.to_string()); - session.diagnostics.abort_if_errors(); - Err(CompilerError::Reported) - } - } + compile_inputs(session.inputs.clone(), &mut analyses, &session) } fn compile_inputs( inputs: Vec, analyses: &mut AnalysisManager, session: &Session, -) -> CompilerResult { +) -> CompilerResult> { let mut stages = ParseStage .next(SemanticAnalysisStage) .next_optional(ApplyRewritesStage) diff --git a/midenc-compile/src/stage.rs b/midenc-compile/src/stage.rs index ef3a16ede..12d2fb070 100644 --- a/midenc-compile/src/stage.rs +++ b/midenc-compile/src/stage.rs @@ -1,7 +1,7 @@ use midenc_hir::pass::AnalysisManager; use midenc_session::Session; -use crate::{CompilerError, CompilerResult}; +use crate::{CompilerResult, CompilerStopped}; /// This trait is implemented by a stage in the compiler pub trait Stage { @@ -72,11 +72,11 @@ where session: &Session, ) -> CompilerResult { if !self.a.enabled(session) { - return Err(CompilerError::Stopped); + return Err(CompilerStopped.into()); } let output = self.a.run(input, analyses, session)?; if !self.b.enabled(session) { - return Err(CompilerError::Stopped); + return Err(CompilerStopped.into()); } self.b.run(output, analyses, session) } @@ -107,7 +107,7 @@ where session: &Session, ) -> CompilerResult { if !self.a.enabled(session) { - return Err(CompilerError::Stopped); + return Err(CompilerStopped.into()); } let output = self.a.run(input, analyses, session)?; if !self.b.enabled(session) { diff --git a/midenc-compile/src/stages/codegen.rs b/midenc-compile/src/stages/codegen.rs index a3c8f1700..9fa8ec55d 100644 --- a/midenc-compile/src/stages/codegen.rs +++ b/midenc-compile/src/stages/codegen.rs @@ -2,18 +2,11 @@ use midenc_codegen_masm::intrinsics; use super::*; -/// The code generator may output either a single program, -/// ora collection of modules, depending on earlier stages. -pub enum Compiled { - Program(Box), - Modules(Vec>), -} - /// Perform code generation on the possibly-linked output of previous stages pub struct CodegenStage; impl Stage for CodegenStage { - type Input = MaybeLinked; - type Output = Compiled; + type Input = LinkerOutput; + type Output = Option; fn enabled(&self, session: &Session) -> bool { session.should_codegen() @@ -21,41 +14,36 @@ impl Stage for CodegenStage { fn run( &mut self, - input: Self::Input, + linker_output: Self::Input, analyses: &mut AnalysisManager, session: &Session, ) -> CompilerResult { - match input { - MaybeLinked::Linked(program) => { - let mut convert_to_masm = masm::ConvertHirToMasm::::default(); - let mut program = convert_to_masm.convert(program, analyses, session)?; - // Ensure intrinsics modules are linked - for intrinsics_module in required_intrinsics_modules(session) { - program.insert(Box::new(intrinsics_module)); - } - Ok(Compiled::Program(program)) - } - MaybeLinked::Unlinked(modules) => { - let mut convert_to_masm = masm::ConvertHirToMasm::::default(); - let mut masm_modules = Vec::with_capacity(modules.len()); - // Ensure intrinsics modules are linked - for intrinsics_module in required_intrinsics_modules(session) { - masm_modules.push(Box::new(intrinsics_module)); - } - for module in modules.into_iter() { - let masm_module = convert_to_masm.convert(module, analyses, session)?; - masm_modules.push(masm_module); - } - Ok(Compiled::Modules(masm_modules)) - } + let Some(program) = linker_output.program else { + return Ok(None); + }; + + let mut convert_to_masm = masm::ConvertHirToMasm::::default(); + let mut artifact = convert_to_masm.convert(program, analyses, session)?; + // Ensure intrinsics modules are linked + for intrinsics_module in required_intrinsics_modules(session) { + artifact.insert(Box::new(intrinsics_module)); } + // Link in any MASM inputs provided to the compiler + for module in linker_output.masm { + artifact.insert(module); + } + + Ok(Some(artifact)) } } fn required_intrinsics_modules(session: &Session) -> Vec { vec![ - intrinsics::load("intrinsics::mem", &session.codemap).expect("undefined intrinsics module"), - intrinsics::load("intrinsics::i32", &session.codemap).expect("undefined intrinsics module"), - intrinsics::load("intrinsics::i64", &session.codemap).expect("undefined intrinsics module"), + intrinsics::load("intrinsics::mem", &session.source_manager) + .expect("undefined intrinsics module"), + intrinsics::load("intrinsics::i32", &session.source_manager) + .expect("undefined intrinsics module"), + intrinsics::load("intrinsics::i64", &session.source_manager) + .expect("undefined intrinsics module"), ] } diff --git a/midenc-compile/src/stages/link.rs b/midenc-compile/src/stages/link.rs index 20fc47728..7fe01bc4c 100644 --- a/midenc-compile/src/stages/link.rs +++ b/midenc-compile/src/stages/link.rs @@ -1,44 +1,77 @@ -use midenc_session::ProjectType; +use midenc_hir::FunctionIdent; +use midenc_session::diagnostics::Report; use super::*; -/// This type is used to represent the fact that depending on -/// flags provided to the compiler, we may or may not perform -/// the link, in which case we will just have a loose collection -/// of modules, not a [Program] -#[allow(clippy::vec_box)] -pub enum MaybeLinked { - Linked(Box), - Unlinked(Vec>), +pub enum LinkerInput { + Hir(Box), + Masm(Box), +} + +pub struct LinkerOutput { + /// The possibly-linked HIR program + pub program: Option>, + /// The set of MASM inputs to the linker + #[allow(clippy::vec_box)] + pub masm: Vec>, } /// Link together one or more HIR modules into an HIR program pub struct LinkerStage; impl Stage for LinkerStage { - type Input = Vec>; - type Output = MaybeLinked; + type Input = Vec; + type Output = LinkerOutput; fn run( &mut self, - input: Self::Input, + inputs: Self::Input, _analyses: &mut AnalysisManager, session: &Session, ) -> CompilerResult { - // Temporary workaround for the issue that backend builds only Program for all - // OutputType::Masm output types. In case we need a library, we should not link the modules. - match session.project_type { - ProjectType::Program => { - if session.should_link() { - let mut builder = hir::ProgramBuilder::new(&session.diagnostics); - for module in input.into_iter() { - builder.add_module(module)?; - } - Ok(MaybeLinked::Linked(builder.link()?)) - } else { - Ok(MaybeLinked::Unlinked(input.into_iter().collect())) + let mut ir = Vec::with_capacity(inputs.len()); + let mut masm = vec![]; + for input in inputs { + match input { + LinkerInput::Hir(module) => { + ir.push(module); + } + LinkerInput::Masm(module) => { + masm.push(module); } } - ProjectType::Library => Ok(MaybeLinked::Unlinked(input.into_iter().collect())), } + let program = if session.should_link() { + // Construct a new [Program] builder + let mut builder = match session.options.entrypoint.as_deref() { + Some(entrypoint) => { + let entrypoint = entrypoint + .parse::() + .map_err(|err| Report::msg(format!("invalid --entrypoint: {err}")))?; + hir::ProgramBuilder::new(&session.diagnostics).with_entrypoint(entrypoint) + } + None => hir::ProgramBuilder::new(&session.diagnostics), + }; + + // Add our HIR modules + for module in ir.into_iter() { + builder.add_module(module)?; + } + + // Handle linking against ad-hoc MASM sources + for module in masm.iter() { + builder + .add_extern_module(module.id, module.functions().map(|f| f.name.function))?; + } + + // Load link libraries now + for link_lib in session.options.link_libraries.iter() { + builder.add_library(link_lib.load(session)?); + } + + Some(builder.link()?) + } else { + None + }; + Ok(LinkerOutput { program, masm }) } } diff --git a/midenc-compile/src/stages/mod.rs b/midenc-compile/src/stages/mod.rs index 22027d791..cea93edfc 100644 --- a/midenc-compile/src/stages/mod.rs +++ b/midenc-compile/src/stages/mod.rs @@ -8,7 +8,7 @@ use midenc_hir::{ use midenc_session::Session; use super::Stage; -use crate::{CompilerError, CompilerResult}; +use crate::CompilerResult; mod codegen; mod link; @@ -17,8 +17,8 @@ mod rewrite; mod sema; pub use self::{ - codegen::{CodegenStage, Compiled}, - link::{LinkerStage, MaybeLinked}, + codegen::CodegenStage, + link::{LinkerInput, LinkerOutput, LinkerStage}, parse::{ParseOutput, ParseStage}, rewrite::ApplyRewritesStage, sema::SemanticAnalysisStage, diff --git a/midenc-compile/src/stages/parse.rs b/midenc-compile/src/stages/parse.rs index 6cc64747a..493ab1122 100644 --- a/midenc-compile/src/stages/parse.rs +++ b/midenc-compile/src/stages/parse.rs @@ -1,6 +1,9 @@ use std::path::Path; -use midenc_session::InputFile; +use midenc_session::{ + diagnostics::{IntoDiagnostic, Spanned, WrapErr}, + InputFile, +}; use wasm::WasmTranslationConfig; use super::*; @@ -12,6 +15,8 @@ pub enum ParseOutput { Ast(Box), /// We parsed HIR from a Wasm module or other binary format Hir(Box), + /// We parsed MASM from a Miden Assembly module or other binary format + Masm(Box), } /// This stage of compilation is where we parse input files into the @@ -36,7 +41,7 @@ impl Stage for ParseStage { FileType::Hir => self.parse_ast_from_file(path.as_ref(), session), FileType::Wasm => self.parse_hir_from_wasm_file(path.as_ref(), session), FileType::Wat => self.parse_hir_from_wat_file(path.as_ref(), session), - unsupported => unreachable!("unsupported file type: {unsupported}"), + FileType::Masm => self.parse_masm_from_file(path.as_ref(), session), }, InputType::Stdin { name, ref input } => match file_type { FileType::Hir => self.parse_ast_from_bytes(input, session), @@ -56,7 +61,9 @@ impl Stage for ParseStage { ..Default::default() }, ), - unsupported => unreachable!("unsupported file type: {unsupported}"), + FileType::Masm => { + self.parse_masm_from_bytes(name.as_str().unwrap(), input, session) + } }, } } @@ -65,30 +72,25 @@ impl ParseStage { fn parse_ast_from_file(&self, path: &Path, session: &Session) -> CompilerResult { use std::io::Read; - let mut file = std::fs::File::open(path)?; + let mut file = std::fs::File::open(path).into_diagnostic()?; let mut bytes = Vec::with_capacity(1024); - file.read_to_end(&mut bytes)?; + file.read_to_end(&mut bytes).into_diagnostic()?; self.parse_ast_from_bytes(&bytes, session) } fn parse_ast_from_bytes(&self, bytes: &[u8], session: &Session) -> CompilerResult { - use std::io::{Error, ErrorKind}; - use midenc_hir::parser::Parser; - let source = core::str::from_utf8(bytes).map_err(|_| { - CompilerError::Io(Error::new(ErrorKind::InvalidInput, "input is not valid utf-8")) - })?; + let source = core::str::from_utf8(bytes) + .into_diagnostic() + .wrap_err("input is not valid utf-8")?; let parser = Parser::new(session); match parser.parse_str(source).map(Box::new) { Ok(ast) => { - session.emit(&ast)?; + session.emit(&ast).into_diagnostic()?; Ok(ParseOutput::Ast(ast)) } - Err(err) => { - session.diagnostics.emit(err); - Err(CompilerError::Reported) - } + Err(err) => Err(err), } } @@ -99,9 +101,11 @@ impl ParseStage { ) -> CompilerResult { use std::io::Read; - let mut file = std::fs::File::open(path)?; + let mut file = std::fs::File::open(path) + .into_diagnostic() + .wrap_err("could not open input for reading")?; let mut bytes = Vec::with_capacity(1024); - file.read_to_end(&mut bytes)?; + file.read_to_end(&mut bytes).into_diagnostic()?; let file_name = path.file_stem().unwrap().to_str().unwrap().to_owned(); let config = wasm::WasmTranslationConfig { source_name: file_name.into(), @@ -116,7 +120,7 @@ impl ParseStage { session: &Session, config: &WasmTranslationConfig, ) -> CompilerResult { - let module = wasm::translate(bytes, config, &session.diagnostics)?.unwrap_one_module(); + let module = wasm::translate(bytes, config, &session)?.unwrap_one_module(); Ok(ParseOutput::Hir(module)) } @@ -131,8 +135,8 @@ impl ParseStage { source_name: file_name.into(), ..Default::default() }; - let wasm = wat::parse_file(path)?; - let module = wasm::translate(&wasm, &config, &session.diagnostics)?.unwrap_one_module(); + let wasm = wat::parse_file(path).into_diagnostic().wrap_err("failed to parse wat")?; + let module = wasm::translate(&wasm, &config, &session)?.unwrap_one_module(); Ok(ParseOutput::Hir(module)) } @@ -143,9 +147,65 @@ impl ParseStage { session: &Session, config: &WasmTranslationConfig, ) -> CompilerResult { - let wasm = wat::parse_bytes(bytes)?; - let module = wasm::translate(&wasm, config, &session.diagnostics)?.unwrap_one_module(); + let wasm = wat::parse_bytes(bytes).into_diagnostic().wrap_err("failed to parse wat")?; + let module = wasm::translate(&wasm, config, &session)?.unwrap_one_module(); Ok(ParseOutput::Hir(module)) } + + fn parse_masm_from_file(&self, path: &Path, session: &Session) -> CompilerResult { + use miden_assembly::{ + ast::{self, Ident, ModuleKind}, + LibraryNamespace, LibraryPath, + }; + use midenc_codegen_masm as masm; + + // Construct library path for MASM module + let module_name = Ident::new(path.file_stem().unwrap().to_str().unwrap()) + .into_diagnostic() + .wrap_err_with(|| format!("failed to construct valid module identifier from path"))?; + let namespace = path + .parent() + .map(|dir| { + LibraryNamespace::User(dir.to_str().unwrap().to_string().into_boxed_str().into()) + }) + .unwrap_or(LibraryNamespace::Anon); + let name = LibraryPath::new_from_components(namespace, [module_name]); + + // Parse AST + let mut parser = ast::Module::parser(ModuleKind::Library); + let ast = parser.parse_file(name, path, &session.source_manager)?; + let span = ast.span(); + + // Convert to MASM IR representation + Ok(ParseOutput::Masm(Box::new(masm::Module::from_ast(&ast, span)))) + } + + fn parse_masm_from_bytes( + &self, + name: &str, + bytes: &[u8], + session: &Session, + ) -> CompilerResult { + use miden_assembly::{ + ast::{self, ModuleKind}, + LibraryPath, + }; + use midenc_codegen_masm as masm; + + let source = core::str::from_utf8(bytes) + .into_diagnostic() + .wrap_err_with(|| format!("input '{name}' contains invalid utf-8"))?; + + // Construct library path for MASM module + let name = LibraryPath::new(name).into_diagnostic()?; + + // Parse AST + let mut parser = ast::Module::parser(ModuleKind::Library); + let ast = parser.parse_str(name, source, &session.source_manager)?; + let span = ast.span(); + + // Convert to MASM IR representation + Ok(ParseOutput::Masm(Box::new(masm::Module::from_ast(&ast, span)))) + } } diff --git a/midenc-compile/src/stages/rewrite.rs b/midenc-compile/src/stages/rewrite.rs index 319e88dca..6a5b8f1cd 100644 --- a/midenc-compile/src/stages/rewrite.rs +++ b/midenc-compile/src/stages/rewrite.rs @@ -5,8 +5,8 @@ use super::*; /// This stage applies all registered (and enabled) module-scoped rewrites to input HIR module(s) pub struct ApplyRewritesStage; impl Stage for ApplyRewritesStage { - type Input = Box; - type Output = Box; + type Input = LinkerInput; + type Output = LinkerInput; fn enabled(&self, session: &Session) -> bool { !session.parse_only() @@ -14,32 +14,39 @@ impl Stage for ApplyRewritesStage { fn run( &mut self, - mut input: Self::Input, + input: Self::Input, analyses: &mut AnalysisManager, session: &Session, ) -> CompilerResult { - // Get all registered module rewrites and apply them in the order they appear - let mut registered = vec![]; - let matches = session.matches(); - for rewrite in inventory::iter::> { - let flag = rewrite.name(); - if matches.try_contains_id(flag).is_ok() { - if let Some(index) = matches.index_of(flag) { - let is_enabled = matches.get_flag(flag); - if is_enabled { - registered.push((index, rewrite.get())); + match input { + input @ LinkerInput::Masm(_) => Ok(input), + LinkerInput::Hir(mut input) => { + // Get all registered module rewrites and apply them in the order they appear + let mut registered = vec![]; + let matches = session.matches(); + for rewrite in inventory::iter::> { + let flag = rewrite.name(); + if matches.try_contains_id(flag).is_ok() { + if let Some(index) = matches.index_of(flag) { + let is_enabled = matches.get_flag(flag); + if is_enabled { + registered.push((index, rewrite.get())); + } + } } } - } - } - registered.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); + registered.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); - // Populate the set of rewrite passes with default transformations, if there are no - // specific passes selected. - let mut rewrites = - midenc_codegen_masm::default_rewrites(registered.into_iter().map(|(_, r)| r), session); - rewrites.apply(&mut input, analyses, session)?; + // Populate the set of rewrite passes with default transformations, if there are no + // specific passes selected. + let mut rewrites = midenc_codegen_masm::default_rewrites( + registered.into_iter().map(|(_, r)| r), + session, + ); + rewrites.apply(&mut input, analyses, session)?; - Ok(input) + Ok(LinkerInput::Hir(input)) + } + } } } diff --git a/midenc-compile/src/stages/sema.rs b/midenc-compile/src/stages/sema.rs index a6bdf298d..f046eec38 100644 --- a/midenc-compile/src/stages/sema.rs +++ b/midenc-compile/src/stages/sema.rs @@ -1,3 +1,5 @@ +use midenc_hir::diagnostics::IntoDiagnostic; + use super::*; /// This stage of compilation takes the output of the parsing @@ -7,7 +9,7 @@ use super::*; pub struct SemanticAnalysisStage; impl Stage for SemanticAnalysisStage { type Input = ParseOutput; - type Output = Box; + type Output = LinkerInput; fn enabled(&self, session: &Session) -> bool { !session.parse_only() @@ -23,13 +25,14 @@ impl Stage for SemanticAnalysisStage { ParseOutput::Ast(ast) => { let mut convert_to_hir = ast::ConvertAstToHir; let module = Box::new(convert_to_hir.convert(ast, analyses, session)?); - session.emit(&module)?; - Ok(module) + session.emit(&module).into_diagnostic()?; + Ok(LinkerInput::Hir(module)) } ParseOutput::Hir(module) => { - session.emit(&module)?; - Ok(module) + session.emit(&module).into_diagnostic()?; + Ok(LinkerInput::Hir(module)) } + ParseOutput::Masm(masm) => Ok(LinkerInput::Masm(masm)), } } } diff --git a/midenc-driver/Cargo.toml b/midenc-driver/Cargo.toml index 3d759627f..5100af94e 100644 --- a/midenc-driver/Cargo.toml +++ b/midenc-driver/Cargo.toml @@ -14,10 +14,8 @@ readme.workspace = true edition.workspace = true [dependencies] -anyhow.workspace = true clap.workspace = true midenc-hir.workspace = true -miden-diagnostics.workspace = true midenc-session.workspace = true midenc-compile.workspace = true thiserror.workspace = true diff --git a/midenc-driver/src/lib.rs b/midenc-driver/src/lib.rs index 74dbcd867..1f08b08e1 100644 --- a/midenc-driver/src/lib.rs +++ b/midenc-driver/src/lib.rs @@ -1,38 +1,50 @@ mod midenc; +pub use clap::Error as ClapError; +pub use midenc_session::diagnostics; +use midenc_session::diagnostics::{miette, Diagnostic, Report}; + pub use self::midenc::Midenc; -/// A convenience alias for `Result` -pub type DriverResult = Result; - -/// This error type is produced by the `midenc` driver -#[derive(Debug, thiserror::Error)] -pub enum DriverError { - /// An error was raised due to invalid command-line arguments or argument validation - #[error(transparent)] - Clap(#[from] clap::Error), - /// Compilation failed - #[error(transparent)] - Compile(#[from] midenc_compile::CompilerError), - /// An error occurred when reading a file - #[error(transparent)] - Io(#[from] std::io::Error), - /// An unexpected error occurred - #[error(transparent)] - Failed(#[from] anyhow::Error), - /// An error was emitted as a diagnostic, so we don't need to emit info to stdout - #[error("exited due to error: see diagnostics for details")] - Reported, +/// A convenience alias for `Result` +pub type DriverResult = Result; + +#[derive(Debug, thiserror::Error, Diagnostic)] +#[error(transparent)] +#[diagnostic()] +pub struct ClapDiagnostic { + #[from] + err: ClapError, +} +impl ClapDiagnostic { + pub fn exit(self) -> ! { + self.err.exit() + } } /// Run the driver as if it was invoked from the command-line -pub fn run(cwd: P, args: A) -> Result<(), DriverError> +pub fn run(cwd: P, args: A) -> Result<(), Report> where P: Into, A: IntoIterator, { + setup_diagnostics(); + match Midenc::run(cwd, args) { - Err(DriverError::Compile(midenc_compile::CompilerError::Stopped)) => Ok(()), + Err(report) => match report.downcast::() { + Ok(_) => Ok(()), + Err(report) => Err(report), + }, result => result, } } + +fn setup_diagnostics() { + use diagnostics::ReportHandlerOpts; + + let result = + diagnostics::reporting::set_hook(Box::new(|_| Box::new(ReportHandlerOpts::new().build()))); + if result.is_ok() { + diagnostics::reporting::set_panic_hook(); + } +} diff --git a/midenc-driver/src/midenc.rs b/midenc-driver/src/midenc.rs index d6643890a..4cb32d151 100644 --- a/midenc-driver/src/midenc.rs +++ b/midenc-driver/src/midenc.rs @@ -1,12 +1,14 @@ use std::{ffi::OsString, path::PathBuf, sync::Arc}; use clap::{ColorChoice, Parser, Subcommand}; -use miden_diagnostics::Emitter; use midenc_compile as compile; use midenc_hir::FunctionIdent; -use midenc_session::{InputFile, TargetEnv, VerbosityFlag, Warnings}; +use midenc_session::{ + diagnostics::{Emitter, Report}, + InputFile, TargetEnv, Verbosity, Warnings, +}; -use super::DriverError; +use crate::ClapDiagnostic; /// This struct provides the command-line interface used by `midenc` #[derive(Debug, Parser)] @@ -46,11 +48,11 @@ enum Commands { short = 'v', value_name = "LEVEL", value_enum, - default_value_t = VerbosityFlag::Info, + default_value_t = Verbosity::Info, default_missing_value = "debug", help_heading = "Diagnostics", )] - verbosity: VerbosityFlag, + verbosity: Verbosity, /// Specify how warnings should be treated by the compiler. #[arg( long, @@ -107,11 +109,11 @@ enum Commands { short = 'v', value_name = "LEVEL", value_enum, - default_value_t = VerbosityFlag::Info, + default_value_t = Verbosity::Info, default_missing_value = "debug", help_heading = "Diagnostics", )] - verbosity: VerbosityFlag, + verbosity: Verbosity, /// Specify how warnings should be treated by the compiler. #[arg( long, @@ -148,7 +150,7 @@ enum Commands { } impl Midenc { - pub fn run(cwd: P, args: A) -> Result<(), DriverError> + pub fn run(cwd: P, args: A) -> Result<(), Report> where P: Into, A: IntoIterator, @@ -160,7 +162,7 @@ impl Midenc { cwd: P, args: A, emitter: Option>, - ) -> Result<(), DriverError> + ) -> Result<(), Report> where P: Into, A: IntoIterator, @@ -168,10 +170,11 @@ impl Midenc { let command = ::command(); let command = command.mut_subcommand("compile", compile::register_flags); - let mut matches = command.try_get_matches_from(args)?; + let mut matches = command.try_get_matches_from(args).map_err(ClapDiagnostic::from)?; let compile_matches = matches.subcommand_matches("compile").cloned().unwrap_or_default(); let cli = ::from_arg_matches_mut(&mut matches) - .map_err(format_error::)?; + .map_err(format_error::) + .map_err(ClapDiagnostic::from)?; cli.invoke(cwd.into(), emitter, compile_matches) } @@ -181,18 +184,14 @@ impl Midenc { cwd: PathBuf, emitter: Option>, matches: clap::ArgMatches, - ) -> Result<(), DriverError> { + ) -> Result<(), Report> { match self.command { Commands::Compile(mut config) => { if config.working_dir.is_none() { config.working_dir = Some(cwd); } let session = config.into_session(emitter).with_arg_matches(matches); - match compile::compile(Arc::new(session)) { - Ok(_) => Ok(()), - Err(compile::CompilerError::Reported) => Err(DriverError::Reported), - Err(err) => Err(DriverError::Compile(err)), - } + compile::compile(Arc::new(session)) } _ => unimplemented!(), } diff --git a/midenc-session/Cargo.toml b/midenc-session/Cargo.toml index 5161bb6da..8731c5778 100644 --- a/midenc-session/Cargo.toml +++ b/midenc-session/Cargo.toml @@ -17,7 +17,10 @@ edition.workspace = true atty = "0.2" clap.workspace = true inventory.workspace = true +miden-assembly.workspace = true +miden-core.workspace = true +miden-stdlib.workspace = true miden-diagnostics.workspace = true midenc-hir-symbol.workspace = true -rustc-hash.workspace = true +midenc-hir-macros.workspace = true thiserror.workspace = true diff --git a/midenc-session/build.rs b/midenc-session/build.rs new file mode 100644 index 000000000..2062cd65c --- /dev/null +++ b/midenc-session/build.rs @@ -0,0 +1,37 @@ +use std::{env, str}; + +fn main() { + println!("cargo::rerun-if-env-changed=MIDENC_BUILD_VERSION"); + println!("cargo::rerun-if-env-changed=MIDENC_BUILD_REV"); + println!("cargo::rerun-if-env-changed=CARGO_PKG_VERSION"); + println!("cargo::rerun-if-env-changed=PROFILE"); + + if let Some(sha) = git_describe() { + println!("cargo::rustc-env=MIDENC_BUILD_REV={sha}"); + } else { + println!("cargo::rustc-env=MIDENC_BUILD_REV=unknown"); + } + + if let Ok(version) = env::var("MIDENC_BUILD_VERSION") { + println!("cargo::rustc-env=MIDENC_BUILD_VERSION={}", &version); + return; + } + + let version = env::var("CARGO_PKG_VERSION").unwrap(); + let profile = env::var("PROFILE").unwrap(); + if profile == "debug" { + println!("cargo::rustc-env=MIDENC_BUILD_VERSION=nightly-{version}"); + } else { + println!("cargo::rustc-env=MIDENC_BUILD_VERSION={version}"); + } +} + +fn git_describe() -> Option { + use std::process::Command; + + Command::new("git") + .args(["describe", "--tags", "--always"]) + .output() + .ok() + .and_then(|out| str::from_utf8(&out.stdout[..]).map(str::trim).map(str::to_owned).ok()) +} diff --git a/midenc-session/src/diagnostics.rs b/midenc-session/src/diagnostics.rs new file mode 100644 index 000000000..a21b2841e --- /dev/null +++ b/midenc-session/src/diagnostics.rs @@ -0,0 +1,392 @@ +use std::{ + collections::BTreeMap, + fmt::{self, Display}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; + +pub use miden_assembly::diagnostics::{ + miette, + miette::MietteDiagnostic as AdHocDiagnostic, + reporting, + reporting::{PrintDiagnostic, ReportHandlerOpts}, + Diagnostic, IntoDiagnostic, Label, LabeledSpan, RelatedError, RelatedLabel, Report, Severity, + WrapErr, +}; +pub use miden_core::debuginfo::*; +pub use miden_diagnostics::{ + term::termcolor::{Buffer as EmitterBuffer, ColorChoice}, + CaptureEmitter, DefaultEmitter, Emitter, FatalError, NullEmitter, +}; +pub use midenc_hir_macros::Spanned; + +use crate::{Verbosity, Warnings}; + +#[derive(Default, Debug, Copy, Clone)] +pub struct DiagnosticsConfig { + pub verbosity: Verbosity, + pub warnings: Warnings, +} + +pub struct DiagnosticsHandler { + emitter: Arc, + source_manager: Arc, + err_count: AtomicUsize, + verbosity: Verbosity, + warnings: Warnings, + silent: bool, +} + +impl Default for DiagnosticsHandler { + fn default() -> Self { + let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto)); + let source_manager = Arc::new(DefaultSourceManager::default()); + Self::new(Default::default(), source_manager, emitter) + } +} + +// We can safely implement these traits for DiagnosticsHandler, +// as the only two non-atomic fields are read-only after creation +unsafe impl Send for DiagnosticsHandler {} +unsafe impl Sync for DiagnosticsHandler {} + +impl DiagnosticsHandler { + /// Create a new [DiagnosticsHandler] from the given [DiagnosticsConfig], + /// [CodeMap], and [Emitter] implementation. + pub fn new( + config: DiagnosticsConfig, + source_manager: Arc, + emitter: Arc, + ) -> Self { + let warnings = match config.warnings { + Warnings::Error => Warnings::Error, + _ if config.verbosity > Verbosity::Warning => Warnings::None, + warnings => warnings, + }; + Self { + emitter, + source_manager, + err_count: AtomicUsize::new(0), + verbosity: config.verbosity, + warnings, + silent: config.verbosity == Verbosity::Silent, + } + } + + #[inline] + pub fn source_manager(&self) -> Arc { + self.source_manager.clone() + } + + #[inline] + pub fn source_manager_ref(&self) -> &dyn SourceManager { + self.source_manager.as_ref() + } + + /// Returns true if the [DiagnosticsHandler] has emitted any error diagnostics + pub fn has_errors(&self) -> bool { + self.err_count.load(Ordering::Relaxed) > 0 + } + + /// Triggers a panic if the [DiagnosticsHandler] has emitted any error diagnostics + #[track_caller] + pub fn abort_if_errors(&self) { + if self.has_errors() { + FatalError.raise(); + } + } + + /// Emits an error message and produces a FatalError object + /// which can be used to terminate execution immediately + pub fn fatal(&self, err: impl ToString) -> FatalError { + self.error(err); + FatalError + } + + /// Emit a diagnostic [Report] + pub fn report(&self, report: impl Into) { + self.emit(report.into()) + } + + /// Report an error diagnostic + pub fn error(&self, error: impl ToString) { + self.emit(Report::msg(error.to_string())); + } + + /// Report a warning diagnostic + /// + /// If `warnings_as_errors` is set, it produces an error diagnostic instead. + pub fn warn(&self, warning: impl ToString) { + if matches!(self.warnings, Warnings::Error) { + return self.error(warning); + } + let diagnostic = AdHocDiagnostic::new(warning.to_string()).with_severity(Severity::Warning); + self.emit(diagnostic); + } + + /// Emits an informational diagnostic + pub fn info(&self, message: impl ToString) { + if self.verbosity > Verbosity::Info { + return; + } + let diagnostic = AdHocDiagnostic::new(message.to_string()).with_severity(Severity::Advice); + self.emit(diagnostic); + } + + /// Starts building an [InFlightDiagnostic] for rich compiler diagnostics. + /// + /// The caller is responsible for dropping/emitting the diagnostic using the + /// [InFlightDiagnostic] API. + pub fn diagnostic(&self, severity: Severity) -> InFlightDiagnosticBuilder<'_> { + InFlightDiagnosticBuilder::new(self, severity) + } + + /// Emits the given diagnostic + #[inline(always)] + pub fn emit(&self, diagnostic: impl Into) { + use std::io::Write; + + let diagnostic: Report = diagnostic.into(); + let diagnostic = match diagnostic.severity() { + Some(Severity::Advice) if self.verbosity > Verbosity::Info => return, + Some(Severity::Warning) => match self.warnings { + Warnings::None => return, + Warnings::All => diagnostic, + Warnings::Error => { + self.err_count.fetch_add(1, Ordering::Relaxed); + Report::from(WarningAsError::from(diagnostic)) + } + }, + Some(Severity::Error) => { + self.err_count.fetch_add(1, Ordering::Relaxed); + diagnostic + } + _ => diagnostic, + }; + + if self.silent { + return; + } + + let mut buffer = self.emitter.buffer(); + let printer = PrintDiagnostic::new(diagnostic); + write!(&mut buffer, "{printer}").expect("failed to write diagnostic to buffer"); + self.emitter.print(buffer).unwrap(); + } +} + +#[derive(thiserror::Error, Diagnostic, Debug)] +#[error("{}", .report)] +#[diagnostic( + severity(Error), + help("this warning was promoted to an error via --warnings-as-errors") +)] +struct WarningAsError { + #[diagnostic_source] + report: Report, +} +impl From for WarningAsError { + fn from(report: Report) -> Self { + Self { report } + } +} + +/// Constructs an in-flight diagnostic using the builder pattern +pub struct InFlightDiagnosticBuilder<'h> { + handler: &'h DiagnosticsHandler, + diagnostic: InFlightDiagnostic, + /// The source id of the primary diagnostic being constructed, if known + primary_source_id: Option, + /// The set of secondary labels which reference code in other source files than the primary + references: BTreeMap, +} +impl<'h> InFlightDiagnosticBuilder<'h> { + pub(crate) fn new(handler: &'h DiagnosticsHandler, severity: Severity) -> Self { + Self { + handler, + diagnostic: InFlightDiagnostic::new(severity), + primary_source_id: None, + references: BTreeMap::default(), + } + } + + /// Sets the primary diagnostic message to `message` + pub fn with_message(mut self, message: impl ToString) -> Self { + self.diagnostic.message = message.to_string(); + self + } + + /// Sets the error code for this diagnostic + pub fn with_code(mut self, code: impl ToString) -> Self { + self.diagnostic.code = Some(code.to_string()); + self + } + + /// Sets the error url for this diagnostic + pub fn with_url(mut self, url: impl ToString) -> Self { + self.diagnostic.url = Some(url.to_string()); + self + } + + /// Adds a primary label for `span` to this diagnostic, with no label message. + pub fn with_primary_span(mut self, span: SourceSpan) -> Self { + use miden_assembly::diagnostics::LabeledSpan; + + assert!(self.diagnostic.labels.is_empty(), "cannot set the primary span more than once"); + let source_id = span.source_id(); + let source_file = self.handler.source_manager.get(source_id).ok(); + self.primary_source_id = Some(source_id); + self.diagnostic.source_code = source_file; + self.diagnostic.labels.push(LabeledSpan::new_primary_with_span(None, span)); + self + } + + /// Adds a primary label for `span` to this diagnostic, with the given message + /// + /// A primary label is one which should be rendered as the relevant source code + /// at which a diagnostic originates. Secondary labels are used for related items + /// involved in the diagnostic. + pub fn with_primary_label(mut self, span: SourceSpan, message: impl ToString) -> Self { + use miden_assembly::diagnostics::LabeledSpan; + + assert!(self.diagnostic.labels.is_empty(), "cannot set the primary span more than once"); + let source_id = span.source_id(); + let source_file = self.handler.source_manager.get(source_id).ok(); + self.primary_source_id = Some(source_id); + self.diagnostic.source_code = source_file; + self.diagnostic + .labels + .push(LabeledSpan::new_primary_with_span(Some(message.to_string()), span)); + self + } + + /// Adds a secondary label for `span` to this diagnostic, with the given message + /// + /// A secondary label is used to point out related items in the source code which + /// are relevant to the diagnostic, but which are not themselves the point at which + /// the diagnostic originates. + pub fn with_secondary_label(mut self, span: SourceSpan, message: impl ToString) -> Self { + use miden_assembly::diagnostics::LabeledSpan; + + assert!( + !self.diagnostic.labels.is_empty(), + "must set a primary label before any secondary labels" + ); + let source_id = span.source_id(); + if source_id != self.primary_source_id.unwrap_or_default() { + let related = self.references.entry(source_id).or_insert_with(|| { + let source_file = self.handler.source_manager.get(source_id).ok(); + RelatedLabel::advice("see diagnostics for more information") + .with_source_file(source_file) + }); + related.labels.push(Label::new(span, message.to_string())); + } else { + self.diagnostic + .labels + .push(LabeledSpan::new_with_span(Some(message.to_string()), span)); + } + self + } + + /// Adds a note to the diagnostic + /// + /// Notes are used for explaining general concepts or suggestions + /// related to a diagnostic, and are not associated with any particular + /// source location. They are always rendered after the other diagnostic + /// content. + pub fn with_help(mut self, note: impl ToString) -> Self { + self.diagnostic.help = Some(note.to_string()); + self + } + + /// Consume this [InFlightDiagnostic] and create a [Report] + pub fn into_report(mut self) -> Report { + if self.diagnostic.message.is_empty() { + self.diagnostic.message = "reported".into(); + } + self.diagnostic.related.extend(self.references.into_values()); + Report::from(self.diagnostic) + } + + /// Emit the underlying [Diagnostic] via the [DiagnosticHandler] + pub fn emit(self) { + let handler = self.handler; + handler.emit(self.into_report()); + } +} + +#[derive(Default)] +struct InFlightDiagnostic { + source_code: Option>, + severity: Option, + message: String, + code: Option, + help: Option, + url: Option, + labels: Vec, + related: Vec, +} + +impl InFlightDiagnostic { + fn new(severity: Severity) -> Self { + Self { + severity: Some(severity), + ..Default::default() + } + } +} + +impl fmt::Display for InFlightDiagnostic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.message) + } +} + +impl fmt::Debug for InFlightDiagnostic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.message) + } +} + +impl std::error::Error for InFlightDiagnostic {} + +impl Diagnostic for InFlightDiagnostic { + fn code<'a>(&'a self) -> Option> { + self.code.as_ref().map(Box::new).map(|c| c as Box) + } + + fn severity(&self) -> Option { + self.severity + } + + fn help<'a>(&'a self) -> Option> { + self.help.as_ref().map(Box::new).map(|c| c as Box) + } + + fn url<'a>(&'a self) -> Option> { + self.url.as_ref().map(Box::new).map(|c| c as Box) + } + + fn labels(&self) -> Option + '_>> { + if self.labels.is_empty() { + return None; + } + let iter = self.labels.iter().cloned(); + Some(Box::new(iter) as Box>) + } + + fn related(&self) -> Option + '_>> { + if self.related.is_empty() { + return None; + } + + let iter = self.related.iter().map(|r| r as &dyn Diagnostic); + Some(Box::new(iter) as Box>) + } + + fn diagnostic_source(&self) -> Option<&(dyn Diagnostic + '_)> { + None + } +} diff --git a/midenc-session/src/duration.rs b/midenc-session/src/duration.rs index 88681479b..34aae7d9a 100644 --- a/midenc-session/src/duration.rs +++ b/midenc-session/src/duration.rs @@ -20,6 +20,11 @@ impl From for HumanDuration { Self(d) } } +impl fmt::Debug for HumanDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} impl fmt::Display for HumanDuration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let t = self.0.as_secs(); diff --git a/midenc-session/src/flags.rs b/midenc-session/src/flags.rs index 8d36c4990..44f61c0bf 100644 --- a/midenc-session/src/flags.rs +++ b/midenc-session/src/flags.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct CompileFlag { pub name: &'static str, pub short: Option, @@ -8,6 +9,7 @@ pub struct CompileFlag { pub action: FlagAction, pub default_missing_value: Option<&'static str>, pub default_value: Option<&'static str>, + pub hide: Option, } impl CompileFlag { pub const fn new(name: &'static str) -> Self { @@ -21,6 +23,7 @@ impl CompileFlag { action: FlagAction::Set, default_missing_value: None, default_value: None, + hide: None, } } @@ -63,6 +66,11 @@ impl CompileFlag { self.default_missing_value = Some(value); self } + + pub const fn hide(mut self, yes: bool) -> Self { + self.hide = Some(yes); + self + } } #[derive(Debug, Copy, Clone)] diff --git a/midenc-session/src/inputs.rs b/midenc-session/src/inputs.rs index 67ea945cd..3a35c1893 100644 --- a/midenc-session/src/inputs.rs +++ b/midenc-session/src/inputs.rs @@ -33,6 +33,24 @@ pub struct InputFile { file_type: FileType, } impl InputFile { + pub fn new(ty: FileType, file: InputType) -> Self { + Self { + file, + file_type: ty, + } + } + + /// Returns an [InputFile] representing an empty WebAssembly module binary + pub fn empty() -> Self { + Self { + file: InputType::Stdin { + name: FileName::Virtual("empty.wasm".into()), + input: vec![], + }, + file_type: FileType::Wasm, + } + } + /// Get an [InputFile] representing the contents of `path`. /// /// This function returns an error if the contents are not a valid supported file type. @@ -72,6 +90,13 @@ impl InputFile { self.file_type } + pub fn file_name(&self) -> FileName { + match &self.file { + InputType::Real(ref path) => FileName::Real(path.clone()), + InputType::Stdin { name, .. } => name.clone(), + } + } + pub fn as_path(&self) -> Option<&Path> { match &self.file { InputType::Real(ref path) => Some(path), diff --git a/midenc-session/src/lib.rs b/midenc-session/src/lib.rs index 021f4605e..bc4e44b48 100644 --- a/midenc-session/src/lib.rs +++ b/midenc-session/src/lib.rs @@ -1,7 +1,12 @@ +#![feature(debug_closure_helpers)] +extern crate alloc; + +pub mod diagnostics; mod duration; mod emit; mod flags; mod inputs; +mod libs; mod options; mod outputs; mod statistics; @@ -12,15 +17,22 @@ use std::{ sync::Arc, }; +/// The version associated with the current compiler toolchain +pub const MIDENC_BUILD_VERSION: &'static str = env!("MIDENC_BUILD_VERSION"); + +/// The git revision associated with the current compiler toolchain +pub const MIDENC_BUILD_REV: &'static str = env!("MIDENC_BUILD_REV"); + use clap::ValueEnum; -use miden_diagnostics::{CodeMap, DiagnosticsHandler, Emitter}; use midenc_hir_symbol::Symbol; pub use self::{ + diagnostics::{DiagnosticsHandler, Emitter, SourceManager}, duration::HumanDuration, emit::Emit, flags::{CompileFlag, FlagAction}, inputs::{FileType, InputFile, InputType, InvalidInputError}, + libs::{LibraryKind, LinkLibrary}, options::*, outputs::{OutputFile, OutputFiles, OutputType, OutputTypeSpec, OutputTypes}, statistics::Statistics, @@ -52,48 +64,45 @@ impl ProjectType { /// This struct provides access to all of the metadata and configuration /// needed during a single compilation session. pub struct Session { - /// The type of project we're compiling this session - pub project_type: ProjectType, - /// The current target environment for this session - pub target: TargetEnv, /// Configuration for the current compiler session pub options: Options, - /// The current source map - pub codemap: Arc, + /// The current source manager + pub source_manager: Arc, /// The current diagnostics handler pub diagnostics: Arc, - /// The location of all libraries shipped with the compiler - pub sysroot: PathBuf, - /// The input being compiled - pub input: InputFile, + /// The inputs being compiled + pub inputs: Vec, /// The outputs to be produced by the compiler during compilation pub output_files: OutputFiles, /// Statistics gathered from the current compiler session pub statistics: Statistics, - /// We store any leftover argument matches in the session for use - /// by any downstream crates that register custom flags - arg_matches: clap::ArgMatches, } + +impl fmt::Debug for Session { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inputs = self.inputs.iter().map(|input| input.file_name()).collect::>(); + f.debug_struct("Session") + .field("options", &self.options) + .field("inputs", &inputs) + .field("output_files", &self.output_files) + .field("statistics", &self.statistics) + .finish_non_exhaustive() + } +} + impl Session { pub fn new( - target: TargetEnv, input: InputFile, output_dir: Option, output_file: Option, tmp_dir: Option, options: Options, emitter: Option>, + source_manager: Arc, ) -> Self { - // TODO: Make sure we pin this down when we need to ship stuff with compiler - let sysroot = match &options.sysroot { - Some(sysroot) => sysroot.clone(), - None => std::env::current_dir().unwrap(), - }; - let codemap = Arc::new(CodeMap::new()); - let diagnostics = Arc::new(DiagnosticsHandler::new( options.diagnostics.clone(), - codemap.clone(), + source_manager.clone(), emitter.unwrap_or_else(|| options.default_emitter()), )); @@ -113,61 +122,61 @@ impl Session { ), }; - let project_type = ProjectType::default_for_target(target); Self { - project_type, - target, options, - codemap, + source_manager, diagnostics, - sysroot, - input, + inputs: vec![input], output_files, statistics: Default::default(), - arg_matches: Default::default(), } } pub fn with_project_type(mut self, ty: ProjectType) -> Self { - self.project_type = ty; + self.options.project_type = ty; self } #[doc(hidden)] pub fn with_arg_matches(mut self, matches: clap::ArgMatches) -> Self { - self.arg_matches = matches; + self.options.set_arg_matches(matches); self } /// Get the value of a custom flag with action `FlagAction::SetTrue` or `FlagAction::SetFalse` + #[inline] pub fn get_flag(&self, name: &str) -> bool { - self.arg_matches.get_flag(name) + self.options.get_flag(name) } /// Get the count of a specific custom flag with action `FlagAction::Count` + #[inline] pub fn get_flag_count(&self, name: &str) -> usize { - self.arg_matches.get_count(name) as usize + self.options.get_flag_count(name) } /// Get the value of a specific custom flag + #[inline] pub fn get_flag_value(&self, name: &str) -> Option<&T> where T: core::any::Any + Clone + Send + Sync + 'static, { - self.arg_matches.get_one(name) + self.options.get_flag_value(name) } /// Iterate over values of a specific custom flag + #[inline] pub fn get_flag_values(&self, name: &str) -> Option> where T: core::any::Any + Clone + Send + Sync + 'static, { - self.arg_matches.get_many(name) + self.options.get_flag_values(name) } /// Get the remaining [clap::ArgMatches] left after parsing the base session configuration + #[inline] pub fn matches(&self) -> &clap::ArgMatches { - &self.arg_matches + self.options.matches() } /// The name of this session (used as the name of the project, output file, etc.) @@ -176,8 +185,8 @@ impl Session { .name .clone() .or_else(|| { - if self.input.is_real() { - Some(self.input.filestem().to_string()) + if self.inputs[0].is_real() { + Some(self.inputs[0].filestem().to_string()) } else { None } @@ -187,13 +196,14 @@ impl Session { }) } - pub fn out_filename(&self, outputs: &OutputFiles, progname: Symbol) -> OutputFile { - let default_filename = self.filename_for_input(outputs, progname); - let out_filename = outputs + pub fn out_filename(&self, progname: Symbol) -> OutputFile { + let default_filename = self.filename_for_input(progname); + let out_filename = self + .output_files .outputs .get(&OutputType::Mast) .and_then(|s| s.to_owned()) - .or_else(|| outputs.out_file.clone()) + .or_else(|| self.output_files.out_file.clone()) .unwrap_or(default_filename); if let OutputFile::Real(ref path) = out_filename { @@ -203,10 +213,11 @@ impl Session { out_filename } - pub fn filename_for_input(&self, outputs: &OutputFiles, progname: Symbol) -> OutputFile { - match self.project_type { + pub fn filename_for_input(&self, progname: Symbol) -> OutputFile { + match self.options.project_type { ProjectType::Program => { - let out_filename = outputs.path(OutputType::Mast); + let out_filename = + self.output_files.path(Some(progname.as_str()), OutputType::Mast); if let OutputFile::Real(ref path) = out_filename { OutputFile::Real(path.with_extension(OutputType::Mast.extension())) } else { @@ -214,7 +225,9 @@ impl Session { } } ProjectType::Library => OutputFile::Real( - outputs.out_dir.join(format!("{progname}.{}", OutputType::Mast.extension())), + self.output_files + .out_dir + .join(format!("{progname}.{}", OutputType::Mast.extension())), ), } } @@ -248,10 +261,8 @@ impl Session { /// Get the path to emit the given [OutputType] to pub fn emit_to(&self, ty: OutputType, name: Option) -> Option { if self.should_emit(ty) { - match self.output_files.path(ty) { - OutputFile::Real(path) => name - .map(|name| path.with_file_name(name.as_str()).with_extension(ty.extension())) - .or(Some(path)), + match self.output_files.path(name.map(|n| n.as_str()), ty) { + OutputFile::Real(path) => Some(path), OutputFile::Stdout => None, } } else { @@ -263,16 +274,10 @@ impl Session { pub fn emit(&self, item: &E) -> std::io::Result<()> { let output_type = item.output_type(); if self.should_emit(output_type) { - match self.output_files.path(output_type) { + let name = item.name().map(|n| n.as_str()); + match self.output_files.path(name, output_type) { OutputFile::Real(path) => { - let file_path = if path.is_dir() { - let item_name = - item.name().map(|s| s.to_string()).unwrap_or("noname".to_string()); - path.join(item_name.as_str()).with_extension(output_type.extension()) - } else { - path - }; - item.write_to_file(&file_path)?; + item.write_to_file(&path)?; } OutputFile::Stdout => { item.write_to_stdout()?; diff --git a/midenc-session/src/libs.rs b/midenc-session/src/libs.rs new file mode 100644 index 000000000..c6f7f507a --- /dev/null +++ b/midenc-session/src/libs.rs @@ -0,0 +1,269 @@ +use std::{ + borrow::Cow, + ffi::OsStr, + path::{Path, PathBuf}, + str::FromStr, +}; + +use miden_assembly::{library::CompiledLibrary, LibraryNamespace}; +use miden_stdlib::StdLibrary; + +use crate::{ + diagnostics::{IntoDiagnostic, Report, WrapErr}, + Session, +}; + +/// The types of libraries that can be linked against during compilation +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum LibraryKind { + /// A compiled MAST library + #[default] + Mast, + /// A source-form MASM library, using the standard project layout + Masm, +} + +impl FromStr for LibraryKind { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "mast" => Ok(Self::Mast), + "masm" => Ok(Self::Masm), + _ => Err(()), + } + } +} + +/// A library requested by the user to be linked against during compilation +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LinkLibrary { + /// The name of the library. + /// + /// If requested by name, e.g. `-l std`, the name is used as given. + /// + /// If requested by path, e.g. `-l ./target/libs/miden-base.masl`, then the name of the library + /// will be the basename of the file specified in the path. + pub name: Cow<'static, str>, + /// If specified, the path from which this library should be loaded + pub path: Option, + /// The kind of library to load. + /// + /// By default this is assumed to be a `.masl` library, but the kind will be detected based on + /// how it is requested by the user. It may also be specified explicitly by the user. + pub kind: LibraryKind, +} +impl LinkLibrary { + pub fn load(&self, session: &Session) -> Result { + if let Some(path) = self.path.as_deref() { + return self.load_from_path(path, session); + } + + // Handle libraries shipped with the compiler, or via Miden crates + if self.name == "std" { + return Ok(StdLibrary::default().into()); + } + + // Search for library among specified search paths + let path = self.find(session)?; + + self.load_from_path(&path, session) + } + + fn load_from_path(&self, path: &Path, session: &Session) -> Result { + match self.kind { + LibraryKind::Masm => { + let ns = LibraryNamespace::new(&self.name) + .into_diagnostic() + .wrap_err_with(|| format!("invalid library namespace '{}'", &self.name))?; + CompiledLibrary::from_dir(path, ns, session.source_manager.clone()) + } + LibraryKind::Mast => CompiledLibrary::deserialize_from_file(&path).map_err(|err| { + Report::msg(format!( + "failed to deserialize library from '{}': {err}", + path.display() + )) + }), + } + } + + fn find(&self, session: &Session) -> Result { + use std::fs; + + for search_path in session.options.search_paths.iter() { + let reader = fs::read_dir(search_path).map_err(|err| { + Report::msg(format!( + "invalid library search path '{}': {err}", + search_path.display() + )) + })?; + for entry in reader { + let Ok(entry) = entry else { + continue; + }; + let path = PathBuf::from(entry.path()); + let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else { + continue; + }; + if stem != self.name.as_ref() { + continue; + } + + match self.kind { + LibraryKind::Mast => { + if !path.is_file() { + return Err(Report::msg(format!( + "unable to load MAST library from '{}': not a file", + path.display() + ))); + } + } + LibraryKind::Masm => { + if !path.is_dir() { + return Err(Report::msg(format!( + "unable to load Miden Assembly library from '{}': not a directory", + path.display() + ))); + } + } + } + return Ok(path); + } + } + + Err(Report::msg(format!( + "unable to locate library '{}' using any of the provided search paths", + &self.name + ))) + } +} + +impl clap::builder::ValueParserFactory for LinkLibrary { + type Parser = LinkLibraryParser; + + fn value_parser() -> Self::Parser { + LinkLibraryParser + } +} + +#[doc(hidden)] +#[derive(Clone)] +pub struct LinkLibraryParser; +impl clap::builder::TypedValueParser for LinkLibraryParser { + type Value = LinkLibrary; + + fn possible_values( + &self, + ) -> Option + '_>> { + use clap::builder::PossibleValue; + + Some(Box::new( + [ + PossibleValue::new("masm").help("A Miden Assembly project directory"), + PossibleValue::new("mast").help("A compiled MAST library file"), + ] + .into_iter(), + )) + } + + /// Parses the `-l` flag using the following format: + /// + /// `-l[KIND=]NAME` + /// + /// * `KIND` is one of: `masl`, `masm`; defaults to `masl` + /// * `NAME` is either an absolute path, or a name (without extension) + fn parse_ref( + &self, + _cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &OsStr, + ) -> Result { + use clap::error::{Error, ErrorKind}; + + let value = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?; + let (kind, name) = value + .split_once('=') + .map(|(kind, name)| (Some(kind), name)) + .unwrap_or((None, value)); + + if name.is_empty() { + return Err(Error::raw( + ErrorKind::ValueValidation, + "invalid link library: must specify a name or path", + )); + } + + let maybe_path = Path::new(name); + let extension = maybe_path.extension().map(|ext| ext.to_str().unwrap()); + let kind = match kind { + Some(kind) if !kind.is_empty() => kind.parse::().map_err(|_| { + Error::raw(ErrorKind::InvalidValue, format!("'{kind}' is not a valid library kind")) + })?, + Some(_) | None => match extension { + Some(kind) => kind.parse::().map_err(|_| { + Error::raw( + ErrorKind::InvalidValue, + format!("'{kind}' is not a valid library kind"), + ) + })?, + None => LibraryKind::default(), + }, + }; + + if maybe_path.is_absolute() { + let meta = maybe_path.metadata().map_err(|err| { + Error::raw( + ErrorKind::ValueValidation, + format!( + "invalid link library: unable to load '{}': {err}", + maybe_path.display() + ), + ) + })?; + + match kind { + LibraryKind::Mast if !meta.is_file() => { + return Err(Error::raw( + ErrorKind::ValueValidation, + format!("invalid link library: '{}' is not a file", maybe_path.display()), + )); + } + LibraryKind::Masm if !meta.is_dir() => { + return Err(Error::raw( + ErrorKind::ValueValidation, + format!( + "invalid link library: kind 'masm' was specified, but '{}' is not a \ + directory", + maybe_path.display() + ), + )); + } + _ => (), + } + + let name = maybe_path.file_stem().unwrap().to_str().unwrap().to_string(); + + Ok(LinkLibrary { + name: name.into(), + path: Some(maybe_path.to_path_buf()), + kind, + }) + } else if extension.is_some() { + let name = name.strip_suffix(unsafe { extension.unwrap_unchecked() }).unwrap(); + let mut name = name.to_string(); + name.pop(); + + Ok(LinkLibrary { + name: name.into(), + path: None, + kind, + }) + } else { + Ok(LinkLibrary { + name: name.to_string().into(), + path: None, + kind, + }) + } + } +} diff --git a/midenc-session/src/options/mod.rs b/midenc-session/src/options/mod.rs index 52acdab2b..727588c00 100644 --- a/midenc-session/src/options/mod.rs +++ b/midenc-session/src/options/mod.rs @@ -1,21 +1,37 @@ -use std::{fmt, path::PathBuf, str::FromStr, sync::Arc}; +use std::{ + fmt, + path::{Path, PathBuf}, + str::FromStr, + sync::Arc, +}; use clap::ValueEnum; -use miden_diagnostics::{term::termcolor::ColorChoice, DiagnosticsConfig, Emitter, Verbosity}; -use crate::OutputTypes; +use crate::{ + diagnostics::{ColorChoice, DiagnosticsConfig, Emitter}, + LinkLibrary, OutputTypes, ProjectType, TargetEnv, +}; /// This struct contains all of the configuration options for the compiler -#[derive(Debug)] pub struct Options { /// The name of the program being compiled pub name: Option, + /// The type of project we're compiling this session + pub project_type: ProjectType, + /// The name of the function to call as the entrypoint + pub entrypoint: Option, + /// The current target environment for this session + pub target: TargetEnv, /// The optimization level for the current program pub optimize: OptLevel, + /// The level of debugging info for the current program + pub debug: DebugInfo, /// The type of outputs to emit pub output_types: OutputTypes, /// The paths in which to search for Miden Assembly libraries to link against pub search_paths: Vec, + /// The set of Miden libraries to link against + pub link_libraries: Vec, /// The location of the libraries which are shipped with the compiler pub sysroot: Option, /// Whether, and how, to color terminal output @@ -28,71 +44,211 @@ pub struct Options { pub print_ir_after_all: bool, /// Print IR to stdout each time the named pass is applied pub print_ir_after_pass: Option, + /// We store any leftover argument matches in the session options for use + /// by any downstream crates that register custom flags + arg_matches: clap::ArgMatches, } + +impl fmt::Debug for Options { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Options") + .field("name", &self.name) + .field("project_type", &self.project_type) + .field("entrypoint", &self.entrypoint) + .field("target", &self.target) + .field("optimize", &self.optimize) + .field("debug", &self.debug) + .field("output_types", &self.output_types) + .field("search_paths", &self.search_paths) + .field("link_libraries", &self.link_libraries) + .field("sysroot", &self.sysroot) + .field("color", &self.color) + .field("diagnostics", &self.diagnostics) + .field("current_dir", &self.current_dir) + .field("print_ir_after_all", &self.print_ir_after_all) + .field("print_ir_after_pass", &self.print_ir_after_pass) + .field_with("extra_arguments", |f| { + let mut map = f.debug_map(); + for id in self.arg_matches.ids() { + use clap::parser::ValueSource; + // Don't print CompilerOptions arg group + if id.as_str() == "CompilerOptions" { + continue; + } + // Don't print default values + if matches!( + self.arg_matches.value_source(id.as_str()), + Some(ValueSource::DefaultValue) + ) { + continue; + } + map.key(&id.as_str()).value_with(|f| { + let mut list = f.debug_list(); + if let Some(occurs) = self + .arg_matches + .try_get_raw_occurrences(id.as_str()) + .expect("expected flag") + { + list.entries(occurs.flat_map(|o| o)); + } + list.finish() + }); + } + map.finish() + }) + .finish() + } +} + impl Default for Options { fn default() -> Self { let current_dir = std::env::current_dir().expect("could not get working directory"); - Self::new(current_dir) + let target = TargetEnv::default(); + let project_type = ProjectType::default_for_target(target); + Self::new(target, project_type, current_dir, None) } } + impl Options { - pub fn new(current_dir: PathBuf) -> Self { + pub fn new( + target: TargetEnv, + project_type: ProjectType, + current_dir: PathBuf, + sysroot: Option, + ) -> Self { + let sysroot = sysroot.or_else(|| { + std::env::var("HOME").ok().map(|home| { + Path::new(&home) + .join(".miden") + .join("toolchains") + .join(crate::MIDENC_BUILD_VERSION) + }) + }); + Self { name: None, + target, + project_type, + entrypoint: None, optimize: OptLevel::None, + debug: DebugInfo::None, output_types: Default::default(), search_paths: vec![], - sysroot: None, + link_libraries: vec![], + sysroot, color: Default::default(), diagnostics: Default::default(), current_dir, print_ir_after_all: false, print_ir_after_pass: None, + arg_matches: Default::default(), } } + #[inline(always)] pub fn with_color(mut self, color: ColorChoice) -> Self { self.color = color; self } + #[inline(always)] pub fn with_verbosity(mut self, verbosity: Verbosity) -> Self { self.diagnostics.verbosity = verbosity; self } + #[inline(always)] + pub fn with_debug_info(mut self, debug: DebugInfo) -> Self { + self.debug = debug; + self + } + + #[inline(always)] + pub fn with_optimization(mut self, level: OptLevel) -> Self { + self.optimize = level; + self + } + pub fn with_warnings(mut self, warnings: Warnings) -> Self { - match warnings { - Warnings::None => { - self.diagnostics.warnings_as_errors = false; - self.diagnostics.no_warn = true; - } - Warnings::All => { - self.diagnostics.warnings_as_errors = false; - self.diagnostics.no_warn = false; - } - Warnings::Error => { - self.diagnostics.warnings_as_errors = true; - self.diagnostics.no_warn = false; - } - } + self.diagnostics.warnings = warnings; self } + #[inline(always)] pub fn with_output_types(mut self, output_types: OutputTypes) -> Self { self.output_types = output_types; self } - /// Get a new [miden_diagnostics::Emitter] based on the current options. + #[doc(hidden)] + pub fn with_arg_matches(mut self, matches: clap::ArgMatches) -> Self { + self.arg_matches = matches; + self + } + + #[doc(hidden)] + pub fn set_arg_matches(&mut self, matches: clap::ArgMatches) { + self.arg_matches = matches; + } + + /// Get a new [Emitter] based on the current options. pub fn default_emitter(&self) -> Arc { - use miden_diagnostics::{DefaultEmitter, NullEmitter}; + use crate::diagnostics::{DefaultEmitter, NullEmitter}; match self.diagnostics.verbosity { Verbosity::Silent => Arc::new(NullEmitter::new(self.color)), _ => Arc::new(DefaultEmitter::new(self.color)), } } + + /// Returns true if source location information should be emitted by the compiler + #[inline(always)] + pub fn emit_source_locations(&self) -> bool { + matches!(self.debug, DebugInfo::Line | DebugInfo::Full) + } + + /// Returns true if rich debugging information should be emitted by the compiler + #[inline(always)] + pub fn emit_debug_decorators(&self) -> bool { + matches!(self.debug, DebugInfo::Full) + } + + /// Returns true if debug assertions are enabled + #[inline(always)] + pub fn emit_debug_assertions(&self) -> bool { + self.debug != DebugInfo::None && matches!(self.optimize, OptLevel::None | OptLevel::Basic) + } + + /// Get the value of a custom flag with action `FlagAction::SetTrue` or `FlagAction::SetFalse` + pub fn get_flag(&self, name: &str) -> bool { + self.arg_matches.get_flag(name) + } + + /// Get the count of a specific custom flag with action `FlagAction::Count` + pub fn get_flag_count(&self, name: &str) -> usize { + self.arg_matches.get_count(name) as usize + } + + /// Get the value of a specific custom flag + pub fn get_flag_value(&self, name: &str) -> Option<&T> + where + T: core::any::Any + Clone + Send + Sync + 'static, + { + self.arg_matches.get_one(name) + } + + /// Iterate over values of a specific custom flag + pub fn get_flag_values(&self, name: &str) -> Option> + where + T: core::any::Any + Clone + Send + Sync + 'static, + { + self.arg_matches.get_many(name) + } + + /// Get the remaining [clap::ArgMatches] left after parsing the base session configuration + pub fn matches(&self) -> &clap::ArgMatches { + &self.arg_matches + } } /// This enum describes the degree to which compiled programs will be optimized @@ -113,8 +269,20 @@ pub enum OptLevel { SizeMin, } +/// This enum describes what type of debugging information to emit in compiled programs +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)] +pub enum DebugInfo { + /// Do not emit debug info in the final output + None, + /// Emit source location information in the final output + #[default] + Line, + /// Emit all available debug information in the final output + Full, +} + /// This enum represents the behavior of the compiler with regard to warnings -#[derive(Debug, Copy, Clone, Default, ValueEnum)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)] pub enum Warnings { /// Disable all warnings None, @@ -147,8 +315,8 @@ impl FromStr for Warnings { } /// This enum represents the type of messages produced by the compiler during execution -#[derive(Debug, Copy, Clone, Default, ValueEnum)] -pub enum VerbosityFlag { +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum Verbosity { /// Emit additional debug/trace information during compilation Debug, /// Emit the standard informational, warning, and error messages @@ -161,25 +329,3 @@ pub enum VerbosityFlag { /// Do not emit anything to stdout/stderr Silent, } -impl From for VerbosityFlag { - fn from(v: Verbosity) -> Self { - match v { - Verbosity::Debug => Self::Debug, - Verbosity::Info => Self::Info, - Verbosity::Warning => Self::Warning, - Verbosity::Error => Self::Error, - Verbosity::Silent => Self::Silent, - } - } -} -impl From for Verbosity { - fn from(flag: VerbosityFlag) -> Self { - match flag { - VerbosityFlag::Debug => Self::Debug, - VerbosityFlag::Info => Self::Info, - VerbosityFlag::Warning => Self::Warning, - VerbosityFlag::Error => Self::Error, - VerbosityFlag::Silent => Self::Silent, - } - } -} diff --git a/midenc-session/src/outputs.rs b/midenc-session/src/outputs.rs index 5e6b62691..a34fc63a9 100644 --- a/midenc-session/src/outputs.rs +++ b/midenc-session/src/outputs.rs @@ -15,9 +15,9 @@ pub enum OutputType { Ast, /// The compiler will emit Miden IR Hir, - /// The compiler will emit Miden Assembly + /// The compiler will emit Miden Assembly text Masm, - /// The compiler will emit binary MAST (Miden Abstract Syntax Tree) + /// The compiler will emit the Merkalized Abstract Syntax Tree #[default] Mast, } @@ -135,12 +135,19 @@ impl OutputFiles { } } - pub fn path(&self, ty: OutputType) -> OutputFile { - self.outputs + pub fn path(&self, name: Option<&str>, ty: OutputType) -> OutputFile { + let mut output = self + .outputs .get(&ty) .and_then(|p| p.to_owned()) .or_else(|| self.out_file.clone()) - .unwrap_or_else(|| OutputFile::Real(self.output_path(ty))) + .unwrap_or_else(|| OutputFile::Real(self.output_path(ty))); + if let OutputFile::Real(ref mut path) = output { + if let Some(name) = name { + path.set_file_name(name); + } + } + output } pub fn output_path(&self, ty: OutputType) -> PathBuf { @@ -272,6 +279,21 @@ pub struct OutputTypeParser; impl clap::builder::TypedValueParser for OutputTypeParser { type Value = OutputTypeSpec; + fn possible_values( + &self, + ) -> Option + '_>> { + use clap::builder::PossibleValue; + Some(Box::new( + [ + PossibleValue::new("ast").help("Abstract Syntax Tree (text)"), + PossibleValue::new("hir").help("High-level Intermediate Representation (text)"), + PossibleValue::new("masm").help("Miden Assembly (text)"), + PossibleValue::new("mast").help("Merkelized Abstract Syntax Tree (binary)"), + ] + .into_iter(), + )) + } + fn parse_ref( &self, _cmd: &clap::Command, diff --git a/midenc-session/src/statistics.rs b/midenc-session/src/statistics.rs index 32c94be81..6528f22a5 100644 --- a/midenc-session/src/statistics.rs +++ b/midenc-session/src/statistics.rs @@ -1,4 +1,5 @@ use std::{ + fmt, sync::atomic::{AtomicU64, Ordering}, time::{Duration, Instant}, }; @@ -23,6 +24,16 @@ pub struct Statistics { /// The elapsed time at which codegen started codegen_time: AtomicU64, } +impl fmt::Debug for Statistics { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Statistics") + .field("elapsed", &self.elapsed()) + .field("parsing", &self.parse_time()) + .field("optimization", &self.opt_time()) + .field("codegen", &self.codegen_time()) + .finish() + } +} impl Default for Statistics { fn default() -> Statistics { Self::new(Instant::now()) diff --git a/midenc/Cargo.toml b/midenc/Cargo.toml index 253a91820..34bae0356 100644 --- a/midenc/Cargo.toml +++ b/midenc/Cargo.toml @@ -14,7 +14,6 @@ readme.workspace = true edition.workspace = true [dependencies] -anyhow.workspace = true env_logger.workspace = true human-panic = "2.0" midenc-driver.workspace = true diff --git a/midenc/src/main.rs b/midenc/src/main.rs index b1bd3bcf9..94ea63c14 100644 --- a/midenc/src/main.rs +++ b/midenc/src/main.rs @@ -1,9 +1,12 @@ use std::env; -use anyhow::anyhow; -use midenc_driver::{self as driver, DriverError}; +use midenc_driver::{ + self as driver, + diagnostics::{IntoDiagnostic, Report, WrapErr}, + ClapDiagnostic, +}; -pub fn main() -> Result<(), DriverError> { +pub fn main() -> Result<(), Report> { if cfg!(not(debug_assertions)) && env::var_os("MIDENC_TRACE").is_none() { human_panic::setup_panic!(); } @@ -18,11 +21,11 @@ pub fn main() -> Result<(), DriverError> { "us" => builder.format_timestamp_micros(), "ns" => builder.format_timestamp_nanos(), other => { - return Err(DriverError::Failed(anyhow!( + return Err(Report::msg(format!( "invalid MIDENC_TRACE_TIMING precision, expected one of [s, ms, us, ns], got \ '{}'", other - ))) + ))); } }; } else { @@ -31,10 +34,20 @@ pub fn main() -> Result<(), DriverError> { builder.init(); // Get current working directory - let cwd = env::current_dir()?; + let cwd = env::current_dir() + .into_diagnostic() + .wrap_err("could not read current working directory")?; match driver::run(cwd, env::args_os()) { - Err(DriverError::Clap(err)) => err.exit(), + Err(report) => match report.downcast::() { + Ok(err) => { + // Remove the miette panic hook, so that clap errors can be reported without + // the diagnostic-style formatting + //drop(std::panic::take_hook()); + err.exit() + } + Err(report) => Err(report), + }, result => result, } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7b699598c..cdf507ca5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-03-10" -components = ["rustfmt", "rust-src"] -targets = ["wasm32-unknown-unknown"] +channel = "nightly-2024-05-07" +components = ["rustfmt", "rust-src", "clippy"] +targets = ["wasm32-unknown-unknown", "wasm32-wasi"] profile = "minimal" diff --git a/sdk/stdlib-sys/src/stdlib/crypto/hashes.rs b/sdk/stdlib-sys/src/stdlib/crypto/hashes.rs index 67a376542..3f257335e 100644 --- a/sdk/stdlib-sys/src/stdlib/crypto/hashes.rs +++ b/sdk/stdlib-sys/src/stdlib/crypto/hashes.rs @@ -1,26 +1,25 @@ //! Contains procedures for computing hashes using BLAKE3 and SHA256 hash //! functions. The input and output elements are assumed to contain one 32-bit //! value per element. -use crate::Felt; -#[link(wasm_import_module = "std::crypto_hashes")] +#[link(wasm_import_module = "std::crypto::hashes::blake3")] extern "C" { /// Computes BLAKE3 1-to-1 hash. /// /// Input: 32-bytes stored in the first 8 elements of the stack (32 bits per element). /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element). /// The output is passed back to the caller via a pointer. - #[link_name = "blake3_hash_1to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] + #[link_name = "hash_1to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] fn extern_blake3_hash_1to1( - e1: Felt, - e2: Felt, - e3: Felt, - e4: Felt, - e5: Felt, - e6: Felt, - e7: Felt, - e8: Felt, - ptr: *mut Felt, + e1: u32, + e2: u32, + e3: u32, + e4: u32, + e5: u32, + e6: u32, + e7: u32, + e8: u32, + ptr: *mut u8, ); /// Computes BLAKE3 2-to-1 hash. @@ -28,27 +27,30 @@ extern "C" { /// Input: 64-bytes stored in the first 16 elements of the stack (32 bits per element). /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element) /// The output is passed back to the caller via a pointer. - #[link_name = "blake3_hash_2to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] + #[link_name = "hash_2to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] fn extern_blake3_hash_2to1( - e1: Felt, - e2: Felt, - e3: Felt, - e4: Felt, - e5: Felt, - e6: Felt, - e7: Felt, - e8: Felt, - e9: Felt, - e10: Felt, - e11: Felt, - e12: Felt, - e13: Felt, - e14: Felt, - e15: Felt, - e16: Felt, - ptr: *mut Felt, + e1: u32, + e2: u32, + e3: u32, + e4: u32, + e5: u32, + e6: u32, + e7: u32, + e8: u32, + e9: u32, + e10: u32, + e11: u32, + e12: u32, + e13: u32, + e14: u32, + e15: u32, + e16: u32, + ptr: *mut u8, ); +} +#[link(wasm_import_module = "std::crypto::hashes::sha256")] +extern "C" { /// Computes SHA256 1-to-1 hash. /// /// Input: 32-bytes stored in the first 8 elements of the stack (32 bits per element). @@ -56,15 +58,15 @@ extern "C" { /// The output is passed back to the caller via a pointer. #[link_name = "sha256_hash_1to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] fn extern_sha256_hash_1to1( - e1: Felt, - e2: Felt, - e3: Felt, - e4: Felt, - e5: Felt, - e6: Felt, - e7: Felt, - e8: Felt, - ptr: *mut Felt, + e1: u32, + e2: u32, + e3: u32, + e4: u32, + e5: u32, + e6: u32, + e7: u32, + e8: u32, + ptr: *mut u8, ); /// Computes SHA256 2-to-1 hash. @@ -74,23 +76,23 @@ extern "C" { /// The output is passed back to the caller via a pointer. #[link_name = "sha256_hash_2to1<0x0000000000000000000000000000000000000000000000000000000000000000>"] fn extern_sha256_hash_2to1( - e1: Felt, - e2: Felt, - e3: Felt, - e4: Felt, - e5: Felt, - e6: Felt, - e7: Felt, - e8: Felt, - e9: Felt, - e10: Felt, - e11: Felt, - e12: Felt, - e13: Felt, - e14: Felt, - e15: Felt, - e16: Felt, - ptr: *mut Felt, + e1: u32, + e2: u32, + e3: u32, + e4: u32, + e5: u32, + e6: u32, + e7: u32, + e8: u32, + e9: u32, + e10: u32, + e11: u32, + e12: u32, + e13: u32, + e14: u32, + e15: u32, + e16: u32, + ptr: *mut u8, ); } @@ -98,139 +100,76 @@ extern "C" { #[inline(always)] fn hash_1to1( input: [u8; 32], - extern_hash_1to1: unsafe extern "C" fn( - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - *mut Felt, - ), + extern_hash_1to1: unsafe extern "C" fn(u32, u32, u32, u32, u32, u32, u32, u32, *mut u8), ) -> [u8; 32] { - let mut felts_input = [Felt::from_u64_unchecked(0); 8]; - for i in 0..8 { - felts_input[i] = Felt::from_u64_unchecked(u32::from_le_bytes( - input[i * 4..(i + 1) * 4].try_into().unwrap(), - ) as u64); - } + let input = unsafe { core::mem::transmute::<_, [u32; 8]>(input) }; unsafe { - let mut ret_area = ::core::mem::MaybeUninit::<[Felt; 8]>::uninit(); - let ptr = ret_area.as_mut_ptr() as *mut Felt; + let mut ret_area = ::core::mem::MaybeUninit::<[u8; 32]>::uninit(); + let ptr = ret_area.as_mut_ptr() as *mut u8; extern_hash_1to1( - felts_input[0], - felts_input[1], - felts_input[2], - felts_input[3], - felts_input[4], - felts_input[5], - felts_input[6], - felts_input[7], - ptr, + input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7], ptr, ); - let felts_out = ret_area.assume_init(); - let mut result = [0u8; 32]; - for i in 0..8 { - let bytes = felts_out[i].as_u64().to_le_bytes(); - // Copy only 4 bytes since output felt values contain u32 value per felt - result[i * 4..(i + 1) * 4].copy_from_slice(&bytes[0..4]); - } - result + ret_area.assume_init() } } /// Hashes a 64-byte input to a 32-byte output using the given hash function. #[inline(always)] fn hash_2to1( - input1: [u8; 32], - input2: [u8; 32], + input: [u8; 64], extern_hash_2to1: unsafe extern "C" fn( - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - Felt, - *mut Felt, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + u32, + *mut u8, ), ) -> [u8; 32] { - let mut felts_input1 = [Felt::from_u64_unchecked(0); 8]; - for i in 0..8 { - felts_input1[i] = Felt::from_u64_unchecked(u32::from_le_bytes( - input1[i * 4..(i + 1) * 4].try_into().unwrap(), - ) as u64); - } - let mut felts_input2 = [Felt::from_u64_unchecked(0); 8]; - for i in 0..8 { - felts_input2[i] = Felt::from_u64_unchecked(u32::from_le_bytes( - input2[i * 4..(i + 1) * 4].try_into().unwrap(), - ) as u64); - } + let input = unsafe { core::mem::transmute::<_, [u32; 16]>(input) }; unsafe { - let mut ret_area = ::core::mem::MaybeUninit::<[Felt; 16]>::uninit(); - let ptr = ret_area.as_mut_ptr() as *mut Felt; + let mut ret_area = ::core::mem::MaybeUninit::<[u8; 32]>::uninit(); + let ptr = ret_area.as_mut_ptr() as *mut u8; extern_hash_2to1( - felts_input1[0], - felts_input1[1], - felts_input1[2], - felts_input1[3], - felts_input1[4], - felts_input1[5], - felts_input1[6], - felts_input1[7], - felts_input2[0], - felts_input2[1], - felts_input2[2], - felts_input2[3], - felts_input2[4], - felts_input2[5], - felts_input2[6], - felts_input2[7], + input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7], + input[8], input[9], input[10], input[11], input[12], input[13], input[14], input[15], ptr, ); - let felts_out = ret_area.assume_init(); - let mut result = [0u8; 32]; - for i in 0..8 { - let bytes = felts_out[i].as_u64().to_le_bytes(); - // Copy only 4 bytes since output felt values contain u32 value per felt - result[i * 4..(i + 1) * 4].copy_from_slice(&bytes[0..4]); - } - result + ret_area.assume_init() } } /// Hashes a 32-byte input to a 32-byte output using the BLAKE3 hash function. -#[inline(always)] +#[inline] pub fn blake3_hash_1to1(input: [u8; 32]) -> [u8; 32] { hash_1to1(input, extern_blake3_hash_1to1) } -/// Hashes a 64-byte input (two 32-byte arrays) to a 32-byte output using the BLAKE3 hash function. -#[inline(always)] -pub fn blake3_hash_2to1(input1: [u8; 32], input2: [u8; 32]) -> [u8; 32] { - hash_2to1(input1, input2, extern_blake3_hash_2to1) +/// Hashes a 64-byte input to a 32-byte output using the BLAKE3 hash function. +#[inline] +pub fn blake3_hash_2to1(input: [u8; 64]) -> [u8; 32] { + hash_2to1(input, extern_blake3_hash_2to1) } /// Hashes a 32-byte input to a 32-byte output using the SHA256 hash function. -#[inline(always)] +#[inline] pub fn sha256_hash_1to1(input: [u8; 32]) -> [u8; 32] { hash_1to1(input, extern_sha256_hash_1to1) } -/// Hashes a 64-byte input(two 32-byte arrays) to a 32-byte output using the SHA256 hash function. -#[inline(always)] -pub fn sha256_hash_2to1(input1: [u8; 32], input2: [u8; 32]) -> [u8; 32] { - hash_2to1(input1, input2, extern_sha256_hash_2to1) +/// Hashes a 64-byte input to a 32-byte output using the SHA256 hash function. +#[inline] +pub fn sha256_hash_2to1(input: [u8; 64]) -> [u8; 32] { + hash_2to1(input, extern_sha256_hash_2to1) } diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml index 675dd6c8c..543767e6f 100644 --- a/tests/integration/Cargo.toml +++ b/tests/integration/Cargo.toml @@ -23,9 +23,10 @@ miden-stdlib.workspace = true miden-diagnostics.workspace = true midenc-session.workspace = true midenc-compile.workspace = true +midenc-driver.workspace = true expect-test = "1.4.1" miden-integration-tests-rust-fib = { path = "../rust-apps/fib" } -wasmprinter = "0.2.63" +wasmprinter = "0.2.80" sha2 = "0.10" rustc-demangle = { version = "0.1.19", features = ["std"] } cargo_metadata = "0.18" @@ -42,4 +43,4 @@ miden-core.workspace = true concat-idents = "1.1" blake3.workspace = true log.workspace = true -env_logger.workspace = true \ No newline at end of file +env_logger.workspace = true diff --git a/tests/integration/expected/abi_transform_stdlib_blake3_hash.hir b/tests/integration/expected/abi_transform_stdlib_blake3_hash.hir index 0e61ff76a..e1a2d5dfd 100644 --- a/tests/integration/expected/abi_transform_stdlib_blake3_hash.hir +++ b/tests/integration/expected/abi_transform_stdlib_blake3_hash.hir @@ -1,9 +1,6 @@ (component ;; Modules (module #abi_transform_stdlib_blake3_hash - ;; Data Segments - (data (mut) (offset 1048576) 0x7e2f73646b2f7072656c7564652f7372632f7374646c69622f63727970746f2f6861736865732e72730000000000100029000000d100000028000000) - ;; Constants (const (id 0) 0x00100000) @@ -11,409 +8,119 @@ (global (export #__stack_pointer) (id 0) (type i32) (const 0)) ;; Functions - (func (export #entrypoint) (param i32) (param i32) (param i32) - (block 0 (param v0 i32) (param v1 i32) (param v2 i32) - (let (v3 i32) (const.i32 0)) - (let (v4 felt) (const.felt 0)) - (let (v5 i32) (global.load i32 (global.symbol #__stack_pointer))) - (let (v6 i32) (const.i32 432)) - (let (v7 i32) (sub.wrapping v5 v6)) - (let (v8 (ptr i32)) (global.symbol #__stack_pointer)) - (store v8 v7) - (let (v9 i32) (const.i32 0)) - (let (v10 i64) (const.i64 0)) - (let (v11 felt) (cast v10)) - (br (block 2 v9 v7 v2 v0 v1 v11))) - - (block 1) - - (block 2 - (param v12 i32) - (param v275 i32) - (param v276 i32) - (param v277 i32) - (param v278 i32) - (param v282 felt) - (let (v13 i32) (const.i32 64)) - (let (v14 i1) (neq v12 v13)) - (let (v15 i32) (cast v14)) - (let (v16 i1) (neq v15 0)) - (condbr v16 (block 4) (block 5))) - - (block 3) - - (block 4 - (let (v279 i32) (const.i32 8)) - (let (v280 i32) (add.wrapping v275 v279)) - (let (v281 i32) (add.wrapping v280 v12)) - (let (v283 u32) (cast v281)) - (let (v284 (ptr felt)) (inttoptr v283)) - (store v284 v282) - (let (v285 i32) (const.i32 8)) - (let (v286 i32) (add.wrapping v12 v285)) - (br (block 2 v286 v275 v276 v277 v278 v282))) - - (block 5 - (let (v17 i32) (const.i32 0)) - (br (block 6 v17 v275 v276 v277 v278))) - - (block 6 - (param v18 i32) - (param v257 i32) - (param v258 i32) - (param v259 i32) - (param v263 i32) - (let (v19 i32) (const.i32 64)) - (let (v20 i1) (neq v18 v19)) - (let (v21 i32) (cast v20)) - (let (v22 i1) (neq v21 0)) - (condbr v22 (block 8) (block 9))) - - (block 7) - - (block 8 - (let (v260 i32) (const.i32 8)) - (let (v261 i32) (add.wrapping v257 v260)) - (let (v262 i32) (add.wrapping v261 v18)) - (let (v264 u32) (cast v263)) - (let (v265 (ptr u32)) (inttoptr v264)) - (let (v266 u32) (load v265)) - (let (v267 i64) (zext v266)) - (let (v268 felt) (cast v267)) - (let (v269 u32) (cast v262)) - (let (v270 (ptr felt)) (inttoptr v269)) - (store v270 v268) - (let (v271 i32) (const.i32 8)) - (let (v272 i32) (add.wrapping v18 v271)) - (let (v273 i32) (const.i32 4)) - (let (v274 i32) (add.wrapping v263 v273)) - (br (block 6 v272 v257 v258 v259 v274))) - - (block 9 - (let (v23 i32) (const.i32 0)) - (let (v24 i64) (const.i64 0)) - (let (v25 felt) (cast v24)) - (br (block 10 v23 v257 v258 v259 v25))) - - (block 10 - (param v26 i32) - (param v202 i32) - (param v203 i32) - (param v206 i32) - (param v252 felt) - (let (v27 i32) (const.i32 64)) - (let (v28 i1) (neq v26 v27)) - (let (v29 i32) (cast v28)) - (let (v30 i1) (neq v29 0)) - (condbr v30 (block 12) (block 13))) - - (block 11) - - (block 12 - (let (v249 i32) (const.i32 72)) - (let (v250 i32) (add.wrapping v202 v249)) - (let (v251 i32) (add.wrapping v250 v26)) - (let (v253 u32) (cast v251)) - (let (v254 (ptr felt)) (inttoptr v253)) - (store v254 v252) - (let (v255 i32) (const.i32 8)) - (let (v256 i32) (add.wrapping v26 v255)) - (br (block 10 v256 v202 v203 v206 v252))) - - (block 13 - (let (v31 i32) (const.i32 0)) - (br (block 15 v31 v202 v203 v206))) - - (block 14 - (let (v207 u32) (cast v166)) - (let (v208 u32) (add.checked v207 392)) - (let (v209 (ptr i64)) (inttoptr v208)) - (let (v210 i64) (load v209)) - (let (v211 u32) (cast v204)) - (let (v212 (ptr i64)) (inttoptr v211)) - (store v212 v210) - (let (v213 i32) (const.i32 24)) - (let (v214 i32) (add.wrapping v204 v213)) - (let (v215 i32) (const.i32 392)) - (let (v216 i32) (add.wrapping v166 v215)) - (let (v217 i32) (const.i32 24)) - (let (v218 i32) (add.wrapping v216 v217)) - (let (v219 u32) (cast v218)) - (let (v220 (ptr i64)) (inttoptr v219)) - (let (v221 i64) (load v220)) - (let (v222 u32) (cast v214)) - (let (v223 (ptr i64)) (inttoptr v222)) - (store v223 v221) - (let (v224 i32) (const.i32 16)) - (let (v225 i32) (add.wrapping v204 v224)) - (let (v226 i32) (const.i32 392)) - (let (v227 i32) (add.wrapping v166 v226)) - (let (v228 i32) (const.i32 16)) - (let (v229 i32) (add.wrapping v227 v228)) - (let (v230 u32) (cast v229)) - (let (v231 (ptr i64)) (inttoptr v230)) - (let (v232 i64) (load v231)) - (let (v233 u32) (cast v225)) - (let (v234 (ptr i64)) (inttoptr v233)) - (store v234 v232) - (let (v235 i32) (const.i32 8)) - (let (v236 i32) (add.wrapping v204 v235)) - (let (v237 i32) (const.i32 392)) - (let (v238 i32) (add.wrapping v166 v237)) - (let (v239 i32) (const.i32 8)) - (let (v240 i32) (add.wrapping v238 v239)) - (let (v241 u32) (cast v240)) - (let (v242 (ptr i64)) (inttoptr v241)) - (let (v243 i64) (load v242)) - (let (v244 u32) (cast v236)) - (let (v245 (ptr i64)) (inttoptr v244)) - (store v245 v243) - (let (v246 i32) (const.i32 432)) - (let (v247 i32) (add.wrapping v166 v246)) - (let (v248 (ptr i32)) (global.symbol #__stack_pointer)) - (store v248 v247) - (ret)) - - (block 15 - (param v32 i32) - (param v37 i32) - (param v190 i32) - (param v205 i32) - (let (v33 i32) (const.i32 64)) - (let (v34 i1) (neq v32 v33)) - (let (v35 i32) (cast v34)) - (let (v36 i1) (neq v35 0)) - (condbr v36 (block 17) (block 18))) - - (block 16) - - (block 17 - (let (v187 i32) (const.i32 72)) - (let (v188 i32) (add.wrapping v37 v187)) - (let (v189 i32) (add.wrapping v188 v32)) - (let (v191 u32) (cast v190)) - (let (v192 (ptr u32)) (inttoptr v191)) - (let (v193 u32) (load v192)) - (let (v194 i64) (zext v193)) - (let (v195 felt) (cast v194)) - (let (v196 u32) (cast v189)) - (let (v197 (ptr felt)) (inttoptr v196)) - (store v197 v195) - (let (v198 i32) (const.i32 8)) - (let (v199 i32) (add.wrapping v32 v198)) - (let (v200 i32) (const.i32 4)) - (let (v201 i32) (add.wrapping v190 v200)) - (br (block 15 v199 v37 v201 v205))) - - (block 18 - (let (v38 u32) (cast v37)) - (let (v39 u32) (add.checked v38 8)) - (let (v40 (ptr felt)) (inttoptr v39)) - (let (v41 felt) (load v40)) - (let (v42 u32) (cast v37)) - (let (v43 u32) (add.checked v42 16)) - (let (v44 (ptr felt)) (inttoptr v43)) - (let (v45 felt) (load v44)) - (let (v46 u32) (cast v37)) - (let (v47 u32) (add.checked v46 24)) - (let (v48 (ptr felt)) (inttoptr v47)) - (let (v49 felt) (load v48)) - (let (v50 u32) (cast v37)) - (let (v51 u32) (add.checked v50 32)) - (let (v52 (ptr felt)) (inttoptr v51)) - (let (v53 felt) (load v52)) - (let (v54 u32) (cast v37)) - (let (v55 u32) (add.checked v54 40)) - (let (v56 (ptr felt)) (inttoptr v55)) - (let (v57 felt) (load v56)) - (let (v58 u32) (cast v37)) - (let (v59 u32) (add.checked v58 48)) - (let (v60 (ptr felt)) (inttoptr v59)) - (let (v61 felt) (load v60)) - (let (v62 u32) (cast v37)) - (let (v63 u32) (add.checked v62 56)) - (let (v64 (ptr felt)) (inttoptr v63)) - (let (v65 felt) (load v64)) - (let (v66 u32) (cast v37)) - (let (v67 u32) (add.checked v66 64)) - (let (v68 (ptr felt)) (inttoptr v67)) - (let (v69 felt) (load v68)) - (let (v70 u32) (cast v37)) - (let (v71 u32) (add.checked v70 72)) - (let (v72 (ptr felt)) (inttoptr v71)) - (let (v73 felt) (load v72)) - (let (v74 u32) (cast v37)) - (let (v75 u32) (add.checked v74 80)) - (let (v76 (ptr felt)) (inttoptr v75)) - (let (v77 felt) (load v76)) - (let (v78 u32) (cast v37)) - (let (v79 u32) (add.checked v78 88)) - (let (v80 (ptr felt)) (inttoptr v79)) - (let (v81 felt) (load v80)) - (let (v82 u32) (cast v37)) - (let (v83 u32) (add.checked v82 96)) - (let (v84 (ptr felt)) (inttoptr v83)) - (let (v85 felt) (load v84)) - (let (v86 u32) (cast v37)) - (let (v87 u32) (add.checked v86 104)) - (let (v88 (ptr felt)) (inttoptr v87)) - (let (v89 felt) (load v88)) - (let (v90 u32) (cast v37)) - (let (v91 u32) (add.checked v90 112)) - (let (v92 (ptr felt)) (inttoptr v91)) - (let (v93 felt) (load v92)) - (let (v94 u32) (cast v37)) - (let (v95 u32) (add.checked v94 120)) - (let (v96 (ptr felt)) (inttoptr v95)) - (let (v97 felt) (load v96)) - (let (v98 u32) (cast v37)) - (let (v99 u32) (add.checked v98 128)) - (let (v100 (ptr felt)) (inttoptr v99)) - (let (v101 felt) (load v100)) - (let (v102 i32) (const.i32 136)) - (let (v103 i32) (add.wrapping v37 v102)) - (let [(v104 felt) (v105 felt) (v106 felt) (v107 felt) (v108 felt) (v109 felt) (v110 felt) (v111 felt)] (call (#std::crypto_hashes #blake3_hash_2to1) v41 v45 v49 v53 v57 v61 v65 v69 v73 v77 v81 v85 v89 v93 v97 v101)) - (let (v112 u32) (cast v103)) - (let (v113 (ptr felt)) (inttoptr v112)) - (store v113 v104) - (let (v114 u32) (add.checked v112 8)) - (let (v115 (ptr felt)) (inttoptr v114)) - (store v115 v105) - (let (v116 u32) (add.checked v112 16)) - (let (v117 (ptr felt)) (inttoptr v116)) - (store v117 v106) - (let (v118 u32) (add.checked v112 24)) - (let (v119 (ptr felt)) (inttoptr v118)) - (store v119 v107) - (let (v120 u32) (add.checked v112 32)) - (let (v121 (ptr felt)) (inttoptr v120)) - (store v121 v108) - (let (v122 u32) (add.checked v112 40)) - (let (v123 (ptr felt)) (inttoptr v122)) - (store v123 v109) - (let (v124 u32) (add.checked v112 48)) - (let (v125 (ptr felt)) (inttoptr v124)) - (store v125 v110) - (let (v126 u32) (add.checked v112 56)) - (let (v127 (ptr felt)) (inttoptr v126)) - (store v127 v111) - (let (v128 i32) (const.i32 264)) - (let (v129 i32) (add.wrapping v37 v128)) - (let (v130 i32) (const.i32 136)) - (let (v131 i32) (add.wrapping v37 v130)) - (let (v132 i32) (const.i32 128)) - (let (v133 u32) (cast v129)) - (let (v134 (ptr u8)) (inttoptr v133)) - (let (v135 u32) (cast v131)) - (let (v136 (ptr u8)) (inttoptr v135)) - (memcpy v136 v134 v132) - (let (v137 i32) (const.i32 416)) - (let (v138 i32) (add.wrapping v37 v137)) - (let (v139 i64) (const.i64 0)) - (let (v140 u32) (cast v138)) - (let (v141 (ptr i64)) (inttoptr v140)) - (store v141 v139) - (let (v142 i32) (const.i32 408)) - (let (v143 i32) (add.wrapping v37 v142)) - (let (v144 i64) (const.i64 0)) - (let (v145 u32) (cast v143)) - (let (v146 (ptr i64)) (inttoptr v145)) - (store v146 v144) - (let (v147 i32) (const.i32 392)) - (let (v148 i32) (add.wrapping v37 v147)) - (let (v149 i32) (const.i32 8)) - (let (v150 i32) (add.wrapping v148 v149)) - (let (v151 i64) (const.i64 0)) - (let (v152 u32) (cast v150)) - (let (v153 (ptr i64)) (inttoptr v152)) - (store v153 v151) - (let (v154 i64) (const.i64 0)) - (let (v155 u32) (cast v37)) - (let (v156 u32) (add.checked v155 392)) - (let (v157 (ptr i64)) (inttoptr v156)) - (store v157 v154) - (let (v158 i32) (const.i32 264)) - (let (v159 i32) (add.wrapping v37 v158)) - (let (v160 i32) (const.i32 0)) - (br (block 19 v160 v37 v159 v205))) - - (block 19 - (param v161 i32) - (param v166 i32) - (param v167 i32) - (param v204 i32) - (let (v162 i32) (const.i32 32)) - (let (v163 i1) (eq v161 v162)) - (let (v164 i32) (cast v163)) - (let (v165 i1) (neq v164 0)) - (condbr v165 (block 14) (block 21))) - - (block 20) - - (block 21 - (let (v168 u32) (cast v167)) - (let (v169 (ptr felt)) (inttoptr v168)) - (let (v170 felt) (load v169)) - (let (v171 i64) (cast v170)) - (let (v172 u32) (cast v166)) - (let (v173 u32) (add.checked v172 424)) - (let (v174 (ptr i64)) (inttoptr v173)) - (store v174 v171) - (let (v175 i32) (const.i32 392)) - (let (v176 i32) (add.wrapping v166 v175)) - (let (v177 i32) (add.wrapping v176 v161)) - (let (v178 i32) (const.i32 4)) - (let (v179 i32) (const.i32 424)) - (let (v180 i32) (add.wrapping v166 v179)) - (let (v181 i32) (const.i32 4)) - (let (v182 i32) (const.i32 1048620)) - (call #core::slice::::copy_from_slice v177 v178 v180 v181 v182) - (let (v183 i32) (const.i32 4)) - (let (v184 i32) (add.wrapping v161 v183)) - (let (v185 i32) (const.i32 8)) - (let (v186 i32) (add.wrapping v167 v185)) - (br (block 19 v184 v166 v186 v204))) - ) - - (func (export #core::slice::::copy_from_slice::len_mismatch_fail) - (param i32) (param i32) (param i32) - (block 0 (param v0 i32) (param v1 i32) (param v2 i32) - (unreachable)) - - (block 1) - ) - - (func (export #core::slice::::copy_from_slice) - (param i32) (param i32) (param i32) (param i32) (param i32) - (block 0 - (param v0 i32) - (param v1 i32) - (param v2 i32) - (param v3 i32) - (param v4 i32) - (let (v5 i1) (neq v1 v3)) - (let (v6 i32) (cast v5)) - (let (v7 i1) (neq v6 0)) - (condbr v7 (block 2) (block 3))) - - (block 1) - - (block 2 - (call #core::slice::::copy_from_slice::len_mismatch_fail v1 v1 v1) - (unreachable)) - - (block 3 - (let (v8 u32) (cast v0)) - (let (v9 (ptr u8)) (inttoptr v8)) - (let (v10 u32) (cast v2)) - (let (v11 (ptr u8)) (inttoptr v10)) - (memcpy v11 v9 v1) + (func (export #entrypoint) (param i32) (param i32) + (block 0 (param v0 i32) (param v1 i32) + (let (v2 i32) (const.i32 0)) + (let (v3 i32) (global.load i32 (global.symbol #__stack_pointer))) + (let (v4 i32) (const.i32 32)) + (let (v5 i32) (sub.wrapping v3 v4)) + (let (v6 (ptr i32)) (global.symbol #__stack_pointer)) + (store v6 v5) + (let (v7 u32) (cast v1)) + (let (v8 (ptr i32)) (inttoptr v7)) + (let (v9 i32) (load v8)) + (let (v10 u32) (cast v1)) + (let (v11 u32) (add.checked v10 4)) + (let (v12 (ptr i32)) (inttoptr v11)) + (let (v13 i32) (load v12)) + (let (v14 u32) (cast v1)) + (let (v15 u32) (add.checked v14 8)) + (let (v16 (ptr i32)) (inttoptr v15)) + (let (v17 i32) (load v16)) + (let (v18 u32) (cast v1)) + (let (v19 u32) (add.checked v18 12)) + (let (v20 (ptr i32)) (inttoptr v19)) + (let (v21 i32) (load v20)) + (let (v22 u32) (cast v1)) + (let (v23 u32) (add.checked v22 16)) + (let (v24 (ptr i32)) (inttoptr v23)) + (let (v25 i32) (load v24)) + (let (v26 u32) (cast v1)) + (let (v27 u32) (add.checked v26 20)) + (let (v28 (ptr i32)) (inttoptr v27)) + (let (v29 i32) (load v28)) + (let (v30 u32) (cast v1)) + (let (v31 u32) (add.checked v30 24)) + (let (v32 (ptr i32)) (inttoptr v31)) + (let (v33 i32) (load v32)) + (let (v34 u32) (cast v1)) + (let (v35 u32) (add.checked v34 28)) + (let (v36 (ptr i32)) (inttoptr v35)) + (let (v37 i32) (load v36)) + (let [(v38 i32) (v39 i32) (v40 i32) (v41 i32) (v42 i32) (v43 i32) (v44 i32) (v45 i32)] (call (#std::crypto::hashes::blake3 #hash_1to1) v9 v13 v17 v21 v25 v29 v33 v37)) + (let (v46 u32) (cast v5)) + (let (v47 (ptr i32)) (inttoptr v46)) + (store v47 v38) + (let (v48 u32) (add.checked v46 8)) + (let (v49 (ptr i32)) (inttoptr v48)) + (store v49 v39) + (let (v50 u32) (add.checked v46 16)) + (let (v51 (ptr i32)) (inttoptr v50)) + (store v51 v40) + (let (v52 u32) (add.checked v46 24)) + (let (v53 (ptr i32)) (inttoptr v52)) + (store v53 v41) + (let (v54 u32) (add.checked v46 32)) + (let (v55 (ptr i32)) (inttoptr v54)) + (store v55 v42) + (let (v56 u32) (add.checked v46 40)) + (let (v57 (ptr i32)) (inttoptr v56)) + (store v57 v43) + (let (v58 u32) (add.checked v46 48)) + (let (v59 (ptr i32)) (inttoptr v58)) + (store v59 v44) + (let (v60 u32) (add.checked v46 56)) + (let (v61 (ptr i32)) (inttoptr v60)) + (store v61 v45) + (let (v62 i32) (const.i32 24)) + (let (v63 i32) (add.wrapping v0 v62)) + (let (v64 i32) (const.i32 24)) + (let (v65 i32) (add.wrapping v5 v64)) + (let (v66 u32) (cast v65)) + (let (v67 (ptr i64)) (inttoptr v66)) + (let (v68 i64) (load v67)) + (let (v69 u32) (cast v63)) + (let (v70 (ptr i64)) (inttoptr v69)) + (store v70 v68) + (let (v71 i32) (const.i32 16)) + (let (v72 i32) (add.wrapping v0 v71)) + (let (v73 i32) (const.i32 16)) + (let (v74 i32) (add.wrapping v5 v73)) + (let (v75 u32) (cast v74)) + (let (v76 (ptr i64)) (inttoptr v75)) + (let (v77 i64) (load v76)) + (let (v78 u32) (cast v72)) + (let (v79 (ptr i64)) (inttoptr v78)) + (store v79 v77) + (let (v80 i32) (const.i32 8)) + (let (v81 i32) (add.wrapping v0 v80)) + (let (v82 i32) (const.i32 8)) + (let (v83 i32) (add.wrapping v5 v82)) + (let (v84 u32) (cast v83)) + (let (v85 (ptr i64)) (inttoptr v84)) + (let (v86 i64) (load v85)) + (let (v87 u32) (cast v81)) + (let (v88 (ptr i64)) (inttoptr v87)) + (store v88 v86) + (let (v89 u32) (cast v5)) + (let (v90 (ptr i64)) (inttoptr v89)) + (let (v91 i64) (load v90)) + (let (v92 u32) (cast v0)) + (let (v93 (ptr i64)) (inttoptr v92)) + (store v93 v91) + (let (v94 i32) (const.i32 32)) + (let (v95 i32) (add.wrapping v5 v94)) + (let (v96 (ptr i32)) (global.symbol #__stack_pointer)) + (store v96 v95) + (br (block 1))) + + (block 1 (ret)) ) ;; Imports - (func (import #std::crypto_hashes #blake3_hash_2to1) - (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (param felt) (result felt felt felt felt felt felt felt felt)) + (func (import #std::crypto::hashes::blake3 #hash_1to1) + (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32 i32 i32 i32 i32 i32 i32 i32)) ) ) diff --git a/tests/integration/expected/abi_transform_stdlib_blake3_hash.masm b/tests/integration/expected/abi_transform_stdlib_blake3_hash.masm new file mode 100644 index 000000000..2c0281d8b --- /dev/null +++ b/tests/integration/expected/abi_transform_stdlib_blake3_hash.masm @@ -0,0 +1,403 @@ +# mod abi_transform_stdlib_blake3_hash + +use.std::crypto::hashes::blake3 + +export.entrypoint + mem_load.0x00001000 + push.32 + u32wrapping_sub + dup.1 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.0 + dup.0 + push.2147483648 + u32lte + assert + dup.3 + dup.0 + push.2147483648 + u32lte + assert + add.28 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.24 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.5 + dup.0 + push.2147483648 + u32lte + assert + add.20 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.6 + dup.0 + push.2147483648 + u32lte + assert + add.16 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.7 + dup.0 + push.2147483648 + u32lte + assert + add.12 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.8 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.9 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + movup.10 + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + exec.::std::crypto::hashes::blake3::hash_1to1 + dup.8 + movup.8 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.7 + add.8 + u32assert + movup.7 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.6 + add.16 + u32assert + movup.6 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.5 + add.24 + u32assert + movup.5 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.4 + add.32 + u32assert + movup.4 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.3 + add.40 + u32assert + movup.3 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.2 + add.48 + u32assert + movup.2 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + swap.1 + add.56 + u32assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.24 + dup.1 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_dw + push.24 + dup.4 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + push.16 + dup.1 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_dw + push.16 + dup.4 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + push.8 + dup.1 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_dw + push.8 + dup.4 + swap.1 + u32wrapping_add + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + dup.0 + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_dw + movup.3 + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + push.32 + u32wrapping_add + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw +end + + diff --git a/tests/integration/expected/abi_transform_stdlib_blake3_hash.wat b/tests/integration/expected/abi_transform_stdlib_blake3_hash.wat index 4427e5441..651e02779 100644 --- a/tests/integration/expected/abi_transform_stdlib_blake3_hash.wat +++ b/tests/integration/expected/abi_transform_stdlib_blake3_hash.wat @@ -1,295 +1,68 @@ (module $abi_transform_stdlib_blake3_hash.wasm - (type (;0;) (func (param i64) (result f64))) - (type (;1;) (func (param f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 i32))) - (type (;2;) (func (param f64) (result i64))) - (type (;3;) (func (param i32 i32 i32))) - (type (;4;) (func (param i32 i32 i32 i32 i32))) - (import "miden:prelude/intrinsics_felt" "from_u64_unchecked" (func $miden_prelude::intrinsics::felt::extern_from_u64_unchecked (;0;) (type 0))) - (import "std::crypto_hashes" "blake3_hash_2to1<0x0000000000000000000000000000000000000000000000000000000000000000>" (func $miden_prelude::stdlib::crypto::hashes::extern_blake3_hash_2to1 (;1;) (type 1))) - (import "miden:prelude/intrinsics_felt" "as_u64" (func $miden_prelude::intrinsics::felt::extern_as_u64 (;2;) (type 2))) - (func $entrypoint (;3;) (type 3) (param i32 i32 i32) - (local i32 i32 f64) + (type (;0;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type (;1;) (func (param i32 i32))) + (import "std::crypto::hashes::blake3" "hash_1to1<0x0000000000000000000000000000000000000000000000000000000000000000>" (func $miden_stdlib_sys::stdlib::crypto::hashes::extern_blake3_hash_1to1 (;0;) (type 0))) + (func $entrypoint (;1;) (type 1) (param i32 i32) + (local i32) global.get $__stack_pointer - i32.const 432 + i32.const 32 i32.sub - local.tee 3 + local.tee 2 global.set $__stack_pointer - i32.const 0 - local.set 4 - i64.const 0 - call $miden_prelude::intrinsics::felt::extern_from_u64_unchecked - local.set 5 - loop ;; label = @1 - block ;; label = @2 - local.get 4 - i32.const 64 - i32.ne - br_if 0 (;@2;) - i32.const 0 - local.set 4 - loop ;; label = @3 - block ;; label = @4 - local.get 4 - i32.const 64 - i32.ne - br_if 0 (;@4;) - i32.const 0 - local.set 4 - i64.const 0 - call $miden_prelude::intrinsics::felt::extern_from_u64_unchecked - local.set 5 - loop ;; label = @5 - block ;; label = @6 - local.get 4 - i32.const 64 - i32.ne - br_if 0 (;@6;) - i32.const 0 - local.set 4 - block ;; label = @7 - loop ;; label = @8 - block ;; label = @9 - local.get 4 - i32.const 64 - i32.ne - br_if 0 (;@9;) - local.get 3 - f64.load offset=8 - local.get 3 - f64.load offset=16 - local.get 3 - f64.load offset=24 - local.get 3 - f64.load offset=32 - local.get 3 - f64.load offset=40 - local.get 3 - f64.load offset=48 - local.get 3 - f64.load offset=56 - local.get 3 - f64.load offset=64 - local.get 3 - f64.load offset=72 - local.get 3 - f64.load offset=80 - local.get 3 - f64.load offset=88 - local.get 3 - f64.load offset=96 - local.get 3 - f64.load offset=104 - local.get 3 - f64.load offset=112 - local.get 3 - f64.load offset=120 - local.get 3 - f64.load offset=128 - local.get 3 - i32.const 136 - i32.add - call $miden_prelude::stdlib::crypto::hashes::extern_blake3_hash_2to1 - local.get 3 - i32.const 264 - i32.add - local.get 3 - i32.const 136 - i32.add - i32.const 128 - memory.copy - local.get 3 - i32.const 416 - i32.add - i64.const 0 - i64.store - local.get 3 - i32.const 408 - i32.add - i64.const 0 - i64.store - local.get 3 - i32.const 392 - i32.add - i32.const 8 - i32.add - i64.const 0 - i64.store - local.get 3 - i64.const 0 - i64.store offset=392 - local.get 3 - i32.const 264 - i32.add - local.set 2 - i32.const 0 - local.set 4 - loop ;; label = @10 - local.get 4 - i32.const 32 - i32.eq - br_if 3 (;@7;) - local.get 3 - local.get 2 - f64.load - call $miden_prelude::intrinsics::felt::extern_as_u64 - i64.store offset=424 - local.get 3 - i32.const 392 - i32.add - local.get 4 - i32.add - i32.const 4 - local.get 3 - i32.const 424 - i32.add - i32.const 4 - i32.const 1048620 - call $core::slice::::copy_from_slice - local.get 4 - i32.const 4 - i32.add - local.set 4 - local.get 2 - i32.const 8 - i32.add - local.set 2 - br 0 (;@10;) - end - end - local.get 3 - i32.const 72 - i32.add - local.get 4 - i32.add - local.get 2 - i64.load32_u align=1 - call $miden_prelude::intrinsics::felt::extern_from_u64_unchecked - f64.store - local.get 4 - i32.const 8 - i32.add - local.set 4 - local.get 2 - i32.const 4 - i32.add - local.set 2 - br 0 (;@8;) - end - end - local.get 0 - local.get 3 - i64.load offset=392 - i64.store align=1 - local.get 0 - i32.const 24 - i32.add - local.get 3 - i32.const 392 - i32.add - i32.const 24 - i32.add - i64.load - i64.store align=1 - local.get 0 - i32.const 16 - i32.add - local.get 3 - i32.const 392 - i32.add - i32.const 16 - i32.add - i64.load - i64.store align=1 - local.get 0 - i32.const 8 - i32.add - local.get 3 - i32.const 392 - i32.add - i32.const 8 - i32.add - i64.load - i64.store align=1 - local.get 3 - i32.const 432 - i32.add - global.set $__stack_pointer - return - end - local.get 3 - i32.const 72 - i32.add - local.get 4 - i32.add - local.get 5 - f64.store - local.get 4 - i32.const 8 - i32.add - local.set 4 - br 0 (;@5;) - end - end - local.get 3 - i32.const 8 - i32.add - local.get 4 - i32.add - local.get 1 - i64.load32_u align=1 - call $miden_prelude::intrinsics::felt::extern_from_u64_unchecked - f64.store - local.get 4 - i32.const 8 - i32.add - local.set 4 - local.get 1 - i32.const 4 - i32.add - local.set 1 - br 0 (;@3;) - end - end - local.get 3 - i32.const 8 - i32.add - local.get 4 - i32.add - local.get 5 - f64.store - local.get 4 - i32.const 8 - i32.add - local.set 4 - br 0 (;@1;) - end - ) - (func $core::slice::::copy_from_slice::len_mismatch_fail (;4;) (type 3) (param i32 i32 i32) - unreachable - unreachable - ) - (func $core::slice::::copy_from_slice (;5;) (type 4) (param i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 1 - local.get 3 - i32.ne - br_if 0 (;@1;) - local.get 0 - local.get 2 - local.get 1 - memory.copy - return - end local.get 1 + i32.load align=1 + local.get 1 + i32.load offset=4 align=1 + local.get 1 + i32.load offset=8 align=1 local.get 1 + i32.load offset=12 align=1 local.get 1 - call $core::slice::::copy_from_slice::len_mismatch_fail - unreachable + i32.load offset=16 align=1 + local.get 1 + i32.load offset=20 align=1 + local.get 1 + i32.load offset=24 align=1 + local.get 1 + i32.load offset=28 align=1 + local.get 2 + call $miden_stdlib_sys::stdlib::crypto::hashes::extern_blake3_hash_1to1 + local.get 0 + i32.const 24 + i32.add + local.get 2 + i32.const 24 + i32.add + i64.load align=1 + i64.store align=1 + local.get 0 + i32.const 16 + i32.add + local.get 2 + i32.const 16 + i32.add + i64.load align=1 + i64.store align=1 + local.get 0 + i32.const 8 + i32.add + local.get 2 + i32.const 8 + i32.add + i64.load align=1 + i64.store align=1 + local.get 0 + local.get 2 + i64.load align=1 + i64.store align=1 + local.get 2 + i32.const 32 + i32.add + global.set $__stack_pointer ) (table (;0;) 1 1 funcref) - (memory (;0;) 17) + (memory (;0;) 16) (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) (export "memory" (memory 0)) (export "entrypoint" (func $entrypoint)) - (data $.rodata (;0;) (i32.const 1048576) "~/sdk/prelude/src/stdlib/crypto/hashes.rs\00\00\00\00\00\10\00)\00\00\00\d1\00\00\00(\00\00\00") ) \ No newline at end of file diff --git a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.hir b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.hir index bd3366937..ea2f47d94 100644 --- a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.hir +++ b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.hir @@ -773,68 +773,65 @@ (let (v9 i32) (const.i32 0)) (call #alloc::raw_vec::RawVec::try_allocate_in v7 v8 v9) (let (v10 u32) (cast v4)) - (let (v11 u32) (add.checked v10 12)) + (let (v11 u32) (add.checked v10 8)) (let (v12 u32) (mod.unchecked v11 2)) (assertz v12) (let (v13 (ptr i32)) (inttoptr v11)) (let (v14 i32) (load v13)) (let (v15 u32) (cast v4)) - (let (v16 u32) (add.checked v15 8)) + (let (v16 u32) (add.checked v15 4)) (let (v17 u32) (mod.unchecked v16 2)) (assertz v17) (let (v18 (ptr i32)) (inttoptr v16)) (let (v19 i32) (load v18)) - (let (v20 u32) (cast v4)) - (let (v21 u32) (add.checked v20 4)) - (let (v22 u32) (mod.unchecked v21 2)) - (assertz v22) - (let (v23 (ptr i32)) (inttoptr v21)) - (let (v24 i32) (load v23)) - (let (v25 i1) (eq v24 0)) - (let (v26 i32) (zext v25)) - (let (v27 i1) (neq v26 0)) - (condbr v27 (block 3) (block 4))) + (let (v20 i1) (eq v19 0)) + (let (v21 i32) (zext v20)) + (let (v22 i1) (neq v21 0)) + (condbr v22 (block 2) (block 3))) - (block 1) + (block 1 + (ret)) (block 2 - (call #alloc::raw_vec::capacity_overflow) - (unreachable)) + (let (v28 u32) (cast v4)) + (let (v29 u32) (add.checked v28 12)) + (let (v30 u32) (mod.unchecked v29 2)) + (assertz v30) + (let (v31 (ptr i32)) (inttoptr v29)) + (let (v32 i32) (load v31)) + (let [(v33 i32) (v34 felt)] (call (#miden::note #get_inputs) v32)) + (let (v35 i32) (const.i32 0)) + (let (v36 u32) (cast v0)) + (let (v37 u32) (add.checked v36 8)) + (let (v38 u32) (mod.unchecked v37 2)) + (assertz v38) + (let (v39 (ptr i32)) (inttoptr v37)) + (store v39 v35) + (let (v40 u32) (cast v0)) + (let (v41 u32) (add.checked v40 4)) + (let (v42 u32) (mod.unchecked v41 2)) + (assertz v42) + (let (v43 (ptr i32)) (inttoptr v41)) + (store v43 v32) + (let (v44 u32) (cast v0)) + (let (v45 u32) (mod.unchecked v44 2)) + (assertz v45) + (let (v46 (ptr i32)) (inttoptr v44)) + (store v46 v14) + (let (v47 i32) (const.i32 16)) + (let (v48 i32) (add.wrapping v4 v47)) + (let (v49 (ptr i32)) (global.symbol #__stack_pointer)) + (store v49 v48) + (br (block 1))) (block 3 - (let [(v31 i32) (v32 felt)] (call (#miden::note #get_inputs) v14)) - (let (v33 i32) (const.i32 0)) - (let (v34 u32) (cast v0)) - (let (v35 u32) (add.checked v34 8)) - (let (v36 u32) (mod.unchecked v35 2)) - (assertz v36) - (let (v37 (ptr i32)) (inttoptr v35)) - (store v37 v33) - (let (v38 u32) (cast v0)) - (let (v39 u32) (add.checked v38 4)) - (let (v40 u32) (mod.unchecked v39 2)) - (assertz v40) - (let (v41 (ptr i32)) (inttoptr v39)) - (store v41 v14) - (let (v42 u32) (cast v0)) - (let (v43 u32) (mod.unchecked v42 2)) - (assertz v43) - (let (v44 (ptr i32)) (inttoptr v42)) - (store v44 v19) - (let (v45 i32) (const.i32 16)) - (let (v46 i32) (add.wrapping v4 v45)) - (let (v47 (ptr i32)) (global.symbol #__stack_pointer)) - (store v47 v46) - (ret)) - - (block 4 - (let (v28 i1) (eq v19 0)) - (let (v29 i32) (zext v28)) - (let (v30 i1) (neq v29 0)) - (condbr v30 (block 2) (block 5))) - - (block 5 - (call #alloc::alloc::handle_alloc_error v19 v14) + (let (v23 u32) (cast v4)) + (let (v24 u32) (add.checked v23 12)) + (let (v25 u32) (mod.unchecked v24 2)) + (assertz v25) + (let (v26 (ptr i32)) (inttoptr v24)) + (let (v27 i32) (load v26)) + (call #alloc::raw_vec::handle_error v14 v27) (unreachable)) ) @@ -956,7 +953,7 @@ (br (block 2 v40 v50))) ) - (func (export #alloc::alloc::handle_alloc_error) + (func (export #alloc::raw_vec::handle_error) (param i32) (param i32) (block 0 (param v0 i32) (param v1 i32) (unreachable)) @@ -964,14 +961,6 @@ (block 1) ) - (func (export #alloc::raw_vec::capacity_overflow) - - (block 0 - (unreachable)) - - (block 1) - ) - ;; Imports (func (import #miden::note #get_inputs) (param i32) (result i32 felt)) ) diff --git a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.masm b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.masm index d510f1e40..2ef95c65b 100644 --- a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.masm +++ b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.masm @@ -1,15 +1,3 @@ -# mod miden::note - -export.get_inputs - push.4294967295 - push.1 - push.0 - push.4294967295 - dup.4 - mem_storew - push.4 - end - # mod abi_transform_tx_kernel_get_inputs_4 use.miden::note @@ -19,85 +7,47 @@ export.entrypoint end -export."__rust_alloc" - push.1048576 - movup.2 - swap.1 - exec."::alloc" -end - - -export."__rust_alloc_zeroed" - push.1048576 +export."miden_tx_kernel_sys::get_inputs" + mem_load.0x00001000 + push.16 + u32wrapping_sub dup.1 - swap.2 - swap.3 swap.1 - exec."::alloc" dup.0 - eq.0 - neq.0 - if.true - swap.1 drop - else - push.0 - push.128 - u32and - movup.2 - dup.2 - dup.0 - push.2147483648 - u32lte - assert - push.0 - dup.2 - gte.0 - while.true - dup.1 - dup.1 - push.1 - u32overflowing_madd - assertz - dup.4 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - dup.2 - dup.2 - dup.2 - exec.::intrinsics::mem::load_sw - push.4294967040 - u32and - movup.5 - u32or - movdn.4 - exec.::intrinsics::mem::store_sw - u32wrapping_add.1 - dup.0 - dup.3 - u32gte - end - dropw - end -end - - -export."wee_alloc::neighbors::Neighbors::remove" + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw dup.0 dup.0 push.2147483648 u32lte assert + add.4 + u32assert + dup.1 dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + push.0 + push.256 + push.4 + dup.5 + swap.1 + u32wrapping_add + exec."alloc::raw_vec::RawVec::try_allocate_in" + dup.1 u32mod.2 assertz.err=0 dup.0 + dup.0 u32mod.16 dup.0 u32mod.4 @@ -106,10 +56,20 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - push.2 - dup.1 swap.1 - u32and + u32mod.2 + assertz.err=0 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + eq.0 neq.0 if.true dup.1 @@ -117,11 +77,110 @@ export."wee_alloc::neighbors::Neighbors::remove" push.2147483648 u32lte assert + add.12 + u32assert + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.1 + exec.::miden::note::get_inputs + dup.7 + dup.0 + push.2147483648 + u32lte + assert add.4 u32assert + push.0 + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.8 + dup.0 + push.2147483648 + u32lte + assert + dup.1 + movup.6 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.0 + movup.7 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.16 + movup.7 + swap.1 + u32wrapping_add + dup.0 + u32mod.16 dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 u32mod.2 assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + dropw + dropw + else + movup.2 + drop + swap.1 + dup.0 + push.2147483648 + u32lte + assert + add.12 + u32assert + dup.0 dup.0 u32mod.16 dup.0 @@ -131,293 +190,614 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - push.4294967292 + movup.2 + exec."alloc::raw_vec::handle_error" + u32mod.2 + assertz.err=0 + push.0 + assert + end +end + + +export."alloc::raw_vec::handle_error" + push.0 assert +end + + +export."alloc::raw_vec::RawVec::try_allocate_in" + dup.1 + neq.0 + if.true dup.1 - swap.1 - u32and - dup.0 - eq.0 + push.536870912 + u32lt + push.0 + push.0 + push.4294967294 + movup.2 + cdrop + u32or neq.0 if.true - drop + push.2 dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - movup.3 - dup.0 - push.2147483648 - u32lte - assert - push.3 - movup.3 swap.1 - u32and - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.3 + u32shl movup.3 - swap.1 - u32and - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - else - swap.1 - drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert - swap.1 - dup.0 - push.2147483648 - u32lte - assert - dup.3 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.4 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - push.4294967292 - movup.6 - swap.1 - u32and - u32or - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.4 - dup.0 - push.2147483648 - u32lte - assert - movup.5 - dup.0 - push.2147483648 - u32lte - assert - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - swap.1 - u32mod.2 - assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - end - else - push.4294967292 - dup.1 - swap.1 - u32and - dup.0 - eq.0 - neq.0 - if.true - drop - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.0 - u32mod.2 - assertz.err=0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.4294967292 - dup.1 - swap.1 - u32and - dup.0 - eq.0 neq.0 if.true - drop - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - movup.3 - dup.0 - push.2147483648 - u32lte - assert - push.3 - movup.3 - swap.1 - u32and - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.3 - movup.3 - swap.1 - u32and + push.4 dup.1 + exec."__rust_alloc_zeroed" dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - else - swap.1 - drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert - swap.1 - dup.0 - push.2147483648 - u32lte - assert - dup.3 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.4 + eq.0 + neq.0 + if.true + movup.3 + swap.1 + drop + drop + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.1 + movup.3 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.2 + dup.0 + push.2147483648 + u32lte + assert + push.4 + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.1 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + else + swap.1 + drop + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.1 + movup.3 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.2 + dup.0 + push.2147483648 + u32lte + assert + dup.1 + movup.4 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.0 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end + else + push.4 + dup.1 + exec."__rust_alloc" + dup.0 + eq.0 + neq.0 + if.true + movup.3 + swap.1 + drop + drop + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.1 + movup.3 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.2 + dup.0 + push.2147483648 + u32lte + assert + push.4 + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.1 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + else + swap.1 + drop + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.8 + u32assert + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.1 + movup.3 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.2 + dup.0 + push.2147483648 + u32lte + assert + dup.1 + movup.4 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.0 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end + end + else + movdn.2 + drop + drop + dup.0 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + swap.1 + dup.0 + push.2147483648 + u32lte + assert + push.0 + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.1 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end + else + movdn.2 + drop + drop + dup.0 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + swap.1 + dup.0 + push.2147483648 + u32lte + assert + push.4.0 + dup.3 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + push.0 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end +end + + +export."__rust_alloc" + push.1048576 + movup.2 + swap.1 + exec."::alloc" +end + + +export."__rust_alloc_zeroed" + push.1048576 + dup.1 + swap.2 + swap.3 + swap.1 + exec."::alloc" + dup.0 + eq.0 + neq.0 + if.true + swap.1 drop + else + push.0 + push.128 + u32and + movup.2 + dup.2 + dup.0 + push.2147483648 + u32lte + assert + push.0 + dup.2 + gte.0 + while.true + dup.1 + dup.1 + push.1 + u32overflowing_madd + assertz + dup.4 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + dup.2 + dup.2 + dup.2 + exec.::intrinsics::mem::load_sw + push.4294967040 + u32and + movup.5 + u32or + movdn.4 + exec.::intrinsics::mem::store_sw + u32wrapping_add.1 + dup.0 + dup.3 + u32gte + end + dropw + end +end + + +export."::alloc" + push.1 + dup.2 + push.1 + u32gt + push.0 + push.0 + push.4294967294 + movup.2 + cdrop + u32or + neq.0 + movup.3 + swap.1 + cdrop + mem_load.0x00001000 + push.16 + u32wrapping_sub + dup.1 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.3 + eq.0 + neq.0 + if.true + swap.3 + movup.2 + drop + drop + push.16 + movup.2 + swap.1 + u32wrapping_add + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + else + dup.0 + dup.0 + push.2147483648 + u32lte + assert + add.12 + u32assert + dup.3 + dup.0 + push.2147483648 + u32lte + assert + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + swap.1 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + push.12 + dup.1 + swap.1 + u32wrapping_add + push.3 + movup.5 + swap.1 + u32wrapping_add + push.2 + u32shr + dup.0 + dup.4 + movup.2 + swap.3 + movdn.2 + swap.1 + exec."wee_alloc::alloc_first_fit" + dup.0 + eq.0 + neq.0 + if.true + drop + dup.1 + dup.0 + push.2147483648 + u32lte + assert + dup.3 + dup.2 + dup.4 + dup.5 + exec."::new_cell_for_free_list" + dup.0 + u32mod.2 + assertz.err=0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + eq.0 + neq.0 + if.true + dup.1 dup.0 push.2147483648 u32lte assert add.4 u32assert - dup.2 + dup.0 dup.0 u32mod.16 dup.0 @@ -427,34 +807,28 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - push.3 - u32and - push.4294967292 - movup.6 - swap.1 - u32and - u32or - dup.4 dup.0 - u32mod.16 dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw + push.2147483648 + u32lte + assert + add.8 + u32assert dup.4 dup.0 push.2147483648 u32lte assert - movup.5 + add.12 + u32assert + dup.5 dup.0 push.2147483648 u32lte assert - dup.2 + add.12 + u32assert + dup.1 dup.0 u32mod.16 dup.0 @@ -464,9 +838,7 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.4 + dup.3 dup.0 u32mod.16 dup.0 @@ -477,18 +849,8 @@ export."wee_alloc::neighbors::Neighbors::remove" u32div.16 exec.::intrinsics::mem::store_sw dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 + movup.4 swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.2 dup.0 u32mod.16 dup.0 @@ -498,154 +860,29 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::store_sw - swap.1 - u32mod.2 - assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - end - else - swap.1 - drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - movup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.3 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.4 - dup.0 - push.2147483648 - u32lte - assert - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.4294967292 - u32and - u32or - dup.5 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.1 - u32mod.2 - assertz.err=0 - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - swap.1 - u32mod.2 - assertz.err=0 - movup.4 - u32mod.2 - assertz.err=0 - movup.3 - u32mod.2 - assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.4294967292 - dup.1 - swap.1 - u32and - dup.0 - eq.0 - neq.0 - if.true - drop - dup.2 + movup.7 dup.0 push.2147483648 u32lte assert - add.4 - u32assert - movup.3 + dup.6 dup.0 push.2147483648 u32lte assert - push.3 - movup.3 + add.12 + u32assert + push.12 + dup.8 swap.1 - u32and - dup.2 + u32wrapping_add + swap.1 + swap.9 + swap.1 + swap.2 + swap.7 + exec."wee_alloc::alloc_first_fit" + dup.7 dup.0 u32mod.16 dup.0 @@ -654,12 +891,8 @@ export."wee_alloc::neighbors::Neighbors::remove" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::store_sw - push.3 - movup.3 - swap.1 - u32and - dup.1 + exec.::intrinsics::mem::load_sw + dup.6 dup.0 u32mod.16 dup.0 @@ -669,38 +902,73 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::store_sw + movup.5 + u32mod.2 + assertz.err=0 + movup.6 + u32mod.2 + assertz.err=0 + swap.1 + u32mod.2 + assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + swap.1 u32mod.2 assertz.err=0 + swap.1 u32mod.2 assertz.err=0 + dup.0 + neq.0 + if.true + push.16 + movup.2 + swap.1 + u32wrapping_add + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + else + drop + push.16 + u32wrapping_add + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.0 + end else + movup.2 swap.1 drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert + drop swap.1 dup.0 push.2147483648 u32lte assert - dup.3 + dup.1 dup.0 push.2147483648 u32lte assert - add.4 + add.12 u32assert - dup.4 dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.2 dup.0 u32mod.16 dup.0 @@ -710,33 +978,6 @@ export."wee_alloc::neighbors::Neighbors::remove" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - push.3 - u32and - push.4294967292 - movup.6 - swap.1 - u32and - u32or - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.4 - dup.0 - push.2147483648 - u32lte - assert - movup.5 - dup.0 - push.2147483648 - u32lte - assert dup.2 dup.0 u32mod.16 @@ -746,32 +987,11 @@ export."wee_alloc::neighbors::Neighbors::remove" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 exec.::intrinsics::mem::store_sw - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 + push.16 + movup.3 swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - push.3 - u32and - dup.2 + u32wrapping_add dup.0 u32mod.16 dup.0 @@ -784,192 +1004,66 @@ export."wee_alloc::neighbors::Neighbors::remove" swap.1 u32mod.2 assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 u32mod.2 assertz.err=0 + push.0 end - end - end -end - - -export."::new_cell_for_free_list" - swap.1 - drop - push.3 - movup.3 - swap.1 - u32shl - push.512 - u32wrapping_add - push.2 - movup.3 - swap.1 - u32shl - dup.0 - dup.2 - u32gt - push.0 - push.0 - push.4294967294 - movup.2 - cdrop - u32or - neq.0 - cdrop - push.65543 - u32wrapping_add - dup.0 - push.16 - u32shr - dup.0 - push.2147483648 - u32lte - assert - push.4294967295 - push.4294967295 - dup.1 - swap.1 - neq - neq.0 - if.true - push.16 - u32shl - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - dup.0 - push.2147483648 - u32lte - assert - push.0.0 - dup.3 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_dw - dup.4 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - push.4294901760 - movup.5 - swap.1 - u32and - dup.4 - swap.1 - u32wrapping_add - push.2 - u32or - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.4 - dup.0 - push.2147483648 - u32lte - assert - dup.1 - movup.5 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.0 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - else - drop - drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - swap.1 - dup.0 - push.2147483648 - u32lte - assert - push.0 - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.1 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 + else + swap.3 + swap.1 + drop + drop + movup.2 + dup.0 + push.2147483648 + u32lte + assert + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.12 + u32assert + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.16 + movup.3 + swap.1 + u32wrapping_add + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + swap.1 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end end end @@ -2485,26 +2579,15 @@ export."wee_alloc::alloc_first_fit" end -export."::alloc" - push.1 - dup.2 - push.1 - u32gt - push.0 - push.0 - push.4294967294 - movup.2 - cdrop - u32or - neq.0 - movup.3 - swap.1 - cdrop - mem_load.0x00000000 - push.16 - u32wrapping_sub - dup.1 - swap.1 +export."wee_alloc::neighbors::Neighbors::remove" + dup.0 + dup.0 + push.2147483648 + u32lte + assert + dup.0 + u32mod.2 + assertz.err=0 dup.0 u32mod.16 dup.0 @@ -2513,19 +2596,23 @@ export."::alloc" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::store_sw - dup.3 - eq.0 + exec.::intrinsics::mem::load_sw + push.2 + dup.1 + swap.1 + u32and neq.0 if.true - swap.3 - movup.2 - drop - drop - push.16 - movup.2 - swap.1 - u32wrapping_add + dup.1 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.0 + u32mod.2 + assertz.err=0 dup.0 u32mod.16 dup.0 @@ -2534,62 +2621,189 @@ export."::alloc" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::store_sw + exec.::intrinsics::mem::load_sw + push.4294967292 + dup.1 + swap.1 + u32and + dup.0 + eq.0 + neq.0 + if.true + drop + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + movup.3 + dup.0 + push.2147483648 + u32lte + assert + push.3 + movup.3 + swap.1 + u32and + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.3 + movup.3 + swap.1 + u32and + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + else + swap.1 + drop + dup.0 + dup.0 + push.2147483648 + u32lte + assert + swap.1 + dup.0 + push.2147483648 + u32lte + assert + dup.3 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + push.4294967292 + movup.6 + swap.1 + u32and + u32or + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.4 + dup.0 + push.2147483648 + u32lte + assert + movup.5 + dup.0 + push.2147483648 + u32lte + assert + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + swap.1 + u32mod.2 + assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + swap.1 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end else - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.12 - u32assert - dup.3 - dup.0 - push.2147483648 - u32lte - assert - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - swap.1 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - push.12 + push.4294967292 dup.1 swap.1 - u32wrapping_add - push.3 - movup.5 - swap.1 - u32wrapping_add - push.2 - u32shr - dup.0 - dup.4 - movup.2 - swap.3 - movdn.2 - swap.1 - exec."wee_alloc::alloc_first_fit" + u32and dup.0 eq.0 neq.0 @@ -2600,11 +2814,8 @@ export."::alloc" push.2147483648 u32lte assert - dup.3 - dup.2 - dup.4 - dup.5 - exec."::new_cell_for_free_list" + add.4 + u32assert dup.0 u32mod.2 assertz.err=0 @@ -2617,48 +2828,32 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::load_sw + push.4294967292 + dup.1 + swap.1 + u32and + dup.0 eq.0 neq.0 if.true - dup.1 + drop + dup.2 dup.0 push.2147483648 u32lte assert add.4 u32assert - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.4 - dup.0 - push.2147483648 - u32lte - assert - add.12 - u32assert - dup.5 + movup.3 dup.0 push.2147483648 u32lte assert - add.12 - u32assert - dup.1 + push.3 + movup.3 + swap.1 + u32and + dup.2 dup.0 u32mod.16 dup.0 @@ -2667,8 +2862,12 @@ export."::alloc" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::load_sw - dup.3 + exec.::intrinsics::mem::store_sw + push.3 + movup.3 + swap.1 + u32and + dup.1 dup.0 u32mod.16 dup.0 @@ -2678,41 +2877,38 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::store_sw - dup.0 - movup.4 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + else swap.1 + drop dup.0 - u32mod.16 dup.0 - u32mod.4 + push.2147483648 + u32lte + assert swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.7 dup.0 push.2147483648 u32lte assert - dup.6 + dup.3 dup.0 push.2147483648 u32lte assert - add.12 + add.4 u32assert - push.12 - dup.8 - swap.1 - u32wrapping_add - swap.1 - swap.9 - swap.1 - swap.2 - swap.7 - exec."wee_alloc::alloc_first_fit" - dup.7 + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.2 dup.0 u32mod.16 dup.0 @@ -2722,7 +2918,14 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - dup.6 + push.3 + u32and + push.4294967292 + movup.6 + swap.1 + u32and + u32or + dup.4 dup.0 u32mod.16 dup.0 @@ -2732,73 +2935,17 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::store_sw - movup.5 - u32mod.2 - assertz.err=0 - movup.6 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 - swap.1 - u32mod.2 - assertz.err=0 - dup.0 - neq.0 - if.true - push.16 - movup.2 - swap.1 - u32wrapping_add - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - else - drop - push.16 - u32wrapping_add - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.0 - end - else - movup.2 - swap.1 - drop - drop - swap.1 + dup.4 dup.0 push.2147483648 u32lte assert - dup.1 + movup.5 dup.0 push.2147483648 u32lte assert - add.12 - u32assert - dup.0 + dup.2 dup.0 u32mod.16 dup.0 @@ -2808,7 +2955,9 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::load_sw - dup.2 + push.3 + u32and + dup.4 dup.0 u32mod.16 dup.0 @@ -2818,10 +2967,19 @@ export."::alloc" movup.2 u32div.16 exec.::intrinsics::mem::store_sw - push.16 - movup.3 + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 swap.1 - u32wrapping_add + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.2 dup.0 u32mod.16 dup.0 @@ -2834,591 +2992,435 @@ export."::alloc" swap.1 u32mod.2 assertz.err=0 + movup.2 u32mod.2 assertz.err=0 - push.0 - end - else - swap.3 - swap.1 - drop - drop - movup.2 - dup.0 - push.2147483648 - u32lte - assert - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.12 - u32assert - dup.0 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.16 - movup.3 - swap.1 - u32wrapping_add - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - swap.1 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - end - end -end - - -export."miden_tx_kernel_sys::get_inputs" - mem_load.0x00000000 - push.16 - u32wrapping_sub - dup.1 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.12 - u32assert - push.0 - push.256 - push.4 - dup.6 - swap.1 - u32wrapping_add - exec."alloc::raw_vec::RawVec::try_allocate_in" - dup.2 - u32mod.2 - assertz.err=0 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - movup.2 - u32mod.2 - assertz.err=0 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - movup.2 - u32mod.2 - assertz.err=0 - movup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::load_sw - eq.0 - neq.0 - if.true - dup.3 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.1 - exec.::miden::note::get_inputs - dup.6 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - push.0 - dup.4 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.7 - dup.0 - push.2147483648 - u32lte - assert - dup.1 - movup.6 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - dup.0 - movup.6 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.16 - movup.6 - swap.1 - u32wrapping_add - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - movup.2 - u32mod.2 - assertz.err=0 - dropw - dropw - else - swap.3 - movup.2 - drop - drop - dup.0 - eq.0 - neq.0 - if.true - exec."alloc::raw_vec::capacity_overflow" - push.0 - assert + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + swap.1 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + end else - exec."alloc::alloc::handle_alloc_error" - push.0 + swap.1 + drop + dup.0 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + movup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.3 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.4 + dup.0 + push.2147483648 + u32lte assert - end - end -end - - -export."alloc::raw_vec::RawVec::try_allocate_in" - dup.1 - neq.0 - if.true - dup.1 - push.536870912 - u32lt - push.0 - push.0 - push.4294967294 - movup.2 - cdrop - u32or - neq.0 - if.true - push.2 dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 swap.1 - u32shl + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.4294967292 + u32and + u32or + dup.5 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.1 + u32mod.2 + assertz.err=0 + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + swap.1 + u32mod.2 + assertz.err=0 + movup.4 + u32mod.2 + assertz.err=0 movup.3 - neq.0 - if.true - push.4 - dup.1 - exec."__rust_alloc_zeroed" - dup.0 - eq.0 - neq.0 - if.true - movup.3 - swap.1 - drop - drop - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - movup.3 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.2 - dup.0 - push.2147483648 - u32lte - assert - push.4 - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.1 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - else - swap.1 - drop - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - movup.3 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.2 - dup.0 - push.2147483648 - u32lte - assert - dup.1 - movup.4 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.0 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - end - else - push.4 + u32mod.2 + assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.4294967292 + dup.1 + swap.1 + u32and + dup.0 + eq.0 + neq.0 + if.true + drop + dup.2 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + movup.3 + dup.0 + push.2147483648 + u32lte + assert + push.3 + movup.3 + swap.1 + u32and + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.3 + movup.3 + swap.1 + u32and dup.1 - exec."__rust_alloc" dup.0 - eq.0 - neq.0 - if.true - movup.3 - swap.1 - drop - drop - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - movup.3 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.2 - dup.0 - push.2147483648 - u32lte - assert - push.4 - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.1 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - else - swap.1 - drop - dup.1 - dup.0 - push.2147483648 - u32lte - assert - add.8 - u32assert - dup.2 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - dup.1 - movup.3 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - movup.2 - dup.0 - push.2147483648 - u32lte - assert - dup.1 - movup.4 - swap.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.0 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 - end + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + else + swap.1 + drop + dup.0 + dup.0 + push.2147483648 + u32lte + assert + swap.1 + dup.0 + push.2147483648 + u32lte + assert + dup.3 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + push.4294967292 + movup.6 + swap.1 + u32and + u32or + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.4 + dup.0 + push.2147483648 + u32lte + assert + movup.5 + dup.0 + push.2147483648 + u32lte + assert + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.4 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + dup.0 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::load_sw + push.3 + u32and + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + swap.1 + u32mod.2 + assertz.err=0 + movup.2 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + swap.1 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 end - else - movdn.2 - drop - drop - dup.0 - dup.0 - push.2147483648 - u32lte - assert - add.4 - u32assert - swap.1 - dup.0 - push.2147483648 - u32lte - assert - push.0 - dup.2 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - push.1 - dup.1 - dup.0 - u32mod.16 - dup.0 - u32mod.4 - swap.1 - u32div.4 - movup.2 - u32div.16 - exec.::intrinsics::mem::store_sw - u32mod.2 - assertz.err=0 - u32mod.2 - assertz.err=0 end + end +end + + +export."::new_cell_for_free_list" + swap.1 + drop + push.3 + movup.3 + swap.1 + u32shl + push.512 + u32wrapping_add + push.2 + movup.3 + swap.1 + u32shl + dup.0 + dup.2 + u32gt + push.0 + push.0 + push.4294967294 + movup.2 + cdrop + u32or + neq.0 + cdrop + push.65543 + u32wrapping_add + dup.0 + push.16 + u32shr + dup.0 + push.2147483648 + u32lte + assert + push.4294967295 + push.4294967295 + dup.1 + swap.1 + neq + neq.0 + if.true + push.16 + u32shl + dup.0 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + dup.1 + dup.0 + push.2147483648 + u32lte + assert + push.0.0 + dup.3 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_dw + dup.4 + dup.0 + push.2147483648 + u32lte + assert + add.4 + u32assert + push.4294901760 + movup.5 + swap.1 + u32and + dup.4 + swap.1 + u32wrapping_add + push.2 + u32or + dup.2 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + movup.4 + dup.0 + push.2147483648 + u32lte + assert + dup.1 + movup.5 + swap.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + push.0 + dup.1 + dup.0 + u32mod.16 + dup.0 + u32mod.4 + swap.1 + u32div.4 + movup.2 + u32div.16 + exec.::intrinsics::mem::store_sw + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 + u32mod.2 + assertz.err=0 else - movdn.2 drop drop dup.0 @@ -3433,8 +3435,8 @@ export."alloc::raw_vec::RawVec::try_allocate_in" push.2147483648 u32lte assert - push.4.0 - dup.3 + push.0 + dup.2 dup.0 u32mod.16 dup.0 @@ -3443,8 +3445,8 @@ export."alloc::raw_vec::RawVec::try_allocate_in" u32div.4 movup.2 u32div.16 - exec.::intrinsics::mem::store_dw - push.0 + exec.::intrinsics::mem::store_sw + push.1 dup.1 dup.0 u32mod.16 @@ -3463,16 +3465,16 @@ export."alloc::raw_vec::RawVec::try_allocate_in" end -export."alloc::alloc::handle_alloc_error" - push.0 assert -end - +# mod miden::note -export."alloc::raw_vec::capacity_overflow" - push.0 assert +export.get_inputs + push.4294967295 + push.1 + push.0 + push.4294967295 + dup.4 + mem_storew + push.4 end -begin - exec.::abi_transform_tx_kernel_get_inputs_4::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.wat b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.wat index 756031a93..2231ccad3 100644 --- a/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.wat +++ b/tests/integration/expected/abi_transform_tx_kernel_get_inputs_4.wat @@ -6,7 +6,6 @@ (type (;4;) (func (param i32 i32 i32) (result i32))) (type (;5;) (func (param i32 i32 i32))) (type (;6;) (func (param i32 i32))) - (type (;7;) (func)) (import "miden::note" "get_inputs<0x0000000000000000000000000000000000000000000000000000000000000000>" (func $miden_tx_kernel_sys::externs::extern_note_get_inputs (;0;) (type 0))) (func $entrypoint (;1;) (type 1) (param i32) local.get 0 @@ -499,45 +498,37 @@ i32.const 0 call $alloc::raw_vec::RawVec::try_allocate_in local.get 1 - i32.load offset=12 - local.set 2 - local.get 1 i32.load offset=8 - local.set 3 + local.set 2 block ;; label = @1 - block ;; label = @2 - local.get 1 - i32.load offset=4 - i32.eqz - br_if 0 (;@2;) - local.get 3 - i32.eqz - br_if 1 (;@1;) - local.get 3 - local.get 2 - call $alloc::alloc::handle_alloc_error - unreachable - end - local.get 2 - call $miden_tx_kernel_sys::externs::extern_note_get_inputs - drop - local.get 0 - i32.const 0 - i32.store offset=8 - local.get 0 + local.get 1 + i32.load offset=4 + i32.eqz + br_if 0 (;@1;) local.get 2 - i32.store offset=4 - local.get 0 - local.get 3 - i32.store local.get 1 - i32.const 16 - i32.add - global.set $__stack_pointer - return + i32.load offset=12 + call $alloc::raw_vec::handle_error + unreachable end - call $alloc::raw_vec::capacity_overflow - unreachable + local.get 1 + i32.load offset=12 + local.tee 3 + call $miden_tx_kernel_sys::externs::extern_note_get_inputs + drop + local.get 0 + i32.const 0 + i32.store offset=8 + local.get 0 + local.get 3 + i32.store offset=4 + local.get 0 + local.get 2 + i32.store + local.get 1 + i32.const 16 + i32.add + global.set $__stack_pointer ) (func $alloc::raw_vec::RawVec::try_allocate_in (;9;) (type 5) (param i32 i32 i32) (local i32) @@ -613,11 +604,7 @@ local.get 1 i32.store ) - (func $alloc::alloc::handle_alloc_error (;10;) (type 6) (param i32 i32) - unreachable - unreachable - ) - (func $alloc::raw_vec::capacity_overflow (;11;) (type 7) + (func $alloc::raw_vec::handle_error (;10;) (type 6) (param i32 i32) unreachable unreachable ) diff --git a/tests/integration/expected/add_i16.hir b/tests/integration/expected/add_i16.hir index 14a40125f..ea5296370 100644 --- a/tests/integration/expected/add_i16.hir +++ b/tests/integration/expected/add_i16.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_bbf5fdc0851222e92e718cc0af28212663b61bc643024537b3041e4528ea338c + (module #test_rust_4a80dace0dddc4f08e0a7761b4e1d269aa474b6beb14702baa097a4626d593c1 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_i16.masm b/tests/integration/expected/add_i16.masm index b59823c56..a085c685c 100644 --- a/tests/integration/expected/add_i16.masm +++ b/tests/integration/expected/add_i16.masm @@ -1,10 +1,7 @@ -# mod test_rust_bbf5fdc0851222e92e718cc0af28212663b61bc643024537b3041e4528ea338c +# mod test_rust_4a80dace0dddc4f08e0a7761b4e1d269aa474b6beb14702baa097a4626d593c1 export.entrypoint u32wrapping_add end -begin - exec.::test_rust_bbf5fdc0851222e92e718cc0af28212663b61bc643024537b3041e4528ea338c::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_i16.wat b/tests/integration/expected/add_i16.wat index 6b360432a..7d5efb9e3 100644 --- a/tests/integration/expected/add_i16.wat +++ b/tests/integration/expected/add_i16.wat @@ -1,4 +1,4 @@ -(module $test_rust_bbf5fdc0851222e92e718cc0af28212663b61bc643024537b3041e4528ea338c.wasm +(module $test_rust_4a80dace0dddc4f08e0a7761b4e1d269aa474b6beb14702baa097a4626d593c1.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/add_i32.hir b/tests/integration/expected/add_i32.hir index 1d19342d9..6d7b67cf7 100644 --- a/tests/integration/expected/add_i32.hir +++ b/tests/integration/expected/add_i32.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_2e60443dc47eecaf44c0755d01375597814229ddd052f66a9a8f52569d4bee8b + (module #test_rust_cc3b19fe60136e21eb08ffee1b6d6f2a6534ca0afa46f10f5296cdb8f0adfc30 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_i32.masm b/tests/integration/expected/add_i32.masm index c81270d69..4e0d9cebc 100644 --- a/tests/integration/expected/add_i32.masm +++ b/tests/integration/expected/add_i32.masm @@ -1,10 +1,7 @@ -# mod test_rust_2e60443dc47eecaf44c0755d01375597814229ddd052f66a9a8f52569d4bee8b +# mod test_rust_cc3b19fe60136e21eb08ffee1b6d6f2a6534ca0afa46f10f5296cdb8f0adfc30 export.entrypoint u32wrapping_add end -begin - exec.::test_rust_2e60443dc47eecaf44c0755d01375597814229ddd052f66a9a8f52569d4bee8b::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_i32.wat b/tests/integration/expected/add_i32.wat index 94f7ac209..96d8d29ed 100644 --- a/tests/integration/expected/add_i32.wat +++ b/tests/integration/expected/add_i32.wat @@ -1,4 +1,4 @@ -(module $test_rust_2e60443dc47eecaf44c0755d01375597814229ddd052f66a9a8f52569d4bee8b.wasm +(module $test_rust_cc3b19fe60136e21eb08ffee1b6d6f2a6534ca0afa46f10f5296cdb8f0adfc30.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/add_i64.hir b/tests/integration/expected/add_i64.hir index eea25938b..ba44354e0 100644 --- a/tests/integration/expected/add_i64.hir +++ b/tests/integration/expected/add_i64.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_78c9e486e02020de38ab65bce982123aa9f38ccf330c940ad86e10e7e2912afa + (module #test_rust_069cf45252371f826e737bc3d7f808e1df77c97acac642efbaf976f4ab507131 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_i64.masm b/tests/integration/expected/add_i64.masm index 094a4b3fc..66e0fe2d9 100644 --- a/tests/integration/expected/add_i64.masm +++ b/tests/integration/expected/add_i64.masm @@ -1,10 +1,7 @@ -# mod test_rust_78c9e486e02020de38ab65bce982123aa9f38ccf330c940ad86e10e7e2912afa +# mod test_rust_069cf45252371f826e737bc3d7f808e1df77c97acac642efbaf976f4ab507131 export.entrypoint exec.::std::math::u64::wrapping_add end -begin - exec.::test_rust_78c9e486e02020de38ab65bce982123aa9f38ccf330c940ad86e10e7e2912afa::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_i64.wat b/tests/integration/expected/add_i64.wat index 11c52726c..20d0ba78d 100644 --- a/tests/integration/expected/add_i64.wat +++ b/tests/integration/expected/add_i64.wat @@ -1,4 +1,4 @@ -(module $test_rust_78c9e486e02020de38ab65bce982123aa9f38ccf330c940ad86e10e7e2912afa.wasm +(module $test_rust_069cf45252371f826e737bc3d7f808e1df77c97acac642efbaf976f4ab507131.wasm (type (;0;) (func (param i64 i64) (result i64))) (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) local.get 1 diff --git a/tests/integration/expected/add_i8.hir b/tests/integration/expected/add_i8.hir index 0a734b2a8..838a7f89f 100644 --- a/tests/integration/expected/add_i8.hir +++ b/tests/integration/expected/add_i8.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_aa4e8ec18774f7791634833f5a14b633e4c1a7d4ec8ae07ec95668a9f3fb05d6 + (module #test_rust_3dc5f4de1f29681a88ff9608b56c29738ebfd35b5bf875f151de489b1c7e50f7 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_i8.masm b/tests/integration/expected/add_i8.masm index 08a187ea7..73ca10240 100644 --- a/tests/integration/expected/add_i8.masm +++ b/tests/integration/expected/add_i8.masm @@ -1,10 +1,7 @@ -# mod test_rust_aa4e8ec18774f7791634833f5a14b633e4c1a7d4ec8ae07ec95668a9f3fb05d6 +# mod test_rust_3dc5f4de1f29681a88ff9608b56c29738ebfd35b5bf875f151de489b1c7e50f7 export.entrypoint u32wrapping_add end -begin - exec.::test_rust_aa4e8ec18774f7791634833f5a14b633e4c1a7d4ec8ae07ec95668a9f3fb05d6::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_i8.wat b/tests/integration/expected/add_i8.wat index 214f0a79d..7f101004a 100644 --- a/tests/integration/expected/add_i8.wat +++ b/tests/integration/expected/add_i8.wat @@ -1,4 +1,4 @@ -(module $test_rust_aa4e8ec18774f7791634833f5a14b633e4c1a7d4ec8ae07ec95668a9f3fb05d6.wasm +(module $test_rust_3dc5f4de1f29681a88ff9608b56c29738ebfd35b5bf875f151de489b1c7e50f7.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/add_u16.hir b/tests/integration/expected/add_u16.hir index 3f798df4c..dd4cfe0ca 100644 --- a/tests/integration/expected/add_u16.hir +++ b/tests/integration/expected/add_u16.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_10c9fc505476d6021538a9abdcf2c4d73bd1d8cc42c25993867649bb63b4319d + (module #test_rust_711c7705576a28225a7e87d297c54811b91eb1b69f3f407376a0af96dcad37b2 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_u16.masm b/tests/integration/expected/add_u16.masm index c0f945e05..5637b6e04 100644 --- a/tests/integration/expected/add_u16.masm +++ b/tests/integration/expected/add_u16.masm @@ -1,10 +1,7 @@ -# mod test_rust_10c9fc505476d6021538a9abdcf2c4d73bd1d8cc42c25993867649bb63b4319d +# mod test_rust_711c7705576a28225a7e87d297c54811b91eb1b69f3f407376a0af96dcad37b2 export.entrypoint u32wrapping_add push.65535 u32and end -begin - exec.::test_rust_10c9fc505476d6021538a9abdcf2c4d73bd1d8cc42c25993867649bb63b4319d::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_u16.wat b/tests/integration/expected/add_u16.wat index 7d9d4729b..28623e9e6 100644 --- a/tests/integration/expected/add_u16.wat +++ b/tests/integration/expected/add_u16.wat @@ -1,4 +1,4 @@ -(module $test_rust_10c9fc505476d6021538a9abdcf2c4d73bd1d8cc42c25993867649bb63b4319d.wasm +(module $test_rust_711c7705576a28225a7e87d297c54811b91eb1b69f3f407376a0af96dcad37b2.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/add_u32.hir b/tests/integration/expected/add_u32.hir index ee934d5ed..b70ef7da8 100644 --- a/tests/integration/expected/add_u32.hir +++ b/tests/integration/expected/add_u32.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_d6a5f6b2fb9509a2986e40a5ab057ae035f5eeb6fb66c1c0bd59648ee4c31a35 + (module #test_rust_ca3478b0c28e59b401b0d632fb0a1c51d0c45a319d503d2a2a705107e8aab84f ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_u32.masm b/tests/integration/expected/add_u32.masm index e48b0dcdd..612f5bd95 100644 --- a/tests/integration/expected/add_u32.masm +++ b/tests/integration/expected/add_u32.masm @@ -1,10 +1,7 @@ -# mod test_rust_d6a5f6b2fb9509a2986e40a5ab057ae035f5eeb6fb66c1c0bd59648ee4c31a35 +# mod test_rust_ca3478b0c28e59b401b0d632fb0a1c51d0c45a319d503d2a2a705107e8aab84f export.entrypoint u32wrapping_add end -begin - exec.::test_rust_d6a5f6b2fb9509a2986e40a5ab057ae035f5eeb6fb66c1c0bd59648ee4c31a35::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_u32.wat b/tests/integration/expected/add_u32.wat index 87c21d879..93f93487b 100644 --- a/tests/integration/expected/add_u32.wat +++ b/tests/integration/expected/add_u32.wat @@ -1,4 +1,4 @@ -(module $test_rust_d6a5f6b2fb9509a2986e40a5ab057ae035f5eeb6fb66c1c0bd59648ee4c31a35.wasm +(module $test_rust_ca3478b0c28e59b401b0d632fb0a1c51d0c45a319d503d2a2a705107e8aab84f.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/add_u64.hir b/tests/integration/expected/add_u64.hir index 5a954bcb2..7f2c3f6b2 100644 --- a/tests/integration/expected/add_u64.hir +++ b/tests/integration/expected/add_u64.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_27208cf7b2bc95c69612a6c3f988bc5cc944dda110c0f0bcdf4c2e97066d261f + (module #test_rust_11c5a2e5412edeeffd507e0b820658a62ad960143f3b5167b2fe215d1ecfecfa ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_u64.masm b/tests/integration/expected/add_u64.masm index 880387f3c..21d20476e 100644 --- a/tests/integration/expected/add_u64.masm +++ b/tests/integration/expected/add_u64.masm @@ -1,10 +1,7 @@ -# mod test_rust_27208cf7b2bc95c69612a6c3f988bc5cc944dda110c0f0bcdf4c2e97066d261f +# mod test_rust_11c5a2e5412edeeffd507e0b820658a62ad960143f3b5167b2fe215d1ecfecfa export.entrypoint exec.::std::math::u64::wrapping_add end -begin - exec.::test_rust_27208cf7b2bc95c69612a6c3f988bc5cc944dda110c0f0bcdf4c2e97066d261f::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_u64.wat b/tests/integration/expected/add_u64.wat index 00e305c8f..9ea5cbba4 100644 --- a/tests/integration/expected/add_u64.wat +++ b/tests/integration/expected/add_u64.wat @@ -1,4 +1,4 @@ -(module $test_rust_27208cf7b2bc95c69612a6c3f988bc5cc944dda110c0f0bcdf4c2e97066d261f.wasm +(module $test_rust_11c5a2e5412edeeffd507e0b820658a62ad960143f3b5167b2fe215d1ecfecfa.wasm (type (;0;) (func (param i64 i64) (result i64))) (func $entrypoint (;0;) (type 0) (param i64 i64) (result i64) local.get 1 diff --git a/tests/integration/expected/add_u8.hir b/tests/integration/expected/add_u8.hir index 55f61144f..6ec45e6ce 100644 --- a/tests/integration/expected/add_u8.hir +++ b/tests/integration/expected/add_u8.hir @@ -1,6 +1,6 @@ (component ;; Modules - (module #test_rust_4a87475b2f9d8d15ad35a776ff173dc1470f87cacd0a8312306b9896bb828aa3 + (module #test_rust_195424540a8740c46e8ef057ffe65c7d2c73576a03c615db26b6ef1ef00d0357 ;; Constants (const (id 0) 0x00100000) diff --git a/tests/integration/expected/add_u8.masm b/tests/integration/expected/add_u8.masm index 131a67065..4e7adf93a 100644 --- a/tests/integration/expected/add_u8.masm +++ b/tests/integration/expected/add_u8.masm @@ -1,10 +1,7 @@ -# mod test_rust_4a87475b2f9d8d15ad35a776ff173dc1470f87cacd0a8312306b9896bb828aa3 +# mod test_rust_195424540a8740c46e8ef057ffe65c7d2c73576a03c615db26b6ef1ef00d0357 export.entrypoint u32wrapping_add push.255 u32and end -begin - exec.::test_rust_4a87475b2f9d8d15ad35a776ff173dc1470f87cacd0a8312306b9896bb828aa3::entrypoint -end \ No newline at end of file diff --git a/tests/integration/expected/add_u8.wat b/tests/integration/expected/add_u8.wat index e5b13c342..fb425d3ed 100644 --- a/tests/integration/expected/add_u8.wat +++ b/tests/integration/expected/add_u8.wat @@ -1,4 +1,4 @@ -(module $test_rust_4a87475b2f9d8d15ad35a776ff173dc1470f87cacd0a8312306b9896bb828aa3.wasm +(module $test_rust_195424540a8740c46e8ef057ffe65c7d2c73576a03c615db26b6ef1ef00d0357.wasm (type (;0;) (func (param i32 i32) (result i32))) (func $entrypoint (;0;) (type 0) (param i32 i32) (result i32) local.get 1 diff --git a/tests/integration/expected/components/add_wasm_component.hir b/tests/integration/expected/components/add_wasm_component.hir index 9bd6a5a89..6e5bffd04 100644 --- a/tests/integration/expected/components/add_wasm_component.hir +++ b/tests/integration/expected/components/add_wasm_component.hir @@ -80,480 +80,496 @@ (assertz v6) (let (v7 (ptr i32)) (inttoptr v5)) (let (v8 i32) (load v7)) - (let (v9 i1) (eq v8 0)) - (let (v10 i32) (zext v9)) - (let (v11 i1) (neq v10 0)) - (condbr v11 (block 2) (block 3))) + (let (v9 i1) (neq v8 0)) + (condbr v9 (block 2) (block 3))) (block 1 (param v3 i32) (ret v3)) (block 2 - (let (v330 i32) (const.i32 0)) - (br (block 1 v330))) + (let (v11 i32) (const.i32 -1)) + (let (v12 i32) (add.wrapping v1 v11)) + (let (v13 i32) (const.i32 0)) + (let (v14 i32) (sub.wrapping v13 v1)) + (let (v15 i32) (const.i32 2)) + (let (v16 u32) (bitcast v15)) + (let (v17 i32) (shl.wrapping v0 v16)) + (br (block 4 v8 v2 v17 v14 v12))) (block 3 - (let (v12 i32) (const.i32 -1)) - (let (v13 i32) (add.wrapping v1 v12)) - (let (v14 i32) (const.i32 0)) - (let (v15 i32) (sub.wrapping v14 v1)) - (let (v16 i32) (const.i32 2)) - (let (v17 u32) (bitcast v16)) - (let (v18 i32) (shl.wrapping v0 v17)) - (br (block 4 v8 v2 v18 v15 v13))) + (let (v10 i32) (const.i32 0)) + (ret v10)) (block 4 - (param v19 i32) - (param v160 i32) - (param v171 i32) - (param v185 i32) + (param v18 i32) + (param v169 i32) + (param v182 i32) (param v197 i32) - (let (v20 i32) (const.i32 8)) - (let (v21 i32) (add.wrapping v19 v20)) - (let (v22 u32) (cast v19)) - (let (v23 u32) (add.checked v22 8)) - (let (v24 u32) (mod.unchecked v23 2)) - (assertz v24) - (let (v25 (ptr i32)) (inttoptr v23)) - (let (v26 i32) (load v25)) - (let (v27 i32) (const.i32 1)) - (let (v28 i32) (band v26 v27)) - (let (v29 i1) (neq v28 0)) - (condbr v29 (block 7) (block 8))) + (param v210 i32) + (let (v19 u32) (cast v18)) + (let (v20 u32) (add.checked v19 8)) + (let (v21 u32) (mod.unchecked v20 2)) + (assertz v21) + (let (v22 (ptr i32)) (inttoptr v20)) + (let (v23 i32) (load v22)) + (let (v24 i32) (const.i32 1)) + (let (v25 i32) (band v23 v24)) + (let (v26 i1) (neq v25 0)) + (condbr v26 (block 7) (block 8))) (block 5 - (br (block 2))) + (let (v344 i32) (const.i32 0)) + (br (block 1 v344))) (block 6 - (param v161 i32) - (param v168 i32) - (param v170 i32) - (param v184 i32) + (param v172 i32) + (param v179 i32) + (param v181 i32) (param v196 i32) - (param v204 i32) - (param v205 i32) - (let (v162 u32) (cast v161)) - (let (v163 u32) (mod.unchecked v162 2)) - (assertz v163) - (let (v164 (ptr i32)) (inttoptr v162)) - (let (v165 i32) (load v164)) - (let (v166 i32) (const.i32 -4)) - (let (v167 i32) (band v165 v166)) - (let (v169 i32) (sub.wrapping v167 v168)) - (let (v176 u32) (bitcast v169)) - (let (v177 u32) (bitcast v170)) - (let (v178 i1) (lt v176 v177)) - (let (v179 i32) (sext v178)) - (let (v180 i1) (neq v179 0)) - (condbr v180 (block 21 v204 v205 v170 v184 v196) (block 22))) + (param v209 i32) + (param v218 i32) + (param v219 i32) + (let (v173 u32) (cast v172)) + (let (v174 u32) (mod.unchecked v173 2)) + (assertz v174) + (let (v175 (ptr i32)) (inttoptr v173)) + (let (v176 i32) (load v175)) + (let (v177 i32) (const.i32 -4)) + (let (v178 i32) (band v176 v177)) + (let (v180 i32) (sub.wrapping v178 v179)) + (let (v188 u32) (bitcast v180)) + (let (v189 u32) (bitcast v181)) + (let (v190 i1) (lt v188 v189)) + (let (v191 i32) (sext v190)) + (let (v192 i1) (neq v191 0)) + (condbr v192 (block 22 v218 v219 v181 v196 v209) (block 23))) (block 7 - (br (block 9 v21 v26 v19 v160 v171 v185 v197))) + (br (block 9 v18 v23 v169 v182 v197 v210))) (block 8 - (br (block 6 v19 v21 v171 v185 v197 v160 v26))) + (let (v27 i32) (const.i32 8)) + (let (v28 i32) (add.wrapping v18 v27)) + (br (block 6 v18 v28 v182 v197 v210 v169 v23))) (block 9 + (param v29 i32) (param v30 i32) - (param v31 i32) - (param v37 i32) - (param v144 i32) - (param v174 i32) - (param v188 i32) - (param v200 i32) - (let (v32 i32) (const.i32 -2)) - (let (v33 i32) (band v31 v32)) - (let (v34 u32) (cast v30)) + (param v156 i32) + (param v187 i32) + (param v202 i32) + (param v215 i32) + (let (v31 i32) (const.i32 -2)) + (let (v32 i32) (band v30 v31)) + (let (v33 u32) (cast v29)) + (let (v34 u32) (add.checked v33 8)) (let (v35 u32) (mod.unchecked v34 2)) (assertz v35) (let (v36 (ptr i32)) (inttoptr v34)) - (store v36 v33) - (let (v38 u32) (cast v37)) - (let (v39 u32) (add.checked v38 4)) - (let (v40 u32) (mod.unchecked v39 2)) - (assertz v40) - (let (v41 (ptr i32)) (inttoptr v39)) - (let (v42 i32) (load v41)) - (let (v43 i32) (const.i32 -4)) - (let (v44 i32) (band v42 v43)) - (let (v45 u32) (cast v44)) - (let (v46 u32) (mod.unchecked v45 2)) - (assertz v46) - (let (v47 (ptr i32)) (inttoptr v45)) - (let (v48 i32) (load v47)) - (let (v49 u32) (cast v37)) - (let (v50 u32) (mod.unchecked v49 2)) - (assertz v50) - (let (v51 (ptr i32)) (inttoptr v49)) - (let (v52 i32) (load v51)) - (let (v53 i32) (const.i32 -4)) - (let (v54 i32) (band v52 v53)) - (let (v55 i1) (neq v54 0)) - (condbr v55 (block 13) (block 14))) + (store v36 v32) + (let (v37 u32) (cast v29)) + (let (v38 u32) (add.checked v37 4)) + (let (v39 u32) (mod.unchecked v38 2)) + (assertz v39) + (let (v40 (ptr i32)) (inttoptr v38)) + (let (v41 i32) (load v40)) + (let (v42 i32) (const.i32 -4)) + (let (v43 i32) (band v41 v42)) + (let (v44 i1) (neq v43 0)) + (condbr v44 (block 12) (block 13))) (block 10 - (br (block 6 v146 v151 v172 v186 v198 v142 v156))) + (let (v170 i32) (const.i32 8)) + (let (v171 i32) (add.wrapping v157 v170)) + (br (block 6 v157 v171 v183 v198 v211 v152 v165))) (block 11 - (param v112 i32) - (param v113 i32) - (param v120 i32) - (param v131 i32) - (param v143 i32) - (param v173 i32) - (param v187 i32) - (param v199 i32) - (let (v114 i32) (const.i32 3)) - (let (v115 i32) (band v113 v114)) - (let (v116 u32) (cast v112)) - (let (v117 u32) (add.checked v116 4)) - (let (v118 u32) (mod.unchecked v117 2)) - (assertz v118) - (let (v119 (ptr i32)) (inttoptr v117)) - (store v119 v115) - (let (v121 i32) (const.i32 3)) - (let (v122 i32) (band v120 v121)) - (let (v123 u32) (cast v112)) - (let (v124 u32) (mod.unchecked v123 2)) - (assertz v124) - (let (v125 (ptr i32)) (inttoptr v123)) - (store v125 v122) - (let (v126 i32) (const.i32 2)) - (let (v127 i32) (band v120 v126)) - (let (v128 i1) (eq v127 0)) - (let (v129 i32) (zext v128)) - (let (v130 i1) (neq v129 0)) - (condbr v130 (block 18 v143 v131 v173 v187 v199) (block 19))) + (param v55 i32) + (param v75 i32) + (param v122 i32) + (param v142 i32) + (param v155 i32) + (param v186 i32) + (param v201 i32) + (param v214 i32) + (let (v56 u32) (cast v55)) + (let (v57 u32) (mod.unchecked v56 2)) + (assertz v57) + (let (v58 (ptr i32)) (inttoptr v56)) + (let (v59 i32) (load v58)) + (let (v60 i32) (const.i32 -4)) + (let (v61 i32) (band v59 v60)) + (let (v62 i1) (eq v61 0)) + (let (v63 i32) (zext v62)) + (let (v64 i1) (neq v63 0)) + (condbr v64 (block 14 v75 v59 v55 v122 v142 v155 v186 v201 v214) (block 15))) (block 12 - (param v93 i32) - (param v94 i32) - (param v97 i32) - (param v102 i32) - (param v132 i32) - (param v145 i32) - (param v175 i32) - (param v189 i32) - (param v201 i32) - (let (v95 i32) (const.i32 3)) - (let (v96 i32) (band v94 v95)) - (let (v98 i32) (bor v96 v97)) - (let (v99 u32) (cast v93)) - (let (v100 u32) (mod.unchecked v99 2)) - (assertz v100) - (let (v101 (ptr i32)) (inttoptr v99)) - (store v101 v98) - (let (v103 u32) (cast v102)) - (let (v104 u32) (add.checked v103 4)) - (let (v105 u32) (mod.unchecked v104 2)) - (assertz v105) - (let (v106 (ptr i32)) (inttoptr v104)) - (let (v107 i32) (load v106)) - (let (v108 u32) (cast v102)) - (let (v109 u32) (mod.unchecked v108 2)) - (assertz v109) - (let (v110 (ptr i32)) (inttoptr v108)) - (let (v111 i32) (load v110)) - (br (block 11 v102 v107 v111 v132 v145 v175 v189 v201))) + (let (v46 i32) (const.i32 0)) + (let (v47 u32) (cast v43)) + (let (v48 (ptr u8)) (inttoptr v47)) + (let (v49 u8) (load v48)) + (let (v50 i32) (zext v49)) + (let (v51 i32) (const.i32 1)) + (let (v52 i32) (band v50 v51)) + (let (v53 i1) (neq v52 0)) + (let (v54 i32) (select v53 v46 v43)) + (br (block 11 v29 v43 v41 v54 v156 v187 v202 v215))) (block 13 - (let (v56 i32) (const.i32 2)) - (let (v57 i32) (band v52 v56)) - (let (v58 i1) (eq v57 0)) - (let (v59 i32) (zext v58)) - (let (v60 i1) (neq v59 0)) - (condbr v60 (block 15) (block 16))) + (let (v45 i32) (const.i32 0)) + (br (block 11 v29 v43 v41 v45 v156 v187 v202 v215))) (block 14 - (br (block 12 v44 v48 v54 v37 v44 v144 v174 v188 v200))) + (param v92 i32) + (param v102 i32) + (param v109 i32) + (param v121 i32) + (param v141 i32) + (param v154 i32) + (param v185 i32) + (param v200 i32) + (param v213 i32) + (let (v93 i1) (eq v92 0)) + (let (v94 i32) (zext v93)) + (let (v95 i1) (neq v94 0)) + (condbr v95 (block 17 v109 v121 v102 v141 v154 v185 v200 v213) (block 18))) (block 15 - (let (v61 u32) (cast v54)) - (let (v62 u32) (add.checked v61 4)) - (let (v63 u32) (mod.unchecked v62 2)) - (assertz v63) - (let (v64 (ptr i32)) (inttoptr v62)) - (let (v65 i32) (load v64)) - (let (v66 i32) (const.i32 3)) - (let (v67 i32) (band v65 v66)) - (let (v68 i32) (bor v67 v44)) - (let (v69 u32) (cast v54)) - (let (v70 u32) (add.checked v69 4)) - (let (v71 u32) (mod.unchecked v70 2)) - (assertz v71) - (let (v72 (ptr i32)) (inttoptr v70)) - (store v72 v68) - (let (v73 u32) (cast v37)) - (let (v74 u32) (mod.unchecked v73 2)) - (assertz v74) - (let (v75 (ptr i32)) (inttoptr v73)) - (let (v76 i32) (load v75)) - (let (v77 u32) (cast v37)) + (let (v65 i32) (const.i32 2)) + (let (v66 i32) (band v59 v65)) + (let (v67 i1) (neq v66 0)) + (condbr v67 (block 14 v75 v59 v55 v122 v142 v155 v186 v201 v214) (block 16))) + + (block 16 + (let (v68 u32) (cast v61)) + (let (v69 u32) (add.checked v68 4)) + (let (v70 u32) (mod.unchecked v69 2)) + (assertz v70) + (let (v71 (ptr i32)) (inttoptr v69)) + (let (v72 i32) (load v71)) + (let (v73 i32) (const.i32 3)) + (let (v74 i32) (band v72 v73)) + (let (v76 i32) (bor v74 v75)) + (let (v77 u32) (cast v61)) (let (v78 u32) (add.checked v77 4)) (let (v79 u32) (mod.unchecked v78 2)) (assertz v79) (let (v80 (ptr i32)) (inttoptr v78)) - (let (v81 i32) (load v80)) - (let (v82 i32) (const.i32 -4)) - (let (v83 i32) (band v81 v82)) - (let (v84 i1) (eq v83 0)) - (let (v85 i32) (zext v84)) - (let (v86 i1) (neq v85 0)) - (condbr v86 (block 11 v37 v81 v76 v44 v144 v174 v188 v200) (block 17))) - - (block 16 - (br (block 12 v44 v48 v54 v37 v44 v144 v174 v188 v200))) + (store v80 v76) + (let (v81 u32) (cast v55)) + (let (v82 u32) (add.checked v81 4)) + (let (v83 u32) (mod.unchecked v82 2)) + (assertz v83) + (let (v84 (ptr i32)) (inttoptr v82)) + (let (v85 i32) (load v84)) + (let (v86 i32) (const.i32 -4)) + (let (v87 i32) (band v85 v86)) + (let (v88 u32) (cast v55)) + (let (v89 u32) (mod.unchecked v88 2)) + (assertz v89) + (let (v90 (ptr i32)) (inttoptr v88)) + (let (v91 i32) (load v90)) + (br (block 14 v87 v91 v55 v85 v142 v155 v186 v201 v214))) (block 17 - (let (v87 i32) (const.i32 -4)) - (let (v88 i32) (band v76 v87)) - (let (v89 u32) (cast v83)) - (let (v90 u32) (mod.unchecked v89 2)) - (assertz v90) - (let (v91 (ptr i32)) (inttoptr v89)) - (let (v92 i32) (load v91)) - (br (block 12 v83 v92 v88 v37 v44 v144 v174 v188 v200))) + (param v119 i32) + (param v120 i32) + (param v129 i32) + (param v140 i32) + (param v153 i32) + (param v184 i32) + (param v199 i32) + (param v212 i32) + (let (v123 i32) (const.i32 3)) + (let (v124 i32) (band v120 v123)) + (let (v125 u32) (cast v119)) + (let (v126 u32) (add.checked v125 4)) + (let (v127 u32) (mod.unchecked v126 2)) + (assertz v127) + (let (v128 (ptr i32)) (inttoptr v126)) + (store v128 v124) + (let (v130 i32) (const.i32 3)) + (let (v131 i32) (band v129 v130)) + (let (v132 u32) (cast v119)) + (let (v133 u32) (mod.unchecked v132 2)) + (assertz v133) + (let (v134 (ptr i32)) (inttoptr v132)) + (store v134 v131) + (let (v135 i32) (const.i32 2)) + (let (v136 i32) (band v129 v135)) + (let (v137 i1) (eq v136 0)) + (let (v138 i32) (zext v137)) + (let (v139 i1) (neq v138 0)) + (condbr v139 (block 19 v153 v140 v184 v199 v212) (block 20))) (block 18 - (param v142 i32) - (param v146 i32) - (param v172 i32) - (param v186 i32) - (param v198 i32) - (let (v147 u32) (cast v142)) - (let (v148 u32) (mod.unchecked v147 2)) - (assertz v148) - (let (v149 (ptr i32)) (inttoptr v147)) - (store v149 v146) - (let (v150 i32) (const.i32 8)) - (let (v151 i32) (add.wrapping v146 v150)) - (let (v152 u32) (cast v146)) - (let (v153 u32) (add.checked v152 8)) - (let (v154 u32) (mod.unchecked v153 2)) - (assertz v154) - (let (v155 (ptr i32)) (inttoptr v153)) - (let (v156 i32) (load v155)) - (let (v157 i32) (const.i32 1)) - (let (v158 i32) (band v156 v157)) - (let (v159 i1) (neq v158 0)) - (condbr v159 (block 9 v151 v156 v146 v142 v172 v186 v198) (block 20))) + (let (v96 u32) (cast v92)) + (let (v97 u32) (mod.unchecked v96 2)) + (assertz v97) + (let (v98 (ptr i32)) (inttoptr v96)) + (let (v99 i32) (load v98)) + (let (v100 i32) (const.i32 3)) + (let (v101 i32) (band v99 v100)) + (let (v103 i32) (const.i32 -4)) + (let (v104 i32) (band v102 v103)) + (let (v105 i32) (bor v101 v104)) + (let (v106 u32) (cast v92)) + (let (v107 u32) (mod.unchecked v106 2)) + (assertz v107) + (let (v108 (ptr i32)) (inttoptr v106)) + (store v108 v105) + (let (v110 u32) (cast v109)) + (let (v111 u32) (add.checked v110 4)) + (let (v112 u32) (mod.unchecked v111 2)) + (assertz v112) + (let (v113 (ptr i32)) (inttoptr v111)) + (let (v114 i32) (load v113)) + (let (v115 u32) (cast v109)) + (let (v116 u32) (mod.unchecked v115 2)) + (assertz v116) + (let (v117 (ptr i32)) (inttoptr v115)) + (let (v118 i32) (load v117)) + (br (block 17 v109 v114 v118 v141 v154 v185 v200 v213))) (block 19 - (let (v133 u32) (cast v131)) - (let (v134 u32) (mod.unchecked v133 2)) - (assertz v134) - (let (v135 (ptr i32)) (inttoptr v133)) - (let (v136 i32) (load v135)) - (let (v137 i32) (const.i32 2)) - (let (v138 i32) (bor v136 v137)) - (let (v139 u32) (cast v131)) - (let (v140 u32) (mod.unchecked v139 2)) - (assertz v140) - (let (v141 (ptr i32)) (inttoptr v139)) - (store v141 v138) - (br (block 18 v143 v131 v173 v187 v199))) + (param v152 i32) + (param v157 i32) + (param v183 i32) + (param v198 i32) + (param v211 i32) + (let (v158 u32) (cast v152)) + (let (v159 u32) (mod.unchecked v158 2)) + (assertz v159) + (let (v160 (ptr i32)) (inttoptr v158)) + (store v160 v157) + (let (v161 u32) (cast v157)) + (let (v162 u32) (add.checked v161 8)) + (let (v163 u32) (mod.unchecked v162 2)) + (assertz v163) + (let (v164 (ptr i32)) (inttoptr v162)) + (let (v165 i32) (load v164)) + (let (v166 i32) (const.i32 1)) + (let (v167 i32) (band v165 v166)) + (let (v168 i1) (neq v167 0)) + (condbr v168 (block 9 v157 v165 v152 v183 v198 v211) (block 21))) (block 20 - (br (block 10))) + (let (v143 u32) (cast v140)) + (let (v144 u32) (mod.unchecked v143 2)) + (assertz v144) + (let (v145 (ptr i32)) (inttoptr v143)) + (let (v146 i32) (load v145)) + (let (v147 i32) (const.i32 2)) + (let (v148 i32) (bor v146 v147)) + (let (v149 u32) (cast v140)) + (let (v150 u32) (mod.unchecked v149 2)) + (assertz v150) + (let (v151 (ptr i32)) (inttoptr v149)) + (store v151 v148) + (br (block 19 v153 v140 v184 v199 v212))) (block 21 - (param v321 i32) - (param v322 i32) - (param v327 i32) - (param v328 i32) - (param v329 i32) - (let (v323 u32) (cast v321)) - (let (v324 u32) (mod.unchecked v323 2)) - (assertz v324) - (let (v325 (ptr i32)) (inttoptr v323)) - (store v325 v322) - (let (v326 i1) (neq v322 0)) - (condbr v326 (block 4 v322 v321 v327 v328 v329) (block 32))) + (br (block 10))) (block 22 - (let (v181 i32) (const.i32 72)) - (let (v182 i32) (add.wrapping v168 v181)) - (let (v183 i32) (sub.wrapping v167 v170)) - (let (v190 i32) (band v183 v184)) - (let (v191 u32) (bitcast v182)) - (let (v192 u32) (bitcast v190)) - (let (v193 i1) (lte v191 v192)) - (let (v194 i32) (sext v193)) - (let (v195 i1) (neq v194 0)) - (condbr v195 (block 24) (block 25))) - - (block 23 (param v312 i32) (param v313 i32) - (let (v314 i32) (const.i32 1)) - (let (v315 i32) (bor v313 v314)) - (let (v316 u32) (cast v312)) - (let (v317 u32) (mod.unchecked v316 2)) - (assertz v317) - (let (v318 (ptr i32)) (inttoptr v316)) - (store v318 v315) - (let (v319 i32) (const.i32 8)) - (let (v320 i32) (add.wrapping v312 v319)) - (ret v320)) - - (block 24 - (let (v215 i32) (const.i32 0)) - (let (v216 i32) (const.i32 0)) - (let (v217 u32) (cast v190)) - (let (v218 u32) (mod.unchecked v217 2)) - (assertz v218) - (let (v219 (ptr i32)) (inttoptr v217)) - (store v219 v216) - (let (v220 i32) (const.i32 -8)) - (let (v221 i32) (add.wrapping v190 v220)) - (let (v222 i64) (const.i64 0)) - (let (v223 u32) (cast v221)) - (let (v224 u32) (mod.unchecked v223 2)) - (assertz v224) - (let (v225 (ptr i64)) (inttoptr v223)) - (store v225 v222) - (let (v226 u32) (cast v161)) - (let (v227 u32) (mod.unchecked v226 2)) - (assertz v227) - (let (v228 (ptr i32)) (inttoptr v226)) - (let (v229 i32) (load v228)) - (let (v230 i32) (const.i32 -4)) - (let (v231 i32) (band v229 v230)) - (let (v232 u32) (cast v221)) - (let (v233 u32) (mod.unchecked v232 2)) - (assertz v233) - (let (v234 (ptr i32)) (inttoptr v232)) - (store v234 v231) - (let (v235 u32) (cast v161)) - (let (v236 u32) (mod.unchecked v235 2)) - (assertz v236) - (let (v237 (ptr i32)) (inttoptr v235)) - (let (v238 i32) (load v237)) - (let (v239 i32) (const.i32 -4)) - (let (v240 i32) (band v238 v239)) - (let (v241 i1) (eq v240 0)) - (let (v242 i32) (zext v241)) - (let (v243 i1) (neq v242 0)) - (condbr v243 (block 27 v221 v215 v161 v168) (block 28))) + (param v335 i32) + (param v336 i32) + (param v341 i32) + (param v342 i32) + (param v343 i32) + (let (v337 u32) (cast v335)) + (let (v338 u32) (mod.unchecked v337 2)) + (assertz v338) + (let (v339 (ptr i32)) (inttoptr v337)) + (store v339 v336) + (let (v340 i1) (neq v336 0)) + (condbr v340 (block 4 v336 v335 v341 v342 v343) (block 33))) + + (block 23 + (let (v193 i32) (const.i32 72)) + (let (v194 i32) (add.wrapping v179 v193)) + (let (v195 i32) (sub.wrapping v178 v181)) + (let (v203 i32) (band v195 v196)) + (let (v204 u32) (bitcast v194)) + (let (v205 u32) (bitcast v203)) + (let (v206 i1) (lte v204 v205)) + (let (v207 i32) (sext v206)) + (let (v208 i1) (neq v207 0)) + (condbr v208 (block 25) (block 26))) + + (block 24 (param v326 i32) (param v327 i32) + (let (v328 i32) (const.i32 1)) + (let (v329 i32) (bor v327 v328)) + (let (v330 u32) (cast v326)) + (let (v331 u32) (mod.unchecked v330 2)) + (assertz v331) + (let (v332 (ptr i32)) (inttoptr v330)) + (store v332 v329) + (let (v333 i32) (const.i32 8)) + (let (v334 i32) (add.wrapping v326 v333)) + (ret v334)) (block 25 - (let (v202 i32) (band v196 v168)) - (let (v203 i1) (neq v202 0)) - (condbr v203 (block 21 v204 v205 v170 v184 v196) (block 26))) + (let (v229 i32) (const.i32 0)) + (let (v230 i32) (const.i32 0)) + (let (v231 u32) (cast v203)) + (let (v232 u32) (mod.unchecked v231 2)) + (assertz v232) + (let (v233 (ptr i32)) (inttoptr v231)) + (store v233 v230) + (let (v234 i32) (const.i32 -8)) + (let (v235 i32) (add.wrapping v203 v234)) + (let (v236 i64) (const.i64 0)) + (let (v237 u32) (cast v235)) + (let (v238 u32) (mod.unchecked v237 2)) + (assertz v238) + (let (v239 (ptr i64)) (inttoptr v237)) + (store v239 v236) + (let (v240 u32) (cast v172)) + (let (v241 u32) (mod.unchecked v240 2)) + (assertz v241) + (let (v242 (ptr i32)) (inttoptr v240)) + (let (v243 i32) (load v242)) + (let (v244 i32) (const.i32 -4)) + (let (v245 i32) (band v243 v244)) + (let (v246 u32) (cast v235)) + (let (v247 u32) (mod.unchecked v246 2)) + (assertz v247) + (let (v248 (ptr i32)) (inttoptr v246)) + (store v248 v245) + (let (v249 u32) (cast v172)) + (let (v250 u32) (mod.unchecked v249 2)) + (assertz v250) + (let (v251 (ptr i32)) (inttoptr v249)) + (let (v252 i32) (load v251)) + (let (v253 i32) (const.i32 -4)) + (let (v254 i32) (band v252 v253)) + (let (v255 i1) (eq v254 0)) + (let (v256 i32) (zext v255)) + (let (v257 i1) (neq v256 0)) + (condbr v257 (block 28 v235 v229 v172 v179) (block 29))) (block 26 - (let (v206 i32) (const.i32 -4)) - (let (v207 i32) (band v205 v206)) - (let (v208 u32) (cast v204)) - (let (v209 u32) (mod.unchecked v208 2)) - (assertz v209) - (let (v210 (ptr i32)) (inttoptr v208)) - (store v210 v207) - (let (v211 u32) (cast v161)) - (let (v212 u32) (mod.unchecked v211 2)) - (assertz v212) - (let (v213 (ptr i32)) (inttoptr v211)) - (let (v214 i32) (load v213)) - (br (block 23 v161 v214))) + (let (v216 i32) (band v209 v179)) + (let (v217 i1) (neq v216 0)) + (condbr v217 (block 22 v218 v219 v181 v196 v209) (block 27))) (block 27 - (param v266 i32) - (param v267 i32) - (param v268 i32) - (param v274 i32) - (let (v269 i32) (bor v267 v268)) - (let (v270 u32) (cast v266)) - (let (v271 u32) (add.checked v270 4)) - (let (v272 u32) (mod.unchecked v271 2)) - (assertz v272) - (let (v273 (ptr i32)) (inttoptr v271)) - (store v273 v269) - (let (v275 u32) (cast v274)) - (let (v276 u32) (mod.unchecked v275 2)) - (assertz v276) - (let (v277 (ptr i32)) (inttoptr v275)) - (let (v278 i32) (load v277)) - (let (v279 i32) (const.i32 -2)) - (let (v280 i32) (band v278 v279)) - (let (v281 u32) (cast v274)) - (let (v282 u32) (mod.unchecked v281 2)) - (assertz v282) - (let (v283 (ptr i32)) (inttoptr v281)) - (store v283 v280) - (let (v284 u32) (cast v268)) - (let (v285 u32) (mod.unchecked v284 2)) - (assertz v285) - (let (v286 (ptr i32)) (inttoptr v284)) - (let (v287 i32) (load v286)) - (let (v288 i32) (const.i32 3)) - (let (v289 i32) (band v287 v288)) - (let (v290 i32) (bor v289 v266)) - (let (v291 u32) (cast v268)) - (let (v292 u32) (mod.unchecked v291 2)) - (assertz v292) - (let (v293 (ptr i32)) (inttoptr v291)) - (store v293 v290) - (let (v294 i32) (const.i32 2)) - (let (v295 i32) (band v287 v294)) - (let (v296 i1) (neq v295 0)) - (condbr v296 (block 30) (block 31))) + (let (v220 i32) (const.i32 -4)) + (let (v221 i32) (band v219 v220)) + (let (v222 u32) (cast v218)) + (let (v223 u32) (mod.unchecked v222 2)) + (assertz v223) + (let (v224 (ptr i32)) (inttoptr v222)) + (store v224 v221) + (let (v225 u32) (cast v172)) + (let (v226 u32) (mod.unchecked v225 2)) + (assertz v226) + (let (v227 (ptr i32)) (inttoptr v225)) + (let (v228 i32) (load v227)) + (br (block 24 v172 v228))) (block 28 - (let (v244 i32) (const.i32 2)) - (let (v245 i32) (band v238 v244)) - (let (v246 i1) (neq v245 0)) - (condbr v246 (block 27 v221 v215 v161 v168) (block 29))) + (param v280 i32) + (param v281 i32) + (param v282 i32) + (param v288 i32) + (let (v283 i32) (bor v281 v282)) + (let (v284 u32) (cast v280)) + (let (v285 u32) (add.checked v284 4)) + (let (v286 u32) (mod.unchecked v285 2)) + (assertz v286) + (let (v287 (ptr i32)) (inttoptr v285)) + (store v287 v283) + (let (v289 u32) (cast v288)) + (let (v290 u32) (mod.unchecked v289 2)) + (assertz v290) + (let (v291 (ptr i32)) (inttoptr v289)) + (let (v292 i32) (load v291)) + (let (v293 i32) (const.i32 -2)) + (let (v294 i32) (band v292 v293)) + (let (v295 u32) (cast v288)) + (let (v296 u32) (mod.unchecked v295 2)) + (assertz v296) + (let (v297 (ptr i32)) (inttoptr v295)) + (store v297 v294) + (let (v298 u32) (cast v282)) + (let (v299 u32) (mod.unchecked v298 2)) + (assertz v299) + (let (v300 (ptr i32)) (inttoptr v298)) + (let (v301 i32) (load v300)) + (let (v302 i32) (const.i32 3)) + (let (v303 i32) (band v301 v302)) + (let (v304 i32) (bor v303 v280)) + (let (v305 u32) (cast v282)) + (let (v306 u32) (mod.unchecked v305 2)) + (assertz v306) + (let (v307 (ptr i32)) (inttoptr v305)) + (store v307 v304) + (let (v308 i32) (const.i32 2)) + (let (v309 i32) (band v301 v308)) + (let (v310 i1) (neq v309 0)) + (condbr v310 (block 31) (block 32))) (block 29 - (let (v247 u32) (cast v240)) - (let (v248 u32) (add.checked v247 4)) - (let (v249 u32) (mod.unchecked v248 2)) - (assertz v249) - (let (v250 (ptr i32)) (inttoptr v248)) - (let (v251 i32) (load v250)) - (let (v252 i32) (const.i32 3)) - (let (v253 i32) (band v251 v252)) - (let (v254 i32) (bor v253 v221)) - (let (v255 u32) (cast v240)) - (let (v256 u32) (add.checked v255 4)) - (let (v257 u32) (mod.unchecked v256 2)) - (assertz v257) - (let (v258 (ptr i32)) (inttoptr v256)) - (store v258 v254) - (let (v259 u32) (cast v221)) - (let (v260 u32) (add.checked v259 4)) - (let (v261 u32) (mod.unchecked v260 2)) - (assertz v261) - (let (v262 (ptr i32)) (inttoptr v260)) - (let (v263 i32) (load v262)) - (let (v264 i32) (const.i32 3)) - (let (v265 i32) (band v263 v264)) - (br (block 27 v221 v265 v161 v168))) + (let (v258 i32) (const.i32 2)) + (let (v259 i32) (band v252 v258)) + (let (v260 i1) (neq v259 0)) + (condbr v260 (block 28 v235 v229 v172 v179) (block 30))) (block 30 - (let (v301 i32) (const.i32 -3)) - (let (v302 i32) (band v290 v301)) - (let (v303 u32) (cast v268)) - (let (v304 u32) (mod.unchecked v303 2)) - (assertz v304) - (let (v305 (ptr i32)) (inttoptr v303)) - (store v305 v302) - (let (v306 u32) (cast v266)) - (let (v307 u32) (mod.unchecked v306 2)) - (assertz v307) - (let (v308 (ptr i32)) (inttoptr v306)) - (let (v309 i32) (load v308)) - (let (v310 i32) (const.i32 2)) - (let (v311 i32) (bor v309 v310)) - (br (block 23 v266 v311))) + (let (v261 u32) (cast v254)) + (let (v262 u32) (add.checked v261 4)) + (let (v263 u32) (mod.unchecked v262 2)) + (assertz v263) + (let (v264 (ptr i32)) (inttoptr v262)) + (let (v265 i32) (load v264)) + (let (v266 i32) (const.i32 3)) + (let (v267 i32) (band v265 v266)) + (let (v268 i32) (bor v267 v235)) + (let (v269 u32) (cast v254)) + (let (v270 u32) (add.checked v269 4)) + (let (v271 u32) (mod.unchecked v270 2)) + (assertz v271) + (let (v272 (ptr i32)) (inttoptr v270)) + (store v272 v268) + (let (v273 u32) (cast v235)) + (let (v274 u32) (add.checked v273 4)) + (let (v275 u32) (mod.unchecked v274 2)) + (assertz v275) + (let (v276 (ptr i32)) (inttoptr v274)) + (let (v277 i32) (load v276)) + (let (v278 i32) (const.i32 3)) + (let (v279 i32) (band v277 v278)) + (br (block 28 v235 v279 v172 v179))) (block 31 - (let (v297 u32) (cast v266)) - (let (v298 u32) (mod.unchecked v297 2)) - (assertz v298) - (let (v299 (ptr i32)) (inttoptr v297)) - (let (v300 i32) (load v299)) - (br (block 23 v266 v300))) + (let (v315 i32) (const.i32 -3)) + (let (v316 i32) (band v304 v315)) + (let (v317 u32) (cast v282)) + (let (v318 u32) (mod.unchecked v317 2)) + (assertz v318) + (let (v319 (ptr i32)) (inttoptr v317)) + (store v319 v316) + (let (v320 u32) (cast v280)) + (let (v321 u32) (mod.unchecked v320 2)) + (assertz v321) + (let (v322 (ptr i32)) (inttoptr v320)) + (let (v323 i32) (load v322)) + (let (v324 i32) (const.i32 2)) + (let (v325 i32) (bor v323 v324)) + (br (block 24 v280 v325))) (block 32 + (let (v311 u32) (cast v280)) + (let (v312 u32) (mod.unchecked v311 2)) + (assertz v312) + (let (v313 (ptr i32)) (inttoptr v311)) + (let (v314 i32) (load v313)) + (br (block 24 v280 v314))) + + (block 33 (br (block 5))) ) diff --git a/tests/integration/expected/components/add_wasm_component.wat b/tests/integration/expected/components/add_wasm_component.wat index c9358ef50..d40961d7b 100644 --- a/tests/integration/expected/components/add_wasm_component.wat +++ b/tests/integration/expected/components/add_wasm_component.wat @@ -1,694 +1,678 @@ -(component - (core module (;0;) - (type (;0;) (func)) - (type (;1;) (func (param i32 i32) (result i32))) - (type (;2;) (func (param i32 i32 i32 i32) (result i32))) - (type (;3;) (func (param i32 i32 i32) (result i32))) - (type (;4;) (func (param i32 i32 i32 i32))) - (func $__wasm_call_ctors (;0;) (type 0)) - (func $miden:add-package/add-interface@1.0.0#add (;1;) (type 1) (param i32 i32) (result i32) - call $wit_bindgen::rt::run_ctors_once - local.get 1 - local.get 0 - i32.add - ) - (func $__rust_alloc (;2;) (type 1) (param i32 i32) (result i32) +(module $add_wasm_component.wasm + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (type (;2;) (func (param i32 i32 i32 i32) (result i32))) + (type (;3;) (func (param i32 i32 i32) (result i32))) + (type (;4;) (func (param i32 i32 i32 i32))) + (func $__wasm_call_ctors (;0;) (type 0)) + (func $miden:add-package/add-interface@1.0.0#add (;1;) (type 1) (param i32 i32) (result i32) + call $wit_bindgen::rt::run_ctors_once + local.get 1 + local.get 0 + i32.add + ) + (func $__rust_alloc (;2;) (type 1) (param i32 i32) (result i32) + i32.const 1048576 + local.get 1 + local.get 0 + call $::alloc + ) + (func $__rust_realloc (;3;) (type 2) (param i32 i32 i32 i32) (result i32) + (local i32) + block ;; label = @1 i32.const 1048576 - local.get 1 - local.get 0 + local.get 2 + local.get 3 call $::alloc - ) - (func $__rust_realloc (;3;) (type 2) (param i32 i32 i32 i32) (result i32) - (local i32) - block ;; label = @1 - i32.const 1048576 - local.get 2 - local.get 3 - call $::alloc - local.tee 4 - i32.eqz - br_if 0 (;@1;) - local.get 4 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - memory.copy - i32.const 1048576 - local.get 0 - local.get 2 - local.get 1 - call $::dealloc - end + local.tee 4 + i32.eqz + br_if 0 (;@1;) local.get 4 - ) - (func $wee_alloc::alloc_first_fit (;4;) (type 3) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 2 - i32.load - local.tee 3 - i32.eqz - br_if 0 (;@1;) - local.get 1 - i32.const -1 - i32.add - local.set 4 - i32.const 0 - local.get 1 - i32.sub - local.set 5 - local.get 0 - i32.const 2 - i32.shl - local.set 6 - loop ;; label = @2 + local.get 0 + local.get 1 + local.get 3 + local.get 1 + local.get 3 + i32.lt_u + select + memory.copy + i32.const 1048576 + local.get 0 + local.get 2 + local.get 1 + call $::dealloc + end + local.get 4 + ) + (func $wee_alloc::alloc_first_fit (;4;) (type 3) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + local.get 2 + i32.load + local.tee 3 + br_if 0 (;@1;) + i32.const 0 + return + end + local.get 1 + i32.const -1 + i32.add + local.set 4 + i32.const 0 + local.get 1 + i32.sub + local.set 5 + local.get 0 + i32.const 2 + i32.shl + local.set 6 + loop ;; label = @1 + block ;; label = @2 + block ;; label = @3 + local.get 3 + i32.load offset=8 + local.tee 1 + i32.const 1 + i32.and + br_if 0 (;@3;) local.get 3 i32.const 8 i32.add - local.set 7 - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.load offset=8 - local.tee 0 - i32.const 1 - i32.and - br_if 0 (;@4;) - local.get 3 - local.set 1 - br 1 (;@3;) - end - loop ;; label = @4 - local.get 7 - local.get 0 - i32.const -2 - i32.and - i32.store + local.set 0 + br 1 (;@2;) + end + loop ;; label = @3 + local.get 3 + local.get 1 + i32.const -2 + i32.and + i32.store offset=8 + block ;; label = @4 + block ;; label = @5 local.get 3 i32.load offset=4 + local.tee 7 i32.const -4 i32.and - local.tee 1 - i32.load - local.set 7 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.load - local.tee 8 - i32.const -4 - i32.and - local.tee 0 - br_if 0 (;@7;) - local.get 1 - local.set 8 - br 1 (;@6;) - end - block ;; label = @7 - local.get 8 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@7;) - local.get 1 - local.set 8 - br 1 (;@6;) - end - local.get 0 - local.get 0 - i32.load offset=4 - i32.const 3 - i32.and - local.get 1 - i32.or - i32.store offset=4 - local.get 3 - i32.load - local.set 0 - local.get 3 - i32.load offset=4 - local.tee 7 - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 1 (;@5;) - local.get 0 - i32.const -4 - i32.and - local.set 0 - local.get 8 - i32.load - local.set 7 - end - local.get 8 - local.get 7 - i32.const 3 - i32.and - local.get 0 - i32.or - i32.store - local.get 3 - i32.load offset=4 - local.set 7 - local.get 3 - i32.load - local.set 0 - end - local.get 3 - local.get 7 - i32.const 3 - i32.and - i32.store offset=4 - local.get 3 - local.get 0 - i32.const 3 - i32.and - i32.store - block ;; label = @5 - local.get 0 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@5;) - local.get 1 - local.get 1 - i32.load - i32.const 2 - i32.or - i32.store - end - local.get 2 - local.get 1 - i32.store - local.get 1 - i32.const 8 - i32.add - local.set 7 - local.get 1 - local.set 3 - local.get 1 - i32.load offset=8 local.tee 0 - i32.const 1 - i32.and - br_if 0 (;@4;) + br_if 0 (;@5;) + i32.const 0 + local.set 8 + br 1 (;@4;) end + i32.const 0 + local.get 0 + local.get 0 + i32.load8_u + i32.const 1 + i32.and + select + local.set 8 end - block ;; label = @3 - local.get 1 + block ;; label = @4 + local.get 3 i32.load + local.tee 1 i32.const -4 i32.and - local.tee 3 - local.get 7 - i32.sub - local.get 6 - i32.lt_u - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.const 72 - i32.add - local.get 3 - local.get 6 - i32.sub - local.get 5 - i32.and - local.tee 3 - i32.le_u - br_if 0 (;@5;) - local.get 4 - local.get 7 - i32.and - br_if 2 (;@3;) - local.get 2 - local.get 0 - i32.const -4 - i32.and - i32.store - local.get 1 - i32.load - local.set 0 - local.get 1 - local.set 3 - br 1 (;@4;) - end - i32.const 0 - local.set 0 - local.get 3 - i32.const 0 - i32.store - local.get 3 - i32.const -8 - i32.add - local.tee 3 - i64.const 0 - i64.store align=4 - local.get 3 - local.get 1 - i32.load - i32.const -4 - i32.and - i32.store - block ;; label = @5 - local.get 1 - i32.load - local.tee 2 - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 0 (;@5;) - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@5;) - local.get 8 - local.get 8 - i32.load offset=4 - i32.const 3 - i32.and - local.get 3 - i32.or - i32.store offset=4 - local.get 3 - i32.load offset=4 - i32.const 3 - i32.and - local.set 0 - end - local.get 3 - local.get 0 - local.get 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load - i32.const -2 - i32.and - i32.store - local.get 1 - local.get 1 - i32.load - local.tee 0 - i32.const 3 - i32.and - local.get 3 - i32.or - local.tee 7 - i32.store - block ;; label = @5 - local.get 0 - i32.const 2 - i32.and - br_if 0 (;@5;) - local.get 3 - i32.load - local.set 0 - br 1 (;@4;) - end - local.get 1 - local.get 7 - i32.const -3 - i32.and - i32.store - local.get 3 - i32.load - i32.const 2 - i32.or - local.set 0 - end + local.tee 9 + i32.eqz + br_if 0 (;@4;) + local.get 1 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 9 + local.get 9 + i32.load offset=4 + i32.const 3 + i32.and + local.get 0 + i32.or + i32.store offset=4 + local.get 3 + i32.load offset=4 + local.tee 7 + i32.const -4 + i32.and + local.set 0 local.get 3 + i32.load + local.set 1 + end + block ;; label = @4 local.get 0 - i32.const 1 + i32.eqz + br_if 0 (;@4;) + local.get 0 + local.get 0 + i32.load + i32.const 3 + i32.and + local.get 1 + i32.const -4 + i32.and i32.or i32.store local.get 3 - i32.const 8 - i32.add - return + i32.load offset=4 + local.set 7 + local.get 3 + i32.load + local.set 1 + end + local.get 3 + local.get 7 + i32.const 3 + i32.and + i32.store offset=4 + local.get 3 + local.get 1 + i32.const 3 + i32.and + i32.store + block ;; label = @4 + local.get 1 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@4;) + local.get 8 + local.get 8 + i32.load + i32.const 2 + i32.or + i32.store end local.get 2 - local.get 0 + local.get 8 i32.store - local.get 0 + local.get 8 local.set 3 - local.get 0 - br_if 0 (;@2;) + local.get 8 + i32.load offset=8 + local.tee 1 + i32.const 1 + i32.and + br_if 0 (;@3;) end + local.get 8 + i32.const 8 + i32.add + local.set 0 + local.get 8 + local.set 3 end - i32.const 0 - ) - (func $::alloc (;5;) (type 3) (param i32 i32 i32) (result i32) - (local i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 3 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - local.get 2 - br_if 0 (;@2;) - local.get 1 - local.set 2 - br 1 (;@1;) - end + block ;; label = @2 local.get 3 - local.get 0 i32.load - i32.store offset=12 - block ;; label = @2 - local.get 2 - i32.const 3 + i32.const -4 + i32.and + local.tee 8 + local.get 0 + i32.sub + local.get 6 + i32.lt_u + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + local.get 0 + i32.const 72 + i32.add + local.get 8 + local.get 6 + i32.sub + local.get 5 + i32.and + local.tee 8 + i32.le_u + br_if 0 (;@4;) + local.get 4 + local.get 0 + i32.and + br_if 2 (;@2;) + local.get 2 + local.get 1 + i32.const -4 + i32.and + i32.store + local.get 3 + i32.load + local.set 0 + local.get 3 + local.set 1 + br 1 (;@3;) + end + i32.const 0 + local.set 7 + local.get 8 + i32.const 0 + i32.store + local.get 8 + i32.const -8 i32.add - local.tee 4 - i32.const 2 - i32.shr_u - local.tee 5 + local.tee 1 + i64.const 0 + i64.store align=4 local.get 1 - local.get 3 - i32.const 12 - i32.add - call $wee_alloc::alloc_first_fit - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - local.get 4 + local.get 3 + i32.load + i32.const -4 + i32.and + i32.store + block ;; label = @4 + local.get 3 + i32.load + local.tee 9 i32.const -4 i32.and - local.tee 2 + local.tee 8 + i32.eqz + br_if 0 (;@4;) + local.get 9 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 8 + local.get 8 + i32.load offset=4 + i32.const 3 + i32.and + local.get 1 + i32.or + i32.store offset=4 local.get 1 + i32.load offset=4 i32.const 3 - i32.shl - i32.const 512 - i32.add - local.tee 4 - local.get 2 - local.get 4 - i32.gt_u - select - i32.const 65543 - i32.add - local.tee 4 - i32.const 16 - i32.shr_u - memory.grow - local.tee 2 - i32.const -1 - i32.ne - br_if 0 (;@3;) - i32.const 0 - local.set 2 - br 1 (;@2;) + i32.and + local.set 7 end - local.get 2 - i32.const 16 - i32.shl - local.tee 2 - i32.const 0 + local.get 1 + local.get 7 + local.get 3 + i32.or i32.store offset=4 - local.get 2 + local.get 0 + local.get 0 + i32.load + i32.const -2 + i32.and + i32.store local.get 3 - i32.load offset=12 - i32.store offset=8 - local.get 2 - local.get 2 - local.get 4 - i32.const -65536 + local.get 3 + i32.load + local.tee 0 + i32.const 3 i32.and - i32.add - i32.const 2 + local.get 1 i32.or + local.tee 8 i32.store + block ;; label = @4 + local.get 0 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 1 + i32.load + local.set 0 + br 1 (;@3;) + end local.get 3 - local.get 2 - i32.store offset=12 - local.get 5 + local.get 8 + i32.const -3 + i32.and + i32.store local.get 1 - local.get 3 - i32.const 12 - i32.add - call $wee_alloc::alloc_first_fit - local.set 2 + i32.load + i32.const 2 + i32.or + local.set 0 end + local.get 1 local.get 0 - local.get 3 - i32.load offset=12 + i32.const 1 + i32.or i32.store + local.get 1 + i32.const 8 + i32.add + return end - local.get 3 - i32.const 16 - i32.add - global.set $__stack_pointer local.get 2 - ) - (func $::dealloc (;6;) (type 4) (param i32 i32 i32 i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.load - local.set 4 + local.get 1 + i32.store + local.get 1 + local.set 3 + local.get 1 + br_if 0 (;@1;) + end + i32.const 0 + ) + (func $::alloc (;5;) (type 3) (param i32 i32 i32) (result i32) + (local i32 i32 i32) + global.get $__stack_pointer + i32.const 16 + i32.sub + local.tee 3 + global.set $__stack_pointer + block ;; label = @1 + block ;; label = @2 + local.get 2 + br_if 0 (;@2;) local.get 1 - i32.const 0 - i32.store + local.set 2 + br 1 (;@1;) + end + local.get 3 + local.get 0 + i32.load + i32.store offset=12 + block ;; label = @2 + local.get 2 + i32.const 3 + i32.add + local.tee 4 + i32.const 2 + i32.shr_u + local.tee 5 local.get 1 - i32.const -8 + local.get 3 + i32.const 12 i32.add - local.tee 3 + call $wee_alloc::alloc_first_fit + local.tee 2 + br_if 0 (;@2;) + block ;; label = @3 + local.get 4 + i32.const -4 + i32.and + local.tee 2 + local.get 1 + i32.const 3 + i32.shl + i32.const 512 + i32.add + local.tee 4 + local.get 2 + local.get 4 + i32.gt_u + select + i32.const 65543 + i32.add + local.tee 4 + i32.const 16 + i32.shr_u + memory.grow + local.tee 2 + i32.const -1 + i32.ne + br_if 0 (;@3;) + i32.const 0 + local.set 2 + br 1 (;@2;) + end + local.get 2 + i32.const 16 + i32.shl + local.tee 2 + i32.const 0 + i32.store offset=4 + local.get 2 local.get 3 - i32.load - local.tee 5 - i32.const -2 + i32.load offset=12 + i32.store offset=8 + local.get 2 + local.get 2 + local.get 4 + i32.const -65536 i32.and - local.tee 6 + i32.add + i32.const 2 + i32.or i32.store - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 4 - i32.add - local.tee 7 - i32.load - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 0 (;@7;) - local.get 8 - i32.load - local.tee 9 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@6;) - end - local.get 5 + local.get 3 + local.get 2 + i32.store offset=12 + local.get 5 + local.get 1 + local.get 3 + i32.const 12 + i32.add + call $wee_alloc::alloc_first_fit + local.set 2 + end + local.get 0 + local.get 3 + i32.load offset=12 + i32.store + end + local.get 3 + i32.const 16 + i32.add + global.set $__stack_pointer + local.get 2 + ) + (func $::dealloc (;6;) (type 4) (param i32 i32 i32 i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + local.get 1 + i32.eqz + br_if 0 (;@1;) + local.get 3 + i32.eqz + br_if 0 (;@1;) + local.get 0 + i32.load + local.set 4 + local.get 1 + i32.const 0 + i32.store + local.get 1 + i32.const -8 + i32.add + local.tee 3 + local.get 3 + i32.load + local.tee 5 + i32.const -2 + i32.and + local.tee 6 + i32.store + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + local.get 3 + i32.const 4 + i32.add + local.tee 7 + i32.load i32.const -4 i32.and local.tee 8 i32.eqz - br_if 3 (;@3;) - local.get 5 - i32.const 2 + br_if 0 (;@7;) + local.get 8 + i32.load + local.tee 9 + i32.const 1 i32.and i32.eqz - br_if 1 (;@5;) - br 3 (;@3;) + br_if 1 (;@6;) end - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - local.get 5 - i32.const -4 - i32.and - local.tee 10 - br_if 0 (;@8;) - local.get 8 - local.set 1 - br 1 (;@7;) - end - local.get 8 - local.set 1 + local.get 5 + i32.const -4 + i32.and + local.tee 8 + i32.eqz + br_if 3 (;@3;) + local.get 5 + i32.const 2 + i32.and + i32.eqz + br_if 1 (;@5;) + br 3 (;@3;) + end + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 local.get 5 - i32.const 2 - i32.and - br_if 0 (;@7;) - local.get 10 - local.get 10 - i32.load offset=4 - i32.const 3 - i32.and - local.get 8 - i32.or - i32.store offset=4 - local.get 3 - i32.load - local.set 6 - local.get 7 - i32.load - local.tee 5 i32.const -4 i32.and - local.tee 1 - i32.eqz - br_if 1 (;@6;) - local.get 1 - i32.load - local.set 9 + local.tee 10 + br_if 0 (;@8;) + local.get 8 + local.set 1 + br 1 (;@7;) end - local.get 1 - local.get 6 - i32.const -4 + local.get 8 + local.set 1 + local.get 5 + i32.const 2 i32.and - local.get 9 + br_if 0 (;@7;) + local.get 10 + local.get 10 + i32.load offset=4 i32.const 3 i32.and + local.get 8 i32.or - i32.store - local.get 7 - i32.load - local.set 5 + i32.store offset=4 local.get 3 i32.load local.set 6 + local.get 7 + i32.load + local.tee 5 + i32.const -4 + i32.and + local.tee 1 + i32.eqz + br_if 1 (;@6;) + local.get 1 + i32.load + local.set 9 end - local.get 7 - local.get 5 - i32.const 3 - i32.and - i32.store - local.get 3 + local.get 1 local.get 6 - i32.const 3 + i32.const -4 i32.and - i32.store - local.get 6 - i32.const 2 + local.get 9 + i32.const 3 i32.and - i32.eqz - br_if 1 (;@4;) - local.get 8 - local.get 8 - i32.load - i32.const 2 i32.or i32.store - br 1 (;@4;) + local.get 7 + i32.load + local.set 5 + local.get 3 + i32.load + local.set 6 end - local.get 8 - i32.load8_u - i32.const 1 + local.get 7 + local.get 5 + i32.const 3 i32.and - br_if 1 (;@3;) - local.get 1 - local.get 8 - i32.load offset=8 - i32.const -4 + i32.store + local.get 3 + local.get 6 + i32.const 3 i32.and i32.store + local.get 6 + i32.const 2 + i32.and + i32.eqz + br_if 1 (;@4;) local.get 8 - local.get 3 - i32.const 1 + local.get 8 + i32.load + i32.const 2 i32.or - i32.store offset=8 + i32.store + br 1 (;@4;) end - local.get 4 - local.set 3 - br 1 (;@2;) + local.get 8 + i32.load8_u + i32.const 1 + i32.and + br_if 1 (;@3;) + local.get 1 + local.get 8 + i32.load offset=8 + i32.const -4 + i32.and + i32.store + local.get 8 + local.get 3 + i32.const 1 + i32.or + i32.store offset=8 end - local.get 1 local.get 4 - i32.store + local.set 3 + br 1 (;@2;) end - local.get 0 - local.get 3 + local.get 1 + local.get 4 i32.store end - ) - (func $wit_bindgen::rt::run_ctors_once (;7;) (type 0) - block ;; label = @1 - i32.const 0 - i32.load8_u offset=1048581 - br_if 0 (;@1;) - call $__wasm_call_ctors - i32.const 0 - i32.const 1 - i32.store8 offset=1048581 - end - ) - (func $cabi_realloc (;8;) (type 2) (param i32 i32 i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - br_if 0 (;@3;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - i32.const 0 - i32.load8_u offset=1048580 - drop - local.get 3 - local.get 2 - call $__rust_alloc - local.set 2 - br 1 (;@2;) - end - local.get 0 + local.get 0 + local.get 3 + i32.store + end + ) + (func $wit_bindgen::rt::run_ctors_once (;7;) (type 0) + block ;; label = @1 + i32.const 0 + i32.load8_u offset=1048581 + br_if 0 (;@1;) + call $__wasm_call_ctors + i32.const 0 + i32.const 1 + i32.store8 offset=1048581 + end + ) + (func $cabi_realloc (;8;) (type 2) (param i32 i32 i32 i32) (result i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 local.get 1 - local.get 2 + br_if 0 (;@3;) local.get 3 - call $__rust_realloc + i32.eqz + br_if 2 (;@1;) + i32.const 0 + i32.load8_u offset=1048580 + drop + local.get 3 + local.get 2 + call $__rust_alloc local.set 2 + br 1 (;@2;) end + local.get 0 + local.get 1 local.get 2 - br_if 0 (;@1;) - unreachable - unreachable + local.get 3 + call $__rust_realloc + local.set 2 end local.get 2 - ) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "miden:add-package/add-interface@1.0.0#add" (func $miden:add-package/add-interface@1.0.0#add)) - (export "cabi_realloc" (func $cabi_realloc)) - ) - (core instance (;0;) (instantiate 0)) - (alias core export 0 "memory" (core memory (;0;))) - (alias core export 0 "cabi_realloc" (core func (;0;))) - (type (;0;) (func (param "a" u32) (param "b" u32) (result u32))) - (alias core export 0 "miden:add-package/add-interface@1.0.0#add" (core func (;1;))) - (func (;0;) (type 0) (canon lift (core func 1))) - (component (;0;) - (type (;0;) (func (param "a" u32) (param "b" u32) (result u32))) - (import "import-func-add" (func (;0;) (type 0))) - (type (;1;) (func (param "a" u32) (param "b" u32) (result u32))) - (export (;1;) "add" (func 0) (func (type 1))) - ) - (instance (;0;) (instantiate 0 - (with "import-func-add" (func 0)) - ) + br_if 0 (;@1;) + unreachable + unreachable + end + local.get 2 ) - (export (;1;) "miden:add-package/add-interface@1.0.0" (instance 0)) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) + (export "memory" (memory 0)) + (export "miden:add-package/add-interface@1.0.0#add" (func $miden:add-package/add-interface@1.0.0#add)) + (export "cabi_realloc" (func $cabi_realloc)) ) \ No newline at end of file diff --git a/tests/integration/expected/components/inc_wasm_component.wat b/tests/integration/expected/components/inc_wasm_component.wat index 48063992f..7f3f6a55c 100644 --- a/tests/integration/expected/components/inc_wasm_component.wat +++ b/tests/integration/expected/components/inc_wasm_component.wat @@ -1,701 +1,680 @@ -(component - (type (;0;) - (instance - (type (;0;) (func (param "a" u32) (param "b" u32) (result u32))) - (export (;0;) "add" (func (type 0))) - ) +(module $inc_wasm_component.wasm + (type (;0;) (func (param i32 i32) (result i32))) + (type (;1;) (func)) + (type (;2;) (func (param i32) (result i32))) + (type (;3;) (func (param i32 i32 i32 i32) (result i32))) + (type (;4;) (func (param i32 i32 i32) (result i32))) + (type (;5;) (func (param i32 i32 i32 i32))) + (import "miden:add-package/add-interface@1.0.0" "add" (func $inc_wasm_component::bindings::miden::add_package::add_interface::add::wit_import (;0;) (type 0))) + (func $__wasm_call_ctors (;1;) (type 1)) + (func $inc (;2;) (type 2) (param i32) (result i32) + call $wit_bindgen::rt::run_ctors_once + local.get 0 + i32.const 1 + call $inc_wasm_component::bindings::miden::add_package::add_interface::add::wit_import ) - (import "miden:add-package/add-interface@1.0.0" (instance (;0;) (type 0))) - (core module (;0;) - (type (;0;) (func (param i32 i32) (result i32))) - (type (;1;) (func)) - (type (;2;) (func (param i32) (result i32))) - (type (;3;) (func (param i32 i32 i32 i32) (result i32))) - (type (;4;) (func (param i32 i32 i32) (result i32))) - (type (;5;) (func (param i32 i32 i32 i32))) - (import "miden:add-package/add-interface@1.0.0" "add" (func $inc_wasm_component::bindings::miden::add_package::add_interface::add::wit_import (;0;) (type 0))) - (func $__wasm_call_ctors (;1;) (type 1)) - (func $inc (;2;) (type 2) (param i32) (result i32) - call $wit_bindgen::rt::run_ctors_once - local.get 0 - i32.const 1 - call $inc_wasm_component::bindings::miden::add_package::add_interface::add::wit_import - ) - (func $__rust_alloc (;3;) (type 0) (param i32 i32) (result i32) + (func $__rust_alloc (;3;) (type 0) (param i32 i32) (result i32) + i32.const 1048576 + local.get 1 + local.get 0 + call $::alloc + ) + (func $__rust_realloc (;4;) (type 3) (param i32 i32 i32 i32) (result i32) + (local i32) + block ;; label = @1 i32.const 1048576 - local.get 1 - local.get 0 + local.get 2 + local.get 3 call $::alloc - ) - (func $__rust_realloc (;4;) (type 3) (param i32 i32 i32 i32) (result i32) - (local i32) - block ;; label = @1 - i32.const 1048576 - local.get 2 - local.get 3 - call $::alloc - local.tee 4 - i32.eqz - br_if 0 (;@1;) - local.get 4 - local.get 0 - local.get 1 - local.get 3 - local.get 1 - local.get 3 - i32.lt_u - select - memory.copy - i32.const 1048576 - local.get 0 - local.get 2 - local.get 1 - call $::dealloc - end + local.tee 4 + i32.eqz + br_if 0 (;@1;) local.get 4 - ) - (func $wee_alloc::alloc_first_fit (;5;) (type 4) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 2 - i32.load - local.tee 3 - i32.eqz - br_if 0 (;@1;) - local.get 1 - i32.const -1 - i32.add - local.set 4 - i32.const 0 - local.get 1 - i32.sub - local.set 5 - local.get 0 - i32.const 2 - i32.shl - local.set 6 - loop ;; label = @2 + local.get 0 + local.get 1 + local.get 3 + local.get 1 + local.get 3 + i32.lt_u + select + memory.copy + i32.const 1048576 + local.get 0 + local.get 2 + local.get 1 + call $::dealloc + end + local.get 4 + ) + (func $wee_alloc::alloc_first_fit (;5;) (type 4) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + local.get 2 + i32.load + local.tee 3 + br_if 0 (;@1;) + i32.const 0 + return + end + local.get 1 + i32.const -1 + i32.add + local.set 4 + i32.const 0 + local.get 1 + i32.sub + local.set 5 + local.get 0 + i32.const 2 + i32.shl + local.set 6 + loop ;; label = @1 + block ;; label = @2 + block ;; label = @3 + local.get 3 + i32.load offset=8 + local.tee 1 + i32.const 1 + i32.and + br_if 0 (;@3;) local.get 3 i32.const 8 i32.add - local.set 7 - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.load offset=8 - local.tee 0 - i32.const 1 - i32.and - br_if 0 (;@4;) - local.get 3 - local.set 1 - br 1 (;@3;) - end - loop ;; label = @4 - local.get 7 - local.get 0 - i32.const -2 - i32.and - i32.store + local.set 0 + br 1 (;@2;) + end + loop ;; label = @3 + local.get 3 + local.get 1 + i32.const -2 + i32.and + i32.store offset=8 + block ;; label = @4 + block ;; label = @5 local.get 3 i32.load offset=4 + local.tee 7 i32.const -4 i32.and - local.tee 1 - i32.load - local.set 7 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.load - local.tee 8 - i32.const -4 - i32.and - local.tee 0 - br_if 0 (;@7;) - local.get 1 - local.set 8 - br 1 (;@6;) - end - block ;; label = @7 - local.get 8 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@7;) - local.get 1 - local.set 8 - br 1 (;@6;) - end - local.get 0 - local.get 0 - i32.load offset=4 - i32.const 3 - i32.and - local.get 1 - i32.or - i32.store offset=4 - local.get 3 - i32.load - local.set 0 - local.get 3 - i32.load offset=4 - local.tee 7 - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 1 (;@5;) - local.get 0 - i32.const -4 - i32.and - local.set 0 - local.get 8 - i32.load - local.set 7 - end - local.get 8 - local.get 7 - i32.const 3 - i32.and - local.get 0 - i32.or - i32.store - local.get 3 - i32.load offset=4 - local.set 7 - local.get 3 - i32.load - local.set 0 - end - local.get 3 - local.get 7 - i32.const 3 - i32.and - i32.store offset=4 - local.get 3 - local.get 0 - i32.const 3 - i32.and - i32.store - block ;; label = @5 - local.get 0 - i32.const 2 - i32.and - i32.eqz - br_if 0 (;@5;) - local.get 1 - local.get 1 - i32.load - i32.const 2 - i32.or - i32.store - end - local.get 2 - local.get 1 - i32.store - local.get 1 - i32.const 8 - i32.add - local.set 7 - local.get 1 - local.set 3 - local.get 1 - i32.load offset=8 local.tee 0 - i32.const 1 - i32.and - br_if 0 (;@4;) + br_if 0 (;@5;) + i32.const 0 + local.set 8 + br 1 (;@4;) end + i32.const 0 + local.get 0 + local.get 0 + i32.load8_u + i32.const 1 + i32.and + select + local.set 8 end - block ;; label = @3 - local.get 1 + block ;; label = @4 + local.get 3 i32.load + local.tee 1 i32.const -4 i32.and - local.tee 3 - local.get 7 - i32.sub - local.get 6 - i32.lt_u - br_if 0 (;@3;) - block ;; label = @4 - block ;; label = @5 - local.get 7 - i32.const 72 - i32.add - local.get 3 - local.get 6 - i32.sub - local.get 5 - i32.and - local.tee 3 - i32.le_u - br_if 0 (;@5;) - local.get 4 - local.get 7 - i32.and - br_if 2 (;@3;) - local.get 2 - local.get 0 - i32.const -4 - i32.and - i32.store - local.get 1 - i32.load - local.set 0 - local.get 1 - local.set 3 - br 1 (;@4;) - end - i32.const 0 - local.set 0 - local.get 3 - i32.const 0 - i32.store - local.get 3 - i32.const -8 - i32.add - local.tee 3 - i64.const 0 - i64.store align=4 - local.get 3 - local.get 1 - i32.load - i32.const -4 - i32.and - i32.store - block ;; label = @5 - local.get 1 - i32.load - local.tee 2 - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 0 (;@5;) - local.get 2 - i32.const 2 - i32.and - br_if 0 (;@5;) - local.get 8 - local.get 8 - i32.load offset=4 - i32.const 3 - i32.and - local.get 3 - i32.or - i32.store offset=4 - local.get 3 - i32.load offset=4 - i32.const 3 - i32.and - local.set 0 - end - local.get 3 - local.get 0 - local.get 1 - i32.or - i32.store offset=4 - local.get 7 - local.get 7 - i32.load - i32.const -2 - i32.and - i32.store - local.get 1 - local.get 1 - i32.load - local.tee 0 - i32.const 3 - i32.and - local.get 3 - i32.or - local.tee 7 - i32.store - block ;; label = @5 - local.get 0 - i32.const 2 - i32.and - br_if 0 (;@5;) - local.get 3 - i32.load - local.set 0 - br 1 (;@4;) - end - local.get 1 - local.get 7 - i32.const -3 - i32.and - i32.store - local.get 3 - i32.load - i32.const 2 - i32.or - local.set 0 - end + local.tee 9 + i32.eqz + br_if 0 (;@4;) + local.get 1 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 9 + local.get 9 + i32.load offset=4 + i32.const 3 + i32.and + local.get 0 + i32.or + i32.store offset=4 local.get 3 + i32.load offset=4 + local.tee 7 + i32.const -4 + i32.and + local.set 0 + local.get 3 + i32.load + local.set 1 + end + block ;; label = @4 local.get 0 - i32.const 1 + i32.eqz + br_if 0 (;@4;) + local.get 0 + local.get 0 + i32.load + i32.const 3 + i32.and + local.get 1 + i32.const -4 + i32.and i32.or i32.store local.get 3 - i32.const 8 - i32.add - return + i32.load offset=4 + local.set 7 + local.get 3 + i32.load + local.set 1 + end + local.get 3 + local.get 7 + i32.const 3 + i32.and + i32.store offset=4 + local.get 3 + local.get 1 + i32.const 3 + i32.and + i32.store + block ;; label = @4 + local.get 1 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@4;) + local.get 8 + local.get 8 + i32.load + i32.const 2 + i32.or + i32.store end local.get 2 - local.get 0 + local.get 8 i32.store - local.get 0 + local.get 8 local.set 3 - local.get 0 - br_if 0 (;@2;) + local.get 8 + i32.load offset=8 + local.tee 1 + i32.const 1 + i32.and + br_if 0 (;@3;) end + local.get 8 + i32.const 8 + i32.add + local.set 0 + local.get 8 + local.set 3 end - i32.const 0 - ) - (func $::alloc (;6;) (type 4) (param i32 i32 i32) (result i32) - (local i32 i32 i32) - global.get $__stack_pointer - i32.const 16 - i32.sub - local.tee 3 - global.set $__stack_pointer - block ;; label = @1 - block ;; label = @2 - local.get 2 - br_if 0 (;@2;) - local.get 1 - local.set 2 - br 1 (;@1;) - end + block ;; label = @2 local.get 3 - local.get 0 i32.load - i32.store offset=12 - block ;; label = @2 - local.get 2 - i32.const 3 + i32.const -4 + i32.and + local.tee 8 + local.get 0 + i32.sub + local.get 6 + i32.lt_u + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + local.get 0 + i32.const 72 + i32.add + local.get 8 + local.get 6 + i32.sub + local.get 5 + i32.and + local.tee 8 + i32.le_u + br_if 0 (;@4;) + local.get 4 + local.get 0 + i32.and + br_if 2 (;@2;) + local.get 2 + local.get 1 + i32.const -4 + i32.and + i32.store + local.get 3 + i32.load + local.set 0 + local.get 3 + local.set 1 + br 1 (;@3;) + end + i32.const 0 + local.set 7 + local.get 8 + i32.const 0 + i32.store + local.get 8 + i32.const -8 i32.add - local.tee 4 - i32.const 2 - i32.shr_u - local.tee 5 + local.tee 1 + i64.const 0 + i64.store align=4 local.get 1 local.get 3 - i32.const 12 - i32.add - call $wee_alloc::alloc_first_fit - local.tee 2 - br_if 0 (;@2;) - block ;; label = @3 - local.get 4 + i32.load + i32.const -4 + i32.and + i32.store + block ;; label = @4 + local.get 3 + i32.load + local.tee 9 i32.const -4 i32.and - local.tee 2 + local.tee 8 + i32.eqz + br_if 0 (;@4;) + local.get 9 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 8 + local.get 8 + i32.load offset=4 + i32.const 3 + i32.and local.get 1 + i32.or + i32.store offset=4 + local.get 1 + i32.load offset=4 i32.const 3 - i32.shl - i32.const 512 - i32.add - local.tee 4 - local.get 2 - local.get 4 - i32.gt_u - select - i32.const 65543 - i32.add - local.tee 4 - i32.const 16 - i32.shr_u - memory.grow - local.tee 2 - i32.const -1 - i32.ne - br_if 0 (;@3;) - i32.const 0 - local.set 2 - br 1 (;@2;) + i32.and + local.set 7 end - local.get 2 - i32.const 16 - i32.shl - local.tee 2 - i32.const 0 + local.get 1 + local.get 7 + local.get 3 + i32.or i32.store offset=4 - local.get 2 + local.get 0 + local.get 0 + i32.load + i32.const -2 + i32.and + i32.store local.get 3 - i32.load offset=12 - i32.store offset=8 - local.get 2 - local.get 2 - local.get 4 - i32.const -65536 + local.get 3 + i32.load + local.tee 0 + i32.const 3 i32.and - i32.add - i32.const 2 + local.get 1 i32.or + local.tee 8 i32.store + block ;; label = @4 + local.get 0 + i32.const 2 + i32.and + br_if 0 (;@4;) + local.get 1 + i32.load + local.set 0 + br 1 (;@3;) + end local.get 3 - local.get 2 - i32.store offset=12 - local.get 5 + local.get 8 + i32.const -3 + i32.and + i32.store local.get 1 - local.get 3 - i32.const 12 - i32.add - call $wee_alloc::alloc_first_fit - local.set 2 + i32.load + i32.const 2 + i32.or + local.set 0 end + local.get 1 local.get 0 - local.get 3 - i32.load offset=12 + i32.const 1 + i32.or i32.store + local.get 1 + i32.const 8 + i32.add + return end - local.get 3 - i32.const 16 - i32.add - global.set $__stack_pointer local.get 2 - ) - (func $::dealloc (;7;) (type 5) (param i32 i32 i32 i32) - (local i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 1 - i32.eqz - br_if 0 (;@1;) - local.get 3 - i32.eqz - br_if 0 (;@1;) - local.get 0 - i32.load - local.set 4 + local.get 1 + i32.store + local.get 1 + local.set 3 + local.get 1 + br_if 0 (;@1;) + end + i32.const 0 + ) + (func $::alloc (;6;) (type 4) (param i32 i32 i32) (result i32) + (local i32 i32 i32) + global.get $__stack_pointer + i32.const 16 + i32.sub + local.tee 3 + global.set $__stack_pointer + block ;; label = @1 + block ;; label = @2 + local.get 2 + br_if 0 (;@2;) local.get 1 - i32.const 0 - i32.store + local.set 2 + br 1 (;@1;) + end + local.get 3 + local.get 0 + i32.load + i32.store offset=12 + block ;; label = @2 + local.get 2 + i32.const 3 + i32.add + local.tee 4 + i32.const 2 + i32.shr_u + local.tee 5 local.get 1 - i32.const -8 + local.get 3 + i32.const 12 i32.add - local.tee 3 + call $wee_alloc::alloc_first_fit + local.tee 2 + br_if 0 (;@2;) + block ;; label = @3 + local.get 4 + i32.const -4 + i32.and + local.tee 2 + local.get 1 + i32.const 3 + i32.shl + i32.const 512 + i32.add + local.tee 4 + local.get 2 + local.get 4 + i32.gt_u + select + i32.const 65543 + i32.add + local.tee 4 + i32.const 16 + i32.shr_u + memory.grow + local.tee 2 + i32.const -1 + i32.ne + br_if 0 (;@3;) + i32.const 0 + local.set 2 + br 1 (;@2;) + end + local.get 2 + i32.const 16 + i32.shl + local.tee 2 + i32.const 0 + i32.store offset=4 + local.get 2 local.get 3 - i32.load - local.tee 5 - i32.const -2 + i32.load offset=12 + i32.store offset=8 + local.get 2 + local.get 2 + local.get 4 + i32.const -65536 i32.and - local.tee 6 + i32.add + i32.const 2 + i32.or i32.store - block ;; label = @2 - block ;; label = @3 - block ;; label = @4 - block ;; label = @5 - block ;; label = @6 - block ;; label = @7 - local.get 3 - i32.const 4 - i32.add - local.tee 7 - i32.load - i32.const -4 - i32.and - local.tee 8 - i32.eqz - br_if 0 (;@7;) - local.get 8 - i32.load - local.tee 9 - i32.const 1 - i32.and - i32.eqz - br_if 1 (;@6;) - end - local.get 5 + local.get 3 + local.get 2 + i32.store offset=12 + local.get 5 + local.get 1 + local.get 3 + i32.const 12 + i32.add + call $wee_alloc::alloc_first_fit + local.set 2 + end + local.get 0 + local.get 3 + i32.load offset=12 + i32.store + end + local.get 3 + i32.const 16 + i32.add + global.set $__stack_pointer + local.get 2 + ) + (func $::dealloc (;7;) (type 5) (param i32 i32 i32 i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + local.get 1 + i32.eqz + br_if 0 (;@1;) + local.get 3 + i32.eqz + br_if 0 (;@1;) + local.get 0 + i32.load + local.set 4 + local.get 1 + i32.const 0 + i32.store + local.get 1 + i32.const -8 + i32.add + local.tee 3 + local.get 3 + i32.load + local.tee 5 + i32.const -2 + i32.and + local.tee 6 + i32.store + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + local.get 3 + i32.const 4 + i32.add + local.tee 7 + i32.load i32.const -4 i32.and local.tee 8 i32.eqz - br_if 3 (;@3;) - local.get 5 - i32.const 2 + br_if 0 (;@7;) + local.get 8 + i32.load + local.tee 9 + i32.const 1 i32.and i32.eqz - br_if 1 (;@5;) - br 3 (;@3;) + br_if 1 (;@6;) end - block ;; label = @6 - block ;; label = @7 - block ;; label = @8 - local.get 5 - i32.const -4 - i32.and - local.tee 10 - br_if 0 (;@8;) - local.get 8 - local.set 1 - br 1 (;@7;) - end - local.get 8 - local.set 1 + local.get 5 + i32.const -4 + i32.and + local.tee 8 + i32.eqz + br_if 3 (;@3;) + local.get 5 + i32.const 2 + i32.and + i32.eqz + br_if 1 (;@5;) + br 3 (;@3;) + end + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 local.get 5 - i32.const 2 - i32.and - br_if 0 (;@7;) - local.get 10 - local.get 10 - i32.load offset=4 - i32.const 3 - i32.and - local.get 8 - i32.or - i32.store offset=4 - local.get 3 - i32.load - local.set 6 - local.get 7 - i32.load - local.tee 5 i32.const -4 i32.and - local.tee 1 - i32.eqz - br_if 1 (;@6;) - local.get 1 - i32.load - local.set 9 + local.tee 10 + br_if 0 (;@8;) + local.get 8 + local.set 1 + br 1 (;@7;) end - local.get 1 - local.get 6 - i32.const -4 + local.get 8 + local.set 1 + local.get 5 + i32.const 2 i32.and - local.get 9 + br_if 0 (;@7;) + local.get 10 + local.get 10 + i32.load offset=4 i32.const 3 i32.and + local.get 8 i32.or - i32.store - local.get 7 - i32.load - local.set 5 + i32.store offset=4 local.get 3 i32.load local.set 6 + local.get 7 + i32.load + local.tee 5 + i32.const -4 + i32.and + local.tee 1 + i32.eqz + br_if 1 (;@6;) + local.get 1 + i32.load + local.set 9 end - local.get 7 - local.get 5 - i32.const 3 - i32.and - i32.store - local.get 3 + local.get 1 local.get 6 - i32.const 3 + i32.const -4 i32.and - i32.store - local.get 6 - i32.const 2 + local.get 9 + i32.const 3 i32.and - i32.eqz - br_if 1 (;@4;) - local.get 8 - local.get 8 - i32.load - i32.const 2 i32.or i32.store - br 1 (;@4;) + local.get 7 + i32.load + local.set 5 + local.get 3 + i32.load + local.set 6 end - local.get 8 - i32.load8_u - i32.const 1 + local.get 7 + local.get 5 + i32.const 3 i32.and - br_if 1 (;@3;) - local.get 1 - local.get 8 - i32.load offset=8 - i32.const -4 + i32.store + local.get 3 + local.get 6 + i32.const 3 i32.and i32.store + local.get 6 + i32.const 2 + i32.and + i32.eqz + br_if 1 (;@4;) local.get 8 - local.get 3 - i32.const 1 + local.get 8 + i32.load + i32.const 2 i32.or - i32.store offset=8 + i32.store + br 1 (;@4;) end - local.get 4 - local.set 3 - br 1 (;@2;) + local.get 8 + i32.load8_u + i32.const 1 + i32.and + br_if 1 (;@3;) + local.get 1 + local.get 8 + i32.load offset=8 + i32.const -4 + i32.and + i32.store + local.get 8 + local.get 3 + i32.const 1 + i32.or + i32.store offset=8 end - local.get 1 local.get 4 - i32.store + local.set 3 + br 1 (;@2;) end - local.get 0 - local.get 3 + local.get 1 + local.get 4 i32.store end - ) - (func $wit_bindgen::rt::run_ctors_once (;8;) (type 1) - block ;; label = @1 - i32.const 0 - i32.load8_u offset=1048581 - br_if 0 (;@1;) - call $__wasm_call_ctors - i32.const 0 - i32.const 1 - i32.store8 offset=1048581 - end - ) - (func $cabi_realloc (;9;) (type 3) (param i32 i32 i32 i32) (result i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - br_if 0 (;@3;) - local.get 3 - i32.eqz - br_if 2 (;@1;) - i32.const 0 - i32.load8_u offset=1048580 - drop - local.get 3 - local.get 2 - call $__rust_alloc - local.set 2 - br 1 (;@2;) - end - local.get 0 + local.get 0 + local.get 3 + i32.store + end + ) + (func $wit_bindgen::rt::run_ctors_once (;8;) (type 1) + block ;; label = @1 + i32.const 0 + i32.load8_u offset=1048581 + br_if 0 (;@1;) + call $__wasm_call_ctors + i32.const 0 + i32.const 1 + i32.store8 offset=1048581 + end + ) + (func $cabi_realloc (;9;) (type 3) (param i32 i32 i32 i32) (result i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 local.get 1 - local.get 2 + br_if 0 (;@3;) local.get 3 - call $__rust_realloc + i32.eqz + br_if 2 (;@1;) + i32.const 0 + i32.load8_u offset=1048580 + drop + local.get 3 + local.get 2 + call $__rust_alloc local.set 2 + br 1 (;@2;) end + local.get 0 + local.get 1 local.get 2 - br_if 0 (;@1;) - unreachable - unreachable + local.get 3 + call $__rust_realloc + local.set 2 end local.get 2 - ) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) - (export "memory" (memory 0)) - (export "inc" (func $inc)) - (export "cabi_realloc" (func $cabi_realloc)) - ) - (alias export 0 "add" (func (;0;))) - (core func (;0;) (canon lower (func 0))) - (core instance (;0;) - (export "add" (func 0)) - ) - (core instance (;1;) (instantiate 0 - (with "miden:add-package/add-interface@1.0.0" (instance 0)) - ) + br_if 0 (;@1;) + unreachable + unreachable + end + local.get 2 ) - (alias core export 1 "memory" (core memory (;0;))) - (alias core export 1 "cabi_realloc" (core func (;1;))) - (type (;1;) (func (param "a" u32) (result u32))) - (alias core export 1 "inc" (core func (;2;))) - (func (;1;) (type 1) (canon lift (core func 2))) - (export (;2;) "inc" (func 1)) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) + (export "memory" (memory 0)) + (export "inc" (func $inc)) + (export "cabi_realloc" (func $cabi_realloc)) ) \ No newline at end of file diff --git a/tests/integration/expected/fib.masm b/tests/integration/expected/fib.masm index 17350d3b9..6096c2017 100644 --- a/tests/integration/expected/fib.masm +++ b/tests/integration/expected/fib.masm @@ -31,6 +31,3 @@ export.fib end -begin - exec.::miden_integration_tests_rust_fib_wasm::fib -end \ No newline at end of file diff --git a/tests/integration/expected/fib.wat b/tests/integration/expected/fib.wat index d9ca4b113..580653ea1 100644 --- a/tests/integration/expected/fib.wat +++ b/tests/integration/expected/fib.wat @@ -28,7 +28,6 @@ br 0 (;@1;) end ) - (table (;0;) 1 1 funcref) (memory (;0;) 16) (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) (global (;1;) i32 i32.const 1048576) diff --git a/tests/integration/src/cargo_proj/mod.rs b/tests/integration/src/cargo_proj/mod.rs index 31561b0c9..d58feef00 100644 --- a/tests/integration/src/cargo_proj/mod.rs +++ b/tests/integration/src/cargo_proj/mod.rs @@ -44,7 +44,6 @@ pub fn panic_error(what: &str, err: impl Into) -> ! { pub mod paths; use self::paths::CargoPathExt; -use crate::compiler_test::skip_rust_compilation; /* * @@ -207,7 +206,8 @@ impl ProjectBuilder { pub fn build(mut self) -> Project { let last_path_component = self.root.root().file_name().unwrap().to_string_lossy().to_string(); - if skip_rust_compilation(&self.root(), &last_path_component) { + + if self.skip_rust_compilation(&last_path_component) { // Return the root directory without re-creating any files return self.root; } @@ -249,6 +249,22 @@ impl ProjectBuilder { fn rm_root(&self) { self.root.root().rm_rf() } + + fn skip_rust_compilation(&self, artifact_name: &str) -> bool { + let computed_artifact_path = self + .root() + .join("target") + .join("wasm32-unknown-unknown") + .join("release") + .join(artifact_name) + .with_extension("wasm"); + if std::env::var("SKIP_RUST").is_ok() && computed_artifact_path.exists() { + eprintln!("Skipping Rust compilation"); + true + } else { + false + } + } } impl Project { @@ -465,9 +481,6 @@ pub fn is_nightly() -> bool { /// Returns `true` if the local filesystem has low-resolution mtimes. pub fn is_coarse_mtime() -> bool { - // If the filetime crate is being used to emulate HFS then - // return `true`, without looking at the actual hardware. - cfg!(emulate_second_only_system) || // This should actually be a test that `$CARGO_TARGET_DIR` is on an HFS // filesystem, (or any filesystem with low-resolution mtimes). However, // that's tricky to detect, so for now just deal with CI. diff --git a/tests/integration/src/compiler_test.rs b/tests/integration/src/compiler_test.rs index 3dec89137..038743ea1 100644 --- a/tests/integration/src/compiler_test.rs +++ b/tests/integration/src/compiler_test.rs @@ -2,55 +2,23 @@ use core::panic; use std::{ - fmt::Write, - fs, + borrow::Cow, + fmt, fs, io::Read, path::{Path, PathBuf}, process::{Command, Stdio}, sync::Arc, }; -use miden_assembly::{ast::ModuleKind, diagnostics::Report, Assembler, LibraryPath}; -use miden_diagnostics::SourceSpan; -use miden_stdlib::StdLibrary; +use miden_assembly::LibraryPath; use midenc_frontend_wasm::{translate, WasmTranslationConfig}; use midenc_hir::{FunctionIdent, Ident, Symbol}; -use midenc_session::{ - InputFile, InputType, Options, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, -}; +use midenc_session::{InputFile, InputType, OutputType, Session}; use crate::cargo_proj::project; type LinkMasmModules = Vec<(LibraryPath, String)>; -pub enum CompilerTestSource { - Rust(String), - RustCargo { - cargo_project_folder_name: String, - artifact_name: String, - }, - RustCargoLib { - artifact_name: String, - }, - RustCargoComponent { - artifact_name: String, - }, -} - -impl CompilerTestSource { - pub fn artifact_name(&self) -> String { - match self { - CompilerTestSource::RustCargo { - cargo_project_folder_name: _, - artifact_name, - } => artifact_name.clone(), - CompilerTestSource::RustCargoLib { artifact_name } => artifact_name.clone(), - CompilerTestSource::RustCargoComponent { artifact_name } => artifact_name.clone(), - _ => panic!("Not a Rust Cargo project"), - } - } -} - #[derive(derive_more::From)] pub enum HirArtifact { Program(Box), @@ -81,156 +49,372 @@ impl HirArtifact { } } -/// Compile to different stages (e.g. Wasm, IR, MASM) and compare the results against expected -/// output -pub struct CompilerTest { - /// The Wasm translation configuration - pub config: WasmTranslationConfig, - /// The compiler session - pub session: Arc, - /// The source code used to compile the test - pub source: CompilerTestSource, - /// The entrypoint function to use when building the IR - entrypoint: Option, - /// The extra MASM modules to link to the compiled MASM program - pub link_masm_modules: LinkMasmModules, - /// The compiled IR - hir: Option, - /// The MASM source code - masm_src: Option, - /// The compiled IR MASM program - ir_masm_program: Option, String>>, - /// The compiled VM program - vm_masm_program: Option, String>>, +/// Configuration for tests which use as input, the artifact produced by a Cargo build +pub struct CargoTest { + project_dir: PathBuf, + manifest_path: Option>, + target_dir: Option, + name: Cow<'static, str>, + target: Cow<'static, str>, + entrypoint: Option>, + build_std: bool, + build_alloc: bool, } - -impl Default for CompilerTest { - fn default() -> Self { +impl CargoTest { + /// Create a new `cargo` test with the given name, and project directory + pub fn new(name: impl Into>, project_dir: PathBuf) -> Self { Self { - config: WasmTranslationConfig::default(), - session: Arc::new(dummy_session()), - source: CompilerTestSource::Rust(String::new()), + project_dir, + manifest_path: None, + target_dir: None, + name: name.into(), + target: "wasm32-wasi".into(), entrypoint: None, - link_masm_modules: Vec::new(), - hir: None, - masm_src: None, - ir_masm_program: None, - vm_masm_program: None, + build_std: false, + build_alloc: false, } } + + /// Specify whether to build the entire standard library as part of the crate graph + #[inline] + pub fn with_build_std(mut self, build_std: bool) -> Self { + self.build_std = build_std; + self + } + + /// Specify whether to build libcore and liballoc as part of the crate graph (implied by + /// `with_build_std`) + #[inline] + pub fn with_build_alloc(mut self, build_alloc: bool) -> Self { + self.build_alloc = build_alloc; + self + } + + /// Specify the target triple to pass to Cargo + #[inline] + pub fn with_target(mut self, target: impl Into>) -> Self { + self.target = target.into(); + self + } + + /// Specify the target directory for Cargo + #[inline] + pub fn with_target_dir(mut self, target_dir: impl Into) -> Self { + self.target_dir = Some(target_dir.into()); + self + } + + /// Specify the name of the entrypoint function (just the function name, no namespace) + #[inline] + pub fn with_entrypoint(mut self, entrypoint: impl Into>) -> Self { + self.entrypoint = Some(entrypoint.into()); + self + } + + /// Override the Cargo manifest path + #[inline] + pub fn with_manifest_path(mut self, manifest_path: impl Into>) -> Self { + self.manifest_path = Some(manifest_path.into()); + self + } + + /// Get a [PathBuf] representing the path to the expected Cargo artifact + pub fn wasm_artifact_path(&self) -> PathBuf { + self.project_dir + .join("target") + .join(self.target.as_ref()) + .join("release") + .join(self.name.as_ref()) + .with_extension("wasm") + } } -impl CompilerTest { - /// Compile the Wasm component from a Rust Cargo project using cargo-component - pub fn rust_source_cargo_component( - cargo_project_folder: PathBuf, - config: WasmTranslationConfig, +/// Configuration for tests which use as input, the artifact produced by an invocation of `rustc` +pub struct RustcTest { + target_dir: Option, + name: Cow<'static, str>, + target: Cow<'static, str>, + output_name: Option>, + source_code: Cow<'static, str>, + rustflags: Vec>, +} +impl RustcTest { + /// Construct a new `rustc` input with the given name and source code content + pub fn new( + name: impl Into>, + source_code: impl Into>, ) -> Self { - let manifest_path = cargo_project_folder.join("Cargo.toml"); - let mut cargo_build_cmd = Command::new("cargo"); - let compiler_workspace_dir = get_workspace_dir(); - // Enable Wasm bulk-memory proposal (uses Wasm `memory.copy` op instead of `memcpy` import) - // Remap the compiler workspace directory to `~` to have a reproducible build that does not - // have the absolute local path baked into the Wasm binary - cargo_build_cmd.env( - "RUSTFLAGS", - format!( - "-C target-feature=+bulk-memory --remap-path-prefix {compiler_workspace_dir}=~" - ), - ); - cargo_build_cmd - .arg("component") - .arg("build") - .arg("--manifest-path") - .arg(manifest_path) - .arg("--release") - // compile std as part of crate graph compilation - // https://doc.rust-lang.org/cargo/reference/unstable.html#build-std - .arg("-Z") - .arg("build-std=std,core,alloc,panic_abort") - .arg("-Z") - // abort on panic without message formatting (core::fmt uses call_indirect) - .arg("build-std-features=panic_immediate_abort"); - let mut child = cargo_build_cmd - .arg("--message-format=json-render-diagnostics") - .stdout(Stdio::piped()) - .spawn() - .unwrap_or_else(|_| { - panic!( - "Failed to execute cargo build {}.", - cargo_build_cmd - .get_args() - .map(|arg| format!("'{}'", arg.to_str().unwrap())) - .collect::>() - .join(" ") - ) - }); - let wasm_artifacts = find_wasm_artifacts(&mut child); - let output = child.wait().expect("Couldn't get cargo's exit status"); - if !output.success() { - report_cargo_error(child); + Self { + target_dir: None, + name: name.into(), + target: "wasm32-unknown-unknown".into(), + output_name: None, + source_code: source_code.into(), + // Always use spec-compliant C ABI behavior + rustflags: vec!["-Z".into(), "wasm_c_abi=spec".into()], + } + } +} + +/// The various types of input artifacts that can be used to drive compiler tests +pub enum CompilerTestInputType { + /// A project that uses `cargo component build` to produce a Wasm module to use as input + CargoComponent(CargoTest), + /// A project that uses `cargo build` to produce a core Wasm module to use as input + Cargo(CargoTest), + /// A project that uses `rustc` to produce a core Wasm module to use as input + Rustc(RustcTest), +} +impl From for CompilerTestInputType { + fn from(config: CargoTest) -> Self { + Self::Cargo(config) + } +} +impl From for CompilerTestInputType { + fn from(config: RustcTest) -> Self { + Self::Rustc(config) + } +} + +/// [CompilerTestBuilder] is used to obtain a [CompilerTest], and subsequently run that test. +/// +/// Testing the compiler involves orchestrating a number of complex components. First, we must +/// obtain the input we wish to feed into `midenc` for the test. Typically, we have some Rust +/// source code, or a Cargo project, and we must compile that first, in order to get the Wasm +/// module/component which will be passed to `midenc`. This first phase requires some configuration, +/// and that configuration affects later phases (such as the name of the artifact produced). +/// +/// Secondly, we need to prepare the [midenc_session::Session] object for the compiler. This is +/// where we specify inputs, and various bits of configuration that are important to the test, or +/// which are needed in order to obtain useful diagnostic output. This phase requires us to +/// construct the base configuration here, but make it possible to extend/alter in each specific +/// test. +/// +/// Lastly, we must run the test, and in order to do this, we must know where our inputs and outputs +/// are, so that we can fetch files/data/etc. as needed; know the names of things to be called, and +/// more. +pub struct CompilerTestBuilder { + /// The Wasm translation configuration + config: WasmTranslationConfig, + /// The source code used to compile the test + source: CompilerTestInputType, + /// The entrypoint function to use when building the IR + entrypoint: Option, + /// The extra MASM modules to link to the compiled MASM program + link_masm_modules: LinkMasmModules, + /// Extra flags to pass to the midenc driver + midenc_flags: Vec>, + /// Extra RUSTFLAGS to set when compiling Rust code + rustflags: Vec>, + /// The cargo workspace directory of the compiler + workspace_dir: String, +} +impl CompilerTestBuilder { + /// Construct a new [CompilerTestBuilder] for the given source type configuration + pub fn new(source: impl Into) -> Self { + let workspace_dir = get_workspace_dir(); + let mut source = source.into(); + let mut rustflags = match source { + CompilerTestInputType::Rustc(ref mut config) => core::mem::take(&mut config.rustflags), + _ => vec![], + }; + let entrypoint = match source { + CompilerTestInputType::Cargo(ref mut config) => config.entrypoint.take(), + CompilerTestInputType::CargoComponent(ref mut config) => config.entrypoint.take(), + CompilerTestInputType::Rustc(_) => None, + }; + let name = match source { + CompilerTestInputType::Cargo(ref mut config) => config.name.as_ref(), + CompilerTestInputType::CargoComponent(ref mut config) => config.name.as_ref(), + CompilerTestInputType::Rustc(ref mut config) => config.name.as_ref(), + }; + let entrypoint = match entrypoint.as_deref() { + Some(entry) => Some(FunctionIdent { + module: Ident::with_empty_span(Symbol::intern(name)), + function: Ident::with_empty_span(Symbol::intern(entry)), + }), + None => None, + }; + rustflags.extend([ + // Enable bulk-memory features (e.g. native memcpy/memset instructions) + "-C".into(), + "target-feature=+bulk-memory".into(), + // Remap the compiler workspace to `.` so that build outputs do not embed user- + // specific paths, which would cause expect tests to break + "--remap-path-prefix".into(), + format!("{workspace_dir}=../../").into(), + ]); + let mut midenc_flags = vec!["--debug".into(), "--verbose".into()]; + if let Some(entrypoint) = entrypoint { + midenc_flags + .extend(["--entrypoint".into(), format!("{}", entrypoint.display()).into()]); } - assert!(output.success()); - assert_eq!(wasm_artifacts.len(), 1, "Expected one Wasm artifact"); - let wasm_comp_path = &wasm_artifacts.first().unwrap(); - let artifact_name = wasm_comp_path.file_stem().unwrap().to_str().unwrap().to_string(); - let input_file = InputFile::from_path(wasm_comp_path).unwrap(); Self { - config, - session: default_session(input_file), - source: CompilerTestSource::RustCargoComponent { artifact_name }, - ..Default::default() + config: Default::default(), + source, + entrypoint, + link_masm_modules: vec![], + midenc_flags, + rustflags, + workspace_dir, } } - /// Set the Rust source code to compile a library Cargo project to Wasm module - pub fn rust_source_cargo_lib( - cargo_project_folder: PathBuf, - artifact_name: &str, - is_build_std: bool, - entry_func_name: Option, - ) -> Self { - let expected_wasm_artifact_path = wasm_artifact_path(&cargo_project_folder, artifact_name); - // dbg!(&wasm_artifact_path); - let wasm_artifact_path = if !skip_rust_compilation(&cargo_project_folder, artifact_name) - || !expected_wasm_artifact_path.exists() - { - let manifest_path = cargo_project_folder.join("Cargo.toml"); - let mut cargo_build_cmd = Command::new("cargo"); - let compiler_workspace_dir = get_workspace_dir(); - // Enable Wasm bulk-memory proposal (uses Wasm `memory.copy` op instead of `memcpy` - // import) Remap the compiler workspace directory to `~` to have a - // reproducible build that does not have the absolute local path baked into - // the Wasm binary - cargo_build_cmd.env( - "RUSTFLAGS", - format!( - "-C target-feature=+bulk-memory --remap-path-prefix {compiler_workspace_dir}=~" - ), + /// Override the default [WasmTranslationConfig] for the test + pub fn with_wasm_translation_config(&mut self, config: WasmTranslationConfig) -> &mut Self { + self.config = config; + self + } + + /// Specify the entrypoint function to call during the test + pub fn with_entrypoint(&mut self, entrypoint: FunctionIdent) -> &mut Self { + match self.entrypoint.replace(entrypoint) { + Some(prev) if prev == entrypoint => return self, + Some(prev) => { + // Remove the previous --entrypoint ID flag + let index = self + .midenc_flags + .iter() + .position(|flag| flag == "--entrypoint") + .unwrap_or_else(|| { + panic!( + "entrypoint was changed from '{}' -> '{}', but previous entrypoint \ + had been set without passing --entrypoint to midenc", + prev.display(), + entrypoint.display() + ) + }); + self.midenc_flags.remove(index); + self.midenc_flags.remove(index); + } + None => (), + } + self.midenc_flags + .extend(["--entrypoint".into(), format!("{}", entrypoint.display()).into()]); + self + } + + /// Append additional `midenc` compiler flags + pub fn with_midenc_flags( + &mut self, + flags: impl IntoIterator>, + ) -> &mut Self { + self.midenc_flags.extend(flags.into_iter().map(|flag| flag.into())); + self + } + + /// Append additional flags to the value of `RUSTFLAGS` used when invoking `cargo` or `rustc` + pub fn with_rustflags( + &mut self, + flags: impl IntoIterator>, + ) -> &mut Self { + self.rustflags.extend(flags.into_iter().map(|flag| flag.into())); + self + } + + /// Add additional Miden Assembly module sources, to be linked with the program under test. + pub fn link_with_masm_module( + &mut self, + fully_qualified_name: impl AsRef, + source: impl Into, + ) -> &mut Self { + let name = fully_qualified_name.as_ref(); + let path = LibraryPath::new(name) + .unwrap_or_else(|err| panic!("invalid miden assembly module name '{name}': {err}")); + self.link_masm_modules.push((path, source.into())); + self + } + + /// Consume the builder, invoke any tools required to obtain the inputs for the test, and if + /// successful, return a [CompilerTest], ready for evaluation. + pub fn build(self) -> CompilerTest { + // Set up the command used to compile the test inputs (typically Rust -> Wasm) + let mut command = match self.source { + CompilerTestInputType::CargoComponent(_) => { + let mut cmd = Command::new("cargo"); + cmd.arg("component").arg("build"); + cmd + } + CompilerTestInputType::Cargo(_) => { + let mut cmd = Command::new("cargo"); + cmd.arg("build"); + cmd + } + CompilerTestInputType::Rustc(_) => Command::new("rustc"), + }; + + // Extract the directory in which source code is presumed to exist (or will be placed) + let project_dir = match self.source { + CompilerTestInputType::CargoComponent(CargoTest { + ref project_dir, .. + }) + | CompilerTestInputType::Cargo(CargoTest { + ref project_dir, .. + }) => Cow::Borrowed(project_dir.as_path()), + CompilerTestInputType::Rustc(RustcTest { ref target_dir, .. }) => target_dir + .as_deref() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(std::env::temp_dir())), + }; + + // Cargo-based source types share a lot of configuration in common + match self.source { + CompilerTestInputType::CargoComponent(ref config) + | CompilerTestInputType::Cargo(ref config) => { + let manifest_path = project_dir.join("Cargo.toml"); + command + .arg("--manifest-path") + .arg(manifest_path) + .arg("--release") + .arg("--target") + .arg(config.target.as_ref()); + + if config.build_std { + // compile std as part of crate graph compilation + // https://doc.rust-lang.org/cargo/reference/unstable.html#build-std + command.arg("-Z").arg("build-std=core,alloc,std,panic_abort"); + + // abort on panic without message formatting (core::fmt uses call_indirect) + command.arg("-Z").arg("build-std-features=panic_immediate_abort"); + } else if config.build_alloc { + // compile libcore and liballoc as part of crate graph compilation + // https://doc.rust-lang.org/cargo/reference/unstable.html#build-std + command.arg("-Z").arg("build-std=core,alloc"); + + // abort on panic without message formatting (core::fmt uses call_indirect) + command.arg("-Z").arg("build-std-features=panic_immediate_abort"); + } + + // Render Cargo output as JSON + command.arg("--message-format=json-render-diagnostics"); + } + _ => (), + } + + // All test source types support custom RUSTFLAGS + if !self.rustflags.is_empty() { + let mut flags = String::with_capacity( + self.rustflags.iter().map(|flag| flag.len()).sum::() + self.rustflags.len(), ); - cargo_build_cmd - .arg("build") - .arg("--manifest-path") - .arg(manifest_path) - .arg("--release") - .arg("--target=wasm32-wasi"); - if is_build_std { - // compile std as part of crate graph compilation - // https://doc.rust-lang.org/cargo/reference/unstable.html#build-std - cargo_build_cmd.arg("-Z") - .arg("build-std=std,core,alloc,panic_abort") - .arg("-Z") - // abort on panic without message formatting (core::fmt uses call_indirect) - .arg("build-std-features=panic_immediate_abort"); + for (i, flag) in self.rustflags.iter().enumerate() { + if i > 0 { + flags.push(' '); + } + flags.push_str(flag.as_ref()); } - let mut child = cargo_build_cmd - .arg("--message-format=json-render-diagnostics") - .stdout(Stdio::piped()) - .spawn() - .unwrap_or_else(|_| { + command.env("RUSTFLAGS", flags); + } + + // Pipe output of command to terminal + command.stdout(Stdio::piped()); + + // Build test + match self.source { + CompilerTestInputType::CargoComponent(_) => { + let mut child = command.spawn().unwrap_or_else(|_| { panic!( - "Failed to execute cargo build {}.", - cargo_build_cmd + "Failed to execute command: {}", + command .get_args() .map(|arg| format!("'{}'", arg.to_str().unwrap())) .collect::>() @@ -238,111 +422,224 @@ impl CompilerTest { ) }); - // Find the Wasm artifacts from the cargo build output for debugging purposes - let mut wasm_artifacts = find_wasm_artifacts(&mut child); - let output = child.wait().expect("Couldn't get cargo's exit status"); - if !output.success() { - report_cargo_error(child); + let wasm_artifacts = find_wasm_artifacts(&mut child); + let output = child.wait().expect("Couldn't get cargo's exit status"); + if !output.success() { + report_cargo_error(child); + } + assert!(output.success()); + assert_eq!(wasm_artifacts.len(), 1, "Expected one Wasm artifact"); + let wasm_comp_path = &wasm_artifacts.first().unwrap(); + let artifact_name = + wasm_comp_path.file_stem().unwrap().to_str().unwrap().to_string(); + let input_file = InputFile::from_path(wasm_comp_path).unwrap(); + let mut inputs = vec![input_file]; + inputs.extend(self.link_masm_modules.into_iter().map(|(path, content)| { + let path = path.to_string(); + InputFile::new( + midenc_session::FileType::Masm, + InputType::Stdin { + name: path.into(), + input: content.into_bytes(), + }, + ) + })); + + CompilerTest { + config: self.config, + session: default_session(inputs, &self.midenc_flags), + artifact_name: artifact_name.into(), + entrypoint: self.entrypoint, + ..Default::default() + } } - assert!(output.success()); - // filter out dependencies - wasm_artifacts.retain(|path| { - let path_str = path.to_str().unwrap(); - !path_str.contains("release/deps") - }); - dbg!(&wasm_artifacts); - assert_eq!(wasm_artifacts.len(), 1, "Expected one Wasm artifact"); - wasm_artifacts.first().unwrap().to_path_buf() - } else { - expected_wasm_artifact_path - }; + CompilerTestInputType::Cargo(config) => { + let expected_wasm_artifact_path = config.wasm_artifact_path(); + let skip_rust_compilation = + std::env::var("SKIP_RUST").is_ok() && expected_wasm_artifact_path.exists(); + let wasm_artifact_path = if !skip_rust_compilation { + let mut child = command.spawn().unwrap_or_else(|_| { + panic!( + "Failed to execute command: {}", + command + .get_args() + .map(|arg| format!("'{}'", arg.to_str().unwrap())) + .collect::>() + .join(" ") + ) + }); + // Find the Wasm artifacts from the cargo build output for debugging purposes + let mut wasm_artifacts = find_wasm_artifacts(&mut child); + let output = child.wait().expect("Couldn't get cargo's exit status"); + if !output.success() { + report_cargo_error(child); + } + assert!(output.success()); + // filter out dependencies + wasm_artifacts.retain(|path| { + let path_str = path.to_str().unwrap(); + !path_str.contains("release/deps") + }); + dbg!(&wasm_artifacts); + assert_eq!(wasm_artifacts.len(), 1, "Expected one Wasm artifact"); + wasm_artifacts.swap_remove(0) + } else { + drop(command); + expected_wasm_artifact_path + }; + + let input_file = InputFile::from_path(wasm_artifact_path).unwrap(); + let mut inputs = vec![input_file]; + inputs.extend(self.link_masm_modules.into_iter().map(|(path, content)| { + let path = path.to_string(); + InputFile::new( + midenc_session::FileType::Masm, + InputType::Stdin { + name: path.into(), + input: content.into_bytes(), + }, + ) + })); + CompilerTest { + config: self.config, + session: default_session(inputs, &self.midenc_flags), + artifact_name: config.name, + entrypoint: self.entrypoint, + ..Default::default() + } + } + CompilerTestInputType::Rustc(config) => { + // Ensure we have a fresh working directory prepared + let working_dir = config + .target_dir + .clone() + .unwrap_or_else(|| std::env::temp_dir().join(config.name.as_ref())); + if working_dir.exists() { + fs::remove_dir_all(&working_dir).unwrap(); + } + fs::create_dir_all(&working_dir).unwrap(); + + // Prepare inputs + let basename = working_dir.join(config.name.as_ref()); + let input_file = basename.with_extension("rs"); + fs::write(&input_file, config.source_code.as_ref()).unwrap(); + + // Output is the same name as the input, just with a different extension + let output_file = basename.with_extension("wasm"); + + let output = command + .args(["-C", "opt-level=z"]) // optimize for size + .arg("--target") + .arg(config.target.as_ref()) + .arg("-o") + .arg(&output_file) + .arg(&input_file) + .output() + .expect("rustc invocation failed"); + if !output.status.success() { + eprintln!("pwd: {:?}", std::env::current_dir().unwrap()); + eprintln!("{}", String::from_utf8_lossy(&output.stderr)); + panic!("Rust to Wasm compilation failed!"); + } + let input_file = InputFile::from_path(output_file).unwrap(); + let mut inputs = vec![input_file]; + inputs.extend(self.link_masm_modules.into_iter().map(|(path, content)| { + let path = path.to_string(); + InputFile::new( + midenc_session::FileType::Masm, + InputType::Stdin { + name: path.into(), + input: content.into_bytes(), + }, + ) + })); + CompilerTest { + config: self.config, + session: default_session(inputs, &self.midenc_flags), + artifact_name: config.name, + entrypoint: self.entrypoint, + ..Default::default() + } + } + } + } +} + +/// Convenience builders +impl CompilerTestBuilder { + /// Compile the Wasm component from a Rust Cargo project using cargo-component + pub fn rust_source_cargo_component( + cargo_project_folder: impl AsRef, + config: WasmTranslationConfig, + ) -> Self { + let name = cargo_project_folder + .as_ref() + .file_stem() + .map(|name| name.to_string_lossy().into_owned()) + .unwrap_or("".to_string()); + let mut builder = CompilerTestBuilder::new(CompilerTestInputType::CargoComponent( + CargoTest::new(name, cargo_project_folder.as_ref().to_path_buf()), + )); + builder.with_wasm_translation_config(config); + builder + } - let entrypoint = entry_func_name.map(|func_name| FunctionIdent { - module: Ident::new(Symbol::intern(artifact_name), SourceSpan::default()), - function: Ident::new(Symbol::intern(func_name.to_string()), SourceSpan::default()), + /// Set the Rust source code to compile a library Cargo project to Wasm module + pub fn rust_source_cargo_lib( + cargo_project_folder: impl AsRef, + artifact_name: impl Into>, + is_build_std: bool, + entry_func_name: Option>, + midenc_flags: impl IntoIterator>, + ) -> Self { + let cargo_project_folder = cargo_project_folder.as_ref().to_path_buf(); + let config = + CargoTest::new(artifact_name, cargo_project_folder).with_build_std(is_build_std); + let mut builder = CompilerTestBuilder::new(match entry_func_name { + Some(entry) => config.with_entrypoint(entry), + None => config, }); - let input_file = InputFile::from_path(wasm_artifact_path).unwrap(); - Self { - config: WasmTranslationConfig::default(), - session: default_session(input_file), - source: CompilerTestSource::RustCargoLib { - artifact_name: artifact_name.to_string(), - }, - entrypoint, - ..Default::default() - } + builder.with_midenc_flags(midenc_flags); + builder } /// Set the Rust source code to compile using a Cargo project and binary bundle name pub fn rust_source_cargo( - cargo_project_folder: &str, - artifact_name: &str, - entrypoint: &str, + cargo_project_folder: impl AsRef, + artifact_name: impl Into>, + entrypoint: impl Into>, ) -> Self { - let manifest_path = format!("../rust-apps-wasm/{}/Cargo.toml", cargo_project_folder); - // dbg!(&pwd); let temp_dir = std::env::temp_dir(); - let target_dir = temp_dir.join(cargo_project_folder); - let output = Command::new("cargo") - .arg("build") - .arg("--manifest-path") - .arg(manifest_path) - .arg("--release") - // .arg("--bins") - .arg("--target=wasm32-unknown-unknown") - // .arg("--features=wasm-target") - .arg("--target-dir") - .arg(target_dir.clone()) - // compile std as part of crate graph compilation - // https://doc.rust-lang.org/cargo/reference/unstable.html#build-std - .arg("-Z") - .arg("build-std=core,alloc") - .arg("-Z") - // abort on panic without message formatting (core::fmt uses call_indirect) - .arg("build-std-features=panic_immediate_abort") - .output() - .expect("Failed to execute cargo build."); - if !output.status.success() { - eprintln!("pwd: {:?}", std::env::current_dir().unwrap()); - eprintln!("{}", String::from_utf8_lossy(&output.stderr)); - panic!("Rust to Wasm compilation failed!"); - } - let target_bin_file_path = Path::new(&target_dir) - .join("wasm32-unknown-unknown") - .join("release") - .join(artifact_name) - .with_extension("wasm"); - - let input_file = InputFile::from_path(target_bin_file_path).unwrap(); - let session = default_session(input_file); - let entrypoint = FunctionIdent { - module: Ident::new(Symbol::intern(artifact_name), SourceSpan::default()), - function: Ident::new(Symbol::intern(entrypoint.to_string()), SourceSpan::default()), - }; - CompilerTest { - session, - source: CompilerTestSource::RustCargo { - cargo_project_folder_name: cargo_project_folder.to_string(), - artifact_name: artifact_name.to_string(), - }, - entrypoint: Some(entrypoint), - ..Default::default() - } + let target_dir = temp_dir.join(cargo_project_folder.as_ref()); + let project_dir = Path::new("../rust-apps-wasm") + .join(cargo_project_folder.as_ref()) + .canonicalize() + .unwrap_or_else(|_| { + panic!( + "unknown project folder: ../rust-apps-wasm/{}", + cargo_project_folder.as_ref().display() + ) + }); + let config = CargoTest::new(artifact_name, project_dir) + .with_build_alloc(true) + .with_target_dir(target_dir) + .with_target("wasm32-unknown-unknown") + .with_entrypoint(entrypoint); + CompilerTestBuilder::new(config) } /// Set the Rust source code to compile - pub fn rust_source_program(rust_source: &str) -> Self { - let wasm_file = compile_rust_file(rust_source); - let session = default_session(wasm_file); - CompilerTest { - session, - source: CompilerTestSource::Rust(rust_source.to_string()), - ..Default::default() - } + pub fn rust_source_program(rust_source: impl Into>) -> Self { + let rust_source = rust_source.into(); + let name = format!("test_rust_{}", hash_string(&rust_source)); + CompilerTestBuilder::new(RustcTest::new(name, rust_source)) } /// Set the Rust source code to compile and add a binary operation test - pub fn rust_fn_body(rust_source: &str) -> Self { + pub fn rust_fn_body( + rust_source: &str, + midenc_flags: impl IntoIterator>, + ) -> Self { let rust_source = format!( r#" #![no_std] @@ -350,7 +647,7 @@ impl CompilerTest { #[panic_handler] fn my_panic(_info: &core::panic::PanicInfo) -> ! {{ - loop {{}} + core::arch::wasm32::unreachable() }} #[no_mangle] @@ -358,32 +655,26 @@ impl CompilerTest { "#, rust_source ); - let wasm_file = compile_rust_file(&rust_source); - let wasm_filestem = wasm_file.filestem().to_string(); - let session = default_session(wasm_file); - let entrypoint = FunctionIdent { - module: Ident { - name: Symbol::intern(wasm_filestem), - span: SourceSpan::default(), - }, - function: Ident { - name: Symbol::intern("entrypoint"), - span: SourceSpan::default(), - }, - }; - - CompilerTest { - session, - source: CompilerTestSource::Rust(rust_source.to_string()), - entrypoint: Some(entrypoint), - ..Default::default() - } + let name = format!("test_rust_{}", hash_string(&rust_source)); + let module_name = Ident::with_empty_span(Symbol::intern(&name)); + let mut builder = CompilerTestBuilder::new(RustcTest::new(name, rust_source)); + builder.with_midenc_flags(midenc_flags).with_entrypoint(FunctionIdent { + module: module_name, + function: Ident::with_empty_span(Symbol::intern("entrypoint")), + }); + builder } /// Set the Rust source code to compile with `miden-stdlib-sys` (stdlib + intrinsics) - pub fn rust_fn_body_with_stdlib_sys(name: &str, rust_source: &str, is_build_std: bool) -> Self { - let miden_stdlib_sys_path_str = stdlib_sys_crate_path(); - let proj = project(name) + pub fn rust_fn_body_with_stdlib_sys( + name: impl Into>, + source: &str, + is_build_std: bool, + midenc_flags: impl IntoIterator>, + ) -> Self { + let name = name.into(); + let stdlib_sys_path = stdlib_sys_crate_path(); + let proj = project(name.as_ref()) .file( "Cargo.toml", format!( @@ -396,7 +687,7 @@ impl CompilerTest { [dependencies] wee_alloc = {{ version = "0.4.5", default-features = false}} - miden-stdlib-sys = {{ path = "{miden_stdlib_sys_path_str}" }} + miden-stdlib-sys = {{ path = "{stdlib_sys_path}" }} [lib] crate-type = ["cdylib"] @@ -405,7 +696,9 @@ impl CompilerTest { panic = "abort" # optimize for size opt-level = "z" - "# + debug = true + "#, + stdlib_sys_path = stdlib_sys_path.display(), ) .as_str(), ) @@ -418,7 +711,7 @@ impl CompilerTest { #[panic_handler] fn my_panic(_info: &core::panic::PanicInfo) -> ! {{ - loop {{}} + core::arch::wasm32::unreachable() }} @@ -431,77 +724,292 @@ impl CompilerTest { #[no_mangle] pub extern "C" fn entrypoint{} "#, - rust_source + source ) .as_str(), ) .build(); - Self::rust_source_cargo_lib(proj.root(), name, is_build_std, Some("entrypoint".to_string())) + Self::rust_source_cargo_lib( + proj.root(), + name, + is_build_std, + Some("entrypoint".into()), + midenc_flags, + ) } - /// Set the Rust source code to compile with `miden-stdlib-sys` (stdlib + intrinsics) - pub fn rust_fn_body_with_sdk(name: &str, rust_source: &str, is_build_std: bool) -> Self { - let cwd = std::env::current_dir().unwrap(); - let miden_sdk_path = cwd.parent().unwrap().parent().unwrap().join("sdk").join("sdk"); - let miden_sdk_path_str = miden_sdk_path.to_str().unwrap(); - let proj = project(name) + /// Set the Rust source code to compile with `miden-sdk` (sdk + intrinsics) + pub fn rust_source_with_sdk( + name: impl Into>, + source: &str, + is_build_std: bool, + entrypoint: Option>, + midenc_flags: impl IntoIterator>, + ) -> Self { + let name = name.into(); + let sdk_path = sdk_crate_path(); + let proj = project(name.as_ref()) .file( "Cargo.toml", format!( - r#" - [package] - name = "{name}" - version = "0.0.1" - edition = "2015" - authors = [] - - [dependencies] - wee_alloc = {{ version = "0.4.5", default-features = false}} - miden-sdk = {{ path = "{miden_sdk_path_str}" }} - - [lib] - crate-type = ["cdylib"] - - [profile.release] - panic = "abort" - # optimize for size - opt-level = "z" - "# + r#"[package] +name = "{name}" +version = "0.0.1" +edition = "2021" +authors = [] + +[dependencies] +wee_alloc = {{ version = "0.4.5", default-features = false}} +miden-sdk = {{ path = "{sdk_path}" }} + +[lib] +crate-type = ["cdylib"] + +[profile.release] +panic = "abort" +# optimize for size +opt-level = "z" +debug = true +"#, + sdk_path = sdk_path.display(), ) .as_str(), ) .file( "src/lib.rs", format!( - r#" - #![no_std] - #![no_main] + r#"#![no_std] +#![no_main] - #[panic_handler] - fn my_panic(_info: &core::panic::PanicInfo) -> ! {{ - loop {{}} - }} +#[panic_handler] +fn my_panic(_info: &core::panic::PanicInfo) -> ! {{ + core::arch::wasm32::unreachable() +}} - #[global_allocator] - static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; - extern crate miden_sdk; - use miden_sdk::*; +extern crate miden_sdk; +use miden_sdk::*; - extern crate alloc; - use alloc::vec::Vec; +extern crate alloc; +use alloc::vec::Vec; - #[no_mangle] - pub extern "C" fn entrypoint{} - "#, - rust_source +{} +"#, + source ) .as_str(), ) .build(); - Self::rust_source_cargo_lib(proj.root(), name, is_build_std, Some("entrypoint".to_string())) + Self::rust_source_cargo_lib(proj.root(), name, is_build_std, entrypoint, midenc_flags) + } + + /// Like `rust_source_with_sdk`, but expects the source code to be the body of a function + /// which will be used as the entrypoint. + pub fn rust_fn_body_with_sdk( + name: impl Into>, + source: &str, + is_build_std: bool, + midenc_flags: impl IntoIterator>, + ) -> Self { + let source = format!("#[no_mangle]\npub extern \"C\" fn entrypoint{source}"); + Self::rust_source_with_sdk( + name, + &source, + is_build_std, + Some("entrypoint".into()), + midenc_flags, + ) + } +} + +/// Compile to different stages (e.g. Wasm, IR, MASM) and compare the results against expected +/// output +pub struct CompilerTest { + /// The Wasm translation configuration + pub config: WasmTranslationConfig, + /// The compiler session + pub session: Arc, + /// The artifact name from which this test is derived + artifact_name: Cow<'static, str>, + /// The entrypoint function to use when building the IR + entrypoint: Option, + /// The compiled IR + hir: Option, + /// The MASM source code + masm_src: Option, + /// The compiled IR MASM program + ir_masm_program: Option, String>>, + /// The compiled VM program + vm_masm_program: Option, String>>, +} + +impl fmt::Debug for CompilerTest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("CompilerTest") + .field("config", &self.config) + .field("session", &self.session) + .field("artifact_name", &self.artifact_name) + .field("entrypoint", &self.entrypoint) + .field_with("hir", |f| match self.hir.as_ref() { + None => f.debug_tuple("None").finish(), + Some(HirArtifact::Module(module)) => f + .debug_struct("Module") + .field("name", &module.name) + .field("entrypoint", &module.entrypoint()) + .field("is_kernel", &module.is_kernel()) + .field_with("functions", |f| { + f.debug_list() + .entries(module.functions().map(|fun| &fun.signature)) + .finish() + }) + .field_with("globals", |f| { + f.debug_list().entries(module.globals().iter()).finish() + }) + .finish_non_exhaustive(), + Some(HirArtifact::Program(program)) => f + .debug_struct("Program") + .field("is_executable", &program.is_executable()) + .field_with("modules", |f| { + f.debug_list().entries(program.modules().iter().map(|m| m.name)).finish() + }) + .field_with("libraries", |f| { + let mut map = f.debug_map(); + for (digest, lib) in program.libraries().iter() { + map.key(digest).value_with(|f| { + f.debug_list() + .entries(lib.exports().map(|proc| proc.to_string())) + .finish() + }); + } + map.finish() + }) + .finish_non_exhaustive(), + Some(HirArtifact::Component(component)) => f + .debug_struct("Component") + .field_with("exports", |f| { + f.debug_map().entries(component.exports().iter()).finish() + }) + .finish_non_exhaustive(), + }) + .finish_non_exhaustive() + } +} + +impl Default for CompilerTest { + fn default() -> Self { + Self { + config: WasmTranslationConfig::default(), + session: dummy_session(&[]), + artifact_name: "unknown".into(), + entrypoint: None, + hir: None, + masm_src: None, + ir_masm_program: None, + vm_masm_program: None, + } + } +} + +impl CompilerTest { + /// Return the name of the artifact this test is derived from + pub fn artifact_name(&self) -> &str { + self.artifact_name.as_ref() + } + + /// Compile the Wasm component from a Rust Cargo project using cargo-component + pub fn rust_source_cargo_component( + cargo_project_folder: impl AsRef, + config: WasmTranslationConfig, + ) -> Self { + CompilerTestBuilder::rust_source_cargo_component(cargo_project_folder, config).build() + } + + /// Set the Rust source code to compile a library Cargo project to Wasm module + pub fn rust_source_cargo_lib( + cargo_project_folder: impl AsRef, + artifact_name: impl Into>, + is_build_std: bool, + entry_func_name: Option>, + midenc_flags: impl IntoIterator>, + ) -> Self { + CompilerTestBuilder::rust_source_cargo_lib( + cargo_project_folder, + artifact_name, + is_build_std, + entry_func_name, + midenc_flags, + ) + .build() + } + + /// Set the Rust source code to compile using a Cargo project and binary bundle name + pub fn rust_source_cargo( + cargo_project_folder: &str, + artifact_name: impl Into>, + entrypoint: impl Into>, + ) -> Self { + CompilerTestBuilder::rust_source_cargo(cargo_project_folder, artifact_name, entrypoint) + .build() + } + + /// Set the Rust source code to compile + pub fn rust_source_program(rust_source: impl Into>) -> Self { + CompilerTestBuilder::rust_source_program(rust_source).build() + } + + /// Set the Rust source code to compile and add a binary operation test + pub fn rust_fn_body( + source: &str, + midenc_flags: impl IntoIterator>, + ) -> Self { + CompilerTestBuilder::rust_fn_body(source, midenc_flags).build() + } + + /// Set the Rust source code to compile with `miden-stdlib-sys` (stdlib + intrinsics) + pub fn rust_fn_body_with_stdlib_sys( + name: impl Into>, + source: &str, + is_build_std: bool, + midenc_flags: impl IntoIterator>, + ) -> Self { + CompilerTestBuilder::rust_fn_body_with_stdlib_sys(name, source, is_build_std, midenc_flags) + .build() + } + + /// Set the Rust source code to compile with `miden-sdk` (sdk + intrinsics) + pub fn rust_source_with_sdk( + name: impl Into>, + source: &str, + is_build_std: bool, + entrypoint: Option>, + midenc_flags: impl IntoIterator>, + ) -> Self { + CompilerTestBuilder::rust_source_with_sdk( + name, + source, + is_build_std, + entrypoint, + midenc_flags, + ) + .build() + } + + /// Like [Self::rust_source_with_sdk], but expects the source code to be a function parameter + /// list and body, rather than arbitrary source code. + /// + /// NOTE: It is valid to append additional sources _after_ the closing brace of the function + /// body. + pub fn rust_fn_body_with_sdk( + name: impl Into>, + source: &str, + is_build_std: bool, + midenc_flags: impl IntoIterator>, + ) -> Self { + CompilerTestBuilder::rust_fn_body_with_sdk(name, source, is_build_std, midenc_flags).build() } /// Compare the compiled Wasm against the expected output @@ -512,7 +1020,7 @@ impl CompilerTest { } fn wasm_to_ir(&self) -> HirArtifact { - let ir_component = translate(&self.wasm_bytes(), &self.config, &self.session.diagnostics) + let ir_component = translate(&self.wasm_bytes(), &self.config, &self.session) .expect("Failed to translate Wasm binary to IR component"); Box::new(ir_component).into() } @@ -529,18 +1037,22 @@ impl CompilerTest { pub fn expect_ir(&mut self, expected_hir_file: expect_test::ExpectFile) { match self.hir() { HirArtifact::Program(hir_program) => { - // Program does not implement pretty printer yet, use the first module - let ir_module = demangle( - hir_program - .modules() - .iter() - .take(1) - .collect::>() - .first() - .expect("no module in IR program") - .to_string() - .as_str(), - ); + // Program does not implement pretty printer yet, use the module containing the + // entrypoint function, or the first module found if no entrypoint is set + let ir_module = hir_program + .entrypoint() + .map(|entry| { + hir_program + .modules() + .find(&entry.module) + .get() + .expect("missing entrypoint module") + }) + .unwrap_or_else(|| { + hir_program.modules().iter().next().expect("expected at least one module") + }) + .to_string(); + let ir_module = demangle(&ir_module); expected_hir_file.assert_eq(&ir_module); } HirArtifact::Component(hir_component) => { @@ -592,7 +1104,7 @@ impl CompilerTest { /// The compiled Wasm component/module fn wasm_bytes(&self) -> Vec { - match &self.session.input.file { + match &self.session.inputs[0].file { InputType::Real(file_path) => fs::read(file_path) .unwrap_or_else(|_| panic!("Failed to read Wasm file: {}", file_path.display())), InputType::Stdin { name: _, input } => input.clone(), @@ -600,23 +1112,17 @@ impl CompilerTest { } pub(crate) fn compile_wasm_to_masm_program(&mut self) { - match midenc_compile::compile_to_memory(self.session.clone()).unwrap() { - midenc_compile::Compiled::Program(_p) => todo!("Program compilation not yet supported"), - midenc_compile::Compiled::Modules(modules) => { - let src = expected_masm_prog_source_from_modules( - &modules, - self.entrypoint, - &self.link_masm_modules, - ); - self.masm_src = Some(src); - let vm_prog = - vm_masm_prog_from_modules(&modules, self.entrypoint, &self.link_masm_modules); - self.vm_masm_program = Some(vm_prog.map_err(format_report)); - let ir_prog = - ir_masm_prog_from_modules(modules, self.entrypoint, &self.link_masm_modules); - self.ir_masm_program = Some(ir_prog.map_err(format_report)); - } - } + let output = midenc_compile::compile_to_memory(self.session.clone()) + .unwrap() + .expect("no codegen outputs produced, was linking disabled?"); + let midenc_codegen_masm::MasmArtifact::Executable(program) = output else { + panic!("expected executable output, got library"); + }; + let src = program.to_string(); + let vm_prog = program.assemble(&self.session).map_err(format_report); + self.masm_src = Some(src); + self.ir_masm_program = Some(Ok(program.into())); + self.vm_masm_program = Some(vm_prog); } } @@ -626,146 +1132,16 @@ fn format_report(report: miden_assembly::diagnostics::Report) -> String { PrintDiagnostic::new(report).to_string() } -fn wasm_artifact_path(cargo_project_folder: &Path, artifact_name: &str) -> PathBuf { - cargo_project_folder - .to_path_buf() - .join("target") - .join("wasm32-wasi") - .join("release") - .join(artifact_name) - .with_extension("wasm") -} - -/// Directs if we should do the Rust compilation step or not -pub fn skip_rust_compilation(cargo_project_folder: &Path, artifact_name: &str) -> bool { - let expected_wasm_artifact_path = wasm_artifact_path(cargo_project_folder, artifact_name); - let skip_rust = std::env::var("SKIP_RUST").is_ok() && expected_wasm_artifact_path.exists(); - if skip_rust { - eprintln!("Skipping Rust compilation"); - }; - skip_rust -} - -#[allow(clippy::vec_box)] -fn ir_masm_prog_from_modules( - modules: Vec>, - entrypoint: Option, - link_masm_modules: &LinkMasmModules, -) -> Result, Report> { - let mut p = midenc_codegen_masm::Program::empty(); - for (_path, _src) in link_masm_modules { - // TODO: implement linking of MASM source code - } - for module in modules.into_iter() { - p.insert(module); - } - p.entrypoint = entrypoint; - Ok(Box::new(p).freeze()) -} - -// Assemble the VM MASM program from the compiled IR MASM modules -fn vm_masm_prog_from_modules( - modules: &[Box], - entrypoint: Option, - link_masm_modules: &LinkMasmModules, -) -> Result, Report> { - let mut assembler = Assembler::default().with_library(&StdLibrary::default())?; - for (path, src) in link_masm_modules { - let options = miden_assembly::CompileOptions { - kind: ModuleKind::Library, - warnings_as_errors: false, - path: Some(path.clone()), - }; - assembler.add_module_with_options(src, options)?; - } - for module in modules { - let module_src = format!("{}", module); - //eprintln!("### {}\n", module.id); - //eprintln!("{}", &module_src); - let path = module.id.as_str().to_string(); - let library_path = LibraryPath::new(path).unwrap(); - // dbg!(&library_path); - let options = miden_assembly::CompileOptions { - kind: ModuleKind::Library, - warnings_as_errors: false, - path: Some(library_path), - }; - assembler.add_module_with_options(module_src, options)?; - } - if let Some(entrypoint) = entrypoint { - let prog_source = masm_prog_source(entrypoint); - assembler.assemble_program(prog_source).map(Arc::new) - } else { - todo!() - } -} - -// Generate the MASM program source code from the compiled IR MASM modules -fn expected_masm_prog_source_from_modules( - modules: &[Box], - entrypoint: Option, - link_masm_modules: &LinkMasmModules, -) -> String { - let mut src = String::new(); - for (path, module_src) in link_masm_modules { - writeln!(src, "# mod {path}\n").unwrap(); - writeln!(src, "{module_src}").unwrap(); - } - for module in modules { - let module_src = format!("{}", module); - let path = module.id.as_str().to_string(); - if !path.contains("intrinsic") { - // print only user modules and not intrinsic modules - writeln!(src, "# mod {path}\n").unwrap(); - write!(src, "{module_src}").unwrap(); - } - } - if let Some(entrypoint) = entrypoint { - let prog_source = masm_prog_source(entrypoint); - src.push_str(&prog_source); - } else { - todo!() - } - src -} - -// Generate the MASM program source code (call the entrypoint function) -fn masm_prog_source(entrypoint: FunctionIdent) -> String { - let module_name = entrypoint.module.as_str(); - let function_name = entrypoint.function.as_str(); - format!( - r#" -begin - exec.::{module_name}::{function_name} -end"#, - ) -} - -fn stdlib_sys_crate_path() -> String { +fn stdlib_sys_crate_path() -> PathBuf { let cwd = std::env::current_dir().unwrap(); - cwd.parent() - .unwrap() - .parent() - .unwrap() - .join("sdk") - .join("stdlib-sys") - .to_str() - .unwrap() - .to_string() + cwd.parent().unwrap().parent().unwrap().join("sdk").join("stdlib-sys") } -pub fn sdk_crate_path() -> String { +pub fn sdk_crate_path() -> PathBuf { let cwd = std::env::current_dir().unwrap(); - cwd.parent() - .unwrap() - .parent() - .unwrap() - .join("sdk") - .join("sdk") - .to_str() - .unwrap() - .to_string() + cwd.parent().unwrap().parent().unwrap().join("sdk").join("sdk") } + /// Get the directory for the top-level workspace fn get_workspace_dir() -> String { // Get the directory for the integration test suite project @@ -821,70 +1197,39 @@ fn wasm_to_wat(wasm_bytes: &[u8]) -> String { let wat = wasm_printer.print(wasm_bytes.as_ref()).unwrap(); wat } -fn compile_rust_file(rust_source: &str) -> InputFile { - let rustc_opts = [ - "-C", - "opt-level=z", // optimize for size - "--target", - "wasm32-unknown-unknown", - ]; - let file_name = format!("test_rust_{}", hash_string(rust_source)); - let proj_dir = std::env::temp_dir().join(&file_name); - if proj_dir.exists() { - fs::remove_dir_all(&proj_dir).unwrap(); - fs::create_dir_all(&proj_dir).unwrap(); - } else { - fs::create_dir_all(&proj_dir).unwrap(); - } - let input_file = proj_dir.join(format!("{file_name}.rs")); - let output_file = proj_dir.join(format!("{file_name}.wasm")); - fs::write(&input_file, rust_source).unwrap(); - let output = Command::new("rustc") - .args(rustc_opts) - .arg(&input_file) - .arg("-o") - .arg(&output_file) - .output() - .expect("Failed to execute rustc."); - if !output.status.success() { - eprintln!("{}", String::from_utf8_lossy(&output.stderr)); - panic!("Rust to Wasm compilation failed!"); - } - InputFile::from_path(output_file).unwrap() -} -fn dummy_session() -> Session { - let output_type = OutputType::Masm; - let output_types = OutputTypes::new(vec![OutputTypeSpec { - output_type, - path: None, - }]); - let options = Options::default().with_output_types(output_types); - Session::new( - Default::default(), - InputFile::from_path(PathBuf::from("dummy.wasm")).unwrap(), - None, - None, - None, - options, - None, - ) - .with_project_type(ProjectType::Library) +fn dummy_session(flags: &[&str]) -> Arc { + let dummy = InputFile::from_path(PathBuf::from("dummy.wasm")).unwrap(); + default_session([dummy], flags) } /// Create a default session for testing -pub fn default_session(input_file: InputFile) -> Arc { - let default_session = dummy_session(); - let session = Session::new( - Default::default(), - input_file, - None, - None, - None, - default_session.options, - None, - ) - .with_project_type(ProjectType::Library); +pub fn default_session(inputs: I, extra_flags: &[S]) -> Arc +where + I: IntoIterator, + S: AsRef, +{ + use midenc_hir::diagnostics::{ + reporting::{self, ReportHandlerOpts}, + DefaultSourceManager, + }; + + let result = reporting::set_hook(Box::new(|_| Box::new(ReportHandlerOpts::new().build()))); + if result.is_ok() { + reporting::set_panic_hook(); + } + + let mut inputs = inputs.into_iter(); + let input_file = inputs.next().expect("must provide at least one input file"); + let mut flags = vec!["--debug", "-l", "std"]; + flags.extend(extra_flags.iter().map(|flag| flag.as_ref())); + let source_manager = Arc::new(DefaultSourceManager::default()); + let mut options = midenc_compile::CompilerOptions::parse_options(&flags); + options.output_types.insert(OutputType::Masm, None); + let mut session = Session::new(input_file, None, None, None, options, None, source_manager); + for extra_input in inputs { + session.inputs.push(extra_input); + } Arc::new(session) } diff --git a/tests/integration/src/exec_vm.rs b/tests/integration/src/exec_vm.rs index ca2a950b6..3070f146f 100644 --- a/tests/integration/src/exec_vm.rs +++ b/tests/integration/src/exec_vm.rs @@ -1,23 +1,338 @@ +use std::collections::{BTreeMap, VecDeque}; + +use miden_assembly::library::CompiledLibrary; use miden_core::{Program, StackInputs}; -use miden_processor::{DefaultHost, ExecutionError, ExecutionOptions}; +use miden_processor::{AdviceInputs, DefaultHost, ExecutionError, MastForest, Process}; use midenc_hir::Felt; +use midenc_session::Session; -use crate::felt_conversion::TestFelt; +use crate::felt_conversion::{PopFromStack, TestFelt}; -/// Execute the program using the VM with the given arguments -/// Arguments are expected to be in the order they are passed to the entrypoint function -pub fn execute_vm(program: &Program, args: &[Felt]) -> Vec { - // Reverse the arguments to counteract the StackInputs::new() reversing them into a stack - let stack_inputs = StackInputs::new(args.to_vec()).expect("invalid stack inputs"); - dbg!(&stack_inputs); - let trace = miden_processor::execute( - program, - stack_inputs, - DefaultHost::default(), - ExecutionOptions::default(), - ) - .expect("failed to execute program on VM"); - trace.stack_outputs().stack().iter().map(|i| TestFelt(*i)).collect() +/// A test executor for Miden VM tests +pub struct MidenExecutor { + stack: StackInputs, + advice: AdviceInputs, + libraries: Vec, +} +impl MidenExecutor { + /// Construct an executor with the given arguments on the operand stack + pub fn new(args: Vec) -> Self { + Self { + stack: StackInputs::new(args).expect("invalid stack inputs"), + advice: AdviceInputs::default(), + libraries: Default::default(), + } + } + + /// Set the contents of memory for the shadow stack frame of the entrypoint + pub fn with_advice_inputs(&mut self, advice: AdviceInputs) -> &mut Self { + self.advice.extend(advice); + self + } + + /// Add a [CompiledLibrary] to the execution context + pub fn with_library(&mut self, lib: &CompiledLibrary) -> &mut Self { + self.libraries.push(lib.mast_forest().clone()); + self + } + + /// Execute the given program, producing a trace + pub fn execute(mut self, program: &Program, session: &Session) -> MidenExecutionTrace { + use std::collections::BTreeSet; + + use miden_processor::{MemAdviceProvider, ProcessState, VmStateIterator}; + + let advice_provider = MemAdviceProvider::from(self.advice); + let mut host = DefaultHost::new(advice_provider); + for lib in core::mem::take(&mut self.libraries) { + host.load_mast_forest(lib); + } + //dbg!(&self.stack); + let mut process = Process::new_debug(program.kernel().clone(), self.stack, host); + let root_context = process.ctx(); + let result = process.execute(program); + let mut iter = VmStateIterator::new(process, result.clone()); + let mut contexts = BTreeSet::default(); + let mut last_state: Option = None; + for (i, state) in iter.by_ref().enumerate() { + match state { + Ok(state) => { + contexts.insert(state.ctx); + if let Some(op) = state.op { + match op { + miden_core::Operation::MLoad => { + let load_addr = last_state + .as_ref() + .map(|state| state.stack[0].as_int()) + .unwrap(); + let loaded = match state + .memory + .binary_search_by_key(&load_addr, |&(addr, _)| addr) + { + Ok(index) => state.memory[index].1[0].as_int(), + Err(_) => 0, + }; + //dbg!(load_addr, loaded, format!("{loaded:08x}")); + } + miden_core::Operation::MLoadW => { + let load_addr = last_state + .as_ref() + .map(|state| state.stack[0].as_int()) + .unwrap(); + let loaded = match state + .memory + .binary_search_by_key(&load_addr, |&(addr, _)| addr) + { + Ok(index) => { + let word = state.memory[index].1; + [ + word[0].as_int(), + word[1].as_int(), + word[2].as_int(), + word[3].as_int(), + ] + } + Err(_) => [0; 4], + }; + let loaded_bytes = { + let word = loaded; + let a = (word[0] as u32).to_be_bytes(); + let b = (word[1] as u32).to_be_bytes(); + let c = (word[2] as u32).to_be_bytes(); + let d = (word[3] as u32).to_be_bytes(); + let bytes = [ + a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3], c[0], c[1], + c[2], c[3], d[0], d[1], d[2], d[3], + ]; + u128::from_be_bytes(bytes) + }; + //dbg!(load_addr, loaded, format!("{loaded_bytes:032x}")); + } + miden_core::Operation::MStore => { + let store_addr = last_state + .as_ref() + .map(|state| state.stack[0].as_int()) + .unwrap(); + let stored = match state + .memory + .binary_search_by_key(&store_addr, |&(addr, _)| addr) + { + Ok(index) => state.memory[index].1[0].as_int(), + Err(_) => 0, + }; + //dbg!(store_addr, stored, format!("{stored:08x}")); + } + miden_core::Operation::MStoreW => { + let store_addr = last_state + .as_ref() + .map(|state| state.stack[0].as_int()) + .unwrap(); + let stored = { + let memory = state + .memory + .iter() + .find_map(|(addr, word)| { + if addr == &store_addr { + Some(word) + } else { + None + } + }) + .unwrap(); + let a = memory[0].as_int(); + let b = memory[1].as_int(); + let c = memory[2].as_int(); + let d = memory[3].as_int(); + [a, b, c, d] + }; + let stored_bytes = { + let word = stored; + let a = (word[0] as u32).to_be_bytes(); + let b = (word[1] as u32).to_be_bytes(); + let c = (word[2] as u32).to_be_bytes(); + let d = (word[3] as u32).to_be_bytes(); + let bytes = [ + a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3], c[0], c[1], + c[2], c[3], d[0], d[1], d[2], d[3], + ]; + u128::from_be_bytes(bytes) + }; + //dbg!(store_addr, stored, format!("{stored_bytes:032x}")); + } + _ => (), + } + } + last_state = Some(state); + } + Err(err) => { + render_execution_error(err, i, last_state.as_ref(), session); + } + } + } + let (_, _, _, chiplets, _) = iter.into_parts(); + let mut memories = std::collections::BTreeMap::default(); + for context in contexts { + let mem = chiplets.get_mem_state_at( + context, + last_state + .as_ref() + .map(|state| state.clk) + .unwrap_or(miden_processor::RowIndex::from(0)), + ); + memories.insert(context, mem); + } + let outputs = result.unwrap().stack().iter().copied().map(TestFelt).collect(); + MidenExecutionTrace { + root_context, + outputs, + memories, + } + } + + /// Execute a program, parsing the operand stack outputs as a value of type `T` + pub fn execute_into(self, program: &Program, session: &Session) -> T + where + T: PopFromStack + PartialEq, + { + let out = self.execute(program, session); + out.parse_result().expect("invalid result") + } +} + +pub struct MidenExecutionTrace { + root_context: miden_processor::ContextId, + outputs: VecDeque, + memories: BTreeMap>, +} +impl MidenExecutionTrace { + pub fn parse_result(&self) -> Result + where + T: PopFromStack, + { + let mut stack = self.outputs.clone(); + T::try_pop(&mut stack) + } + + #[inline] + pub fn into_outputs(self) -> VecDeque { + self.outputs + } + + /// Read the word at the given Miden memory address + pub fn read_memory_word(&self, addr: u32) -> Option<[Felt; 4]> { + use miden_core::FieldElement; + + let words = self.memories.get(&self.root_context)?; + let addr = addr as u64; + match words.binary_search_by_key(&addr, |item| item.0) { + Ok(index) => Some(words[index].1), + Err(_) => Some([Felt::ZERO; 4]), + } + } + + /// Read the word at the given Miden memory address and element offset + pub fn read_memory_element(&self, addr: u32, index: u8) -> Option { + assert!(index < 4, "invalid element index"); + self.read_memory_word(addr).map(|word| word[index as usize]) + } + + /// Read a value of the given type, given an address in Rust's address space + pub fn read_from_rust_memory(&self, addr: u32) -> Option + where + T: core::any::Any + PopFromStack, + { + use core::any::TypeId; + + use midenc_codegen_masm::NativePtr; + + let ptr = NativePtr::from_ptr(addr); + if TypeId::of::() == TypeId::of::() { + assert_eq!(ptr.offset, 0, "cannot read values of type Felt from unaligned addresses"); + let elem = self.read_memory_element(ptr.waddr, ptr.index)?; + let mut stack = VecDeque::from([TestFelt(elem)]); + return Some(T::try_pop(&mut stack).unwrap_or_else(|_| { + panic!( + "could not decode a value of type {} from {}", + core::any::type_name::(), + addr + ) + })); + } + match core::mem::size_of::() { + n if n < 4 => { + if (4 - ptr.offset as usize) < n { + todo!("unaligned, split read") + } + let elem = self.read_memory_element(ptr.waddr, ptr.index)?; + let elem = if ptr.offset > 0 { + let mask = 2u64.pow(32 - (ptr.offset as u32 * 8)) - 1; + let elem = elem.as_int() & mask; + Felt::new(elem << (ptr.offset as u64 * 8)) + } else { + elem + }; + let mut stack = VecDeque::from([TestFelt(elem)]); + Some(T::try_pop(&mut stack).unwrap_or_else(|_| { + panic!( + "could not decode a value of type {} from {}", + core::any::type_name::(), + addr + ) + })) + } + 4 if ptr.offset > 0 => { + todo!("unaligned, split read") + } + 4 => { + let elem = self.read_memory_element(ptr.waddr, ptr.index)?; + let mut stack = VecDeque::from([TestFelt(elem)]); + Some(T::try_pop(&mut stack).unwrap_or_else(|_| { + panic!( + "could not decode a value of type {} from {}", + core::any::type_name::(), + addr + ) + })) + } + n if n <= 16 && ptr.offset > 0 => { + todo!("unaligned, split read") + } + n if n <= 16 => { + let word = self.read_memory_word(ptr.waddr)?; + let mut stack = VecDeque::from_iter(word.into_iter().map(TestFelt)); + Some(T::try_pop(&mut stack).unwrap_or_else(|_| { + panic!( + "could not decode a value of type {} from {}", + core::any::type_name::(), + addr + ) + })) + } + n => { + let mut buf = VecDeque::default(); + let chunks_needed = n / 4; + if ptr.offset > 0 { + todo!() + } else if ptr.index > 0 { + todo!() + } else { + for i in 0..chunks_needed { + let word = self + .read_memory_word(ptr.waddr + i as u32) + .expect("invalid memory access"); + buf.extend(word.into_iter().map(TestFelt)); + } + } + Some(T::try_pop(&mut buf).unwrap_or_else(|_| { + panic!( + "could not decode a value of type {} from {}", + core::any::type_name::(), + addr + ) + })) + } + } + } } /// Execute the program using the VM with the given arguments @@ -41,3 +356,70 @@ pub fn execute_vm_tracing( } Ok(last_stack.into_iter().map(TestFelt).collect()) } + +fn render_execution_error( + err: ExecutionError, + step: usize, + last_state: Option<&miden_processor::VmState>, + session: &Session, +) -> ! { + use midenc_hir::diagnostics::{ + miette::miette, reporting::PrintDiagnostic, LabeledSpan, SourceManagerExt, + }; + + if let Some(last_state) = last_state { + let mut source_code = None; + let mut labels = vec![]; + let last_op; + let last_context_name; + match dbg!(last_state.asmop.as_ref()) { + Some(op) => { + last_op = op.op().to_string(); + last_context_name = op.context_name(); + let asmop = op.as_ref(); + if let Some(loc) = dbg!(asmop.location()) { + let path = std::path::Path::new(loc.path.as_ref()); + source_code = if path.exists() { + session.source_manager.load_file(path).ok() + } else { + session.source_manager.get_by_path(loc.path.as_ref()) + }; + labels.push(LabeledSpan::new_with_span( + None, + loc.start.to_usize()..loc.end.to_usize(), + )); + } + } + None => { + last_op = + last_state.op.map(|op| op.to_string()).unwrap_or_else(|| "N/A".to_string()); + last_context_name = "N/A"; + } + }; + let stack = last_state.stack.iter().map(|elem| elem.as_int()); + let stack = midenc_hir::DisplayValues::new(stack); + let help = format!( + " +last known context: {last_context_name} +last known op: {last_op} +last known frame pointer: {fmp} (frame pointer starts at 2^30) +last known operand stack: [{stack}]", + fmp = last_state.fmp.as_int() + ); + let report = miette!( + help = help, + labels = labels, + "program execution failed at step {step} (cycle {cycle}): {err}", + step = step, + cycle = last_state.clk, + ); + let report = match source_code { + Some(source) => report.with_source_code(source), + None => report, + }; + + panic!("{}", PrintDiagnostic::new(report)); + } else { + panic!("program execution failed at step {step}: {err}"); + } +} diff --git a/tests/integration/src/felt_conversion.rs b/tests/integration/src/felt_conversion.rs index ef5ac737e..08c43985b 100644 --- a/tests/integration/src/felt_conversion.rs +++ b/tests/integration/src/felt_conversion.rs @@ -209,6 +209,57 @@ impl PopFromStack for TestFelt { } } +impl PushToStack for [u8; N] { + fn try_push(&self, stack: &mut Vec) { + let mut iter = self.iter().array_chunks::<4>(); + let buf_size = (self.len() / 4) + (self.len() % 4 == 0) as usize; + let mut buf = vec![0u32; buf_size]; + let mut i = 0; + for chunk in iter.by_ref() { + let n = u32::from_be_bytes([*chunk[0], *chunk[1], *chunk[2], *chunk[3]]); + buf[i] = n; + i += 1; + } + if let Some(rest) = iter.into_remainder() { + let mut n_buf = [0u8; 4]; + for (i, byte) in rest.into_iter().enumerate() { + n_buf[i] = *byte; + } + buf[i] = u32::from_be_bytes(n_buf); + } + for chunk in buf.into_iter().rev() { + PushToStack::try_push(&chunk, stack); + } + } +} + +impl PopFromStack for [u8; N] { + fn try_pop(stack: &mut VecDeque) -> Result { + let mut out = [0u8; N]; + + let byte_size = out.len(); + let mut i = 0; + while i < byte_size { + let chunk: u32 = PopFromStack::try_pop(stack).expect("invalid u32"); + let bytes = chunk.to_be_bytes(); + if i + 4 > byte_size { + for byte in bytes[..(byte_size - i)].iter().copied() { + out[i] = byte; + i += 1; + } + break; + } else { + for byte in bytes.iter().copied() { + out[i] = byte; + i += 1; + } + } + } + + Ok(out) + } +} + /// Wrapper around `Felt` that implements `From` for a bunch of types that are want to support in /// tests #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/tests/integration/src/lib.rs b/tests/integration/src/lib.rs index 9eaf01ea9..a2a37f058 100644 --- a/tests/integration/src/lib.rs +++ b/tests/integration/src/lib.rs @@ -1,6 +1,7 @@ //! Compilation and semantic tests for the whole compiler pipeline - -#![deny(warnings)] +#![feature(iter_array_chunks)] +#![feature(debug_closure_helpers)] +//#![deny(warnings)] #![deny(missing_docs)] mod cargo_proj; @@ -9,9 +10,9 @@ mod exec_emulator; mod exec_vm; pub(crate) mod felt_conversion; -pub use compiler_test::{default_session, CompilerTest}; +pub use compiler_test::{default_session, CargoTest, CompilerTest, CompilerTestBuilder, RustcTest}; pub use exec_emulator::execute_emulator; -pub use exec_vm::execute_vm; +pub use exec_vm::MidenExecutor; #[cfg(test)] mod rust_masm_tests; diff --git a/tests/integration/src/rust_masm_tests/abi_transform/stdlib.rs b/tests/integration/src/rust_masm_tests/abi_transform/stdlib.rs index 34e5e0491..323757e73 100644 --- a/tests/integration/src/rust_masm_tests/abi_transform/stdlib.rs +++ b/tests/integration/src/rust_masm_tests/abi_transform/stdlib.rs @@ -1,4 +1,5 @@ use core::panic; +use std::collections::VecDeque; use expect_test::expect_file; use miden_core::utils::group_slice_elements; @@ -9,38 +10,66 @@ use proptest::{ test_runner::{TestError, TestRunner}, }; -use crate::{execute_vm, felt_conversion::TestFelt, CompilerTest}; +use crate::{ + felt_conversion::{PopFromStack, PushToStack, TestFelt}, + CompilerTest, MidenExecutor, +}; -#[ignore = "until the VM stack overflow during the MASM generation is fixed"] #[test] +#[ignore = "pending rodata fixes"] fn test_blake3_hash() { - let main_fn = "(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { miden_stdlib_sys::blake3_hash_2to1(a, \ - b) }" - .to_string(); + let main_fn = + "(a: [u8; 32]) -> [u8; 32] { miden_stdlib_sys::blake3_hash_1to1(a) }".to_string(); let artifact_name = "abi_transform_stdlib_blake3_hash"; - let mut test = CompilerTest::rust_fn_body_with_stdlib_sys(artifact_name, &main_fn, true); + let mut test = CompilerTest::rust_fn_body_with_stdlib_sys( + artifact_name, + &main_fn, + true, + ["--test-harness".into()], + ); // Test expected compilation artifacts test.expect_wasm(expect_file![format!("../../../expected/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../../expected/{artifact_name}.hir")]); test.expect_masm(expect_file![format!("../../../expected/{artifact_name}.masm")]); + let ir_program = test.ir_masm_program(); let vm_program = test.vm_masm_program(); + let advice_inputs = ir_program.advice_inputs(); + + println!("{ir_program}"); + panic!("oops"); + // Run the Rust and compiled MASM code against a bunch of random inputs and compare the results - let res = TestRunner::default().run(&any::<[u8; 64]>(), move |ibytes| { + let res = TestRunner::default().run(&any::<[u8; 32]>(), move |ibytes| { let hash_bytes = blake3::hash(&ibytes); let rs_out = hash_bytes.as_bytes(); - let rs_ofelts = group_slice_elements::(rs_out) - .iter() - .map(|&bytes| u32::from_le_bytes(bytes).into()) - .collect::>(); - let ifelts = group_slice_elements::(&ibytes) - .iter() - .map(|&bytes| u32::from_le_bytes(bytes).into()) - .collect::>(); - let vm_out = execute_vm(&vm_program, &ifelts); - prop_assert_eq!(rs_ofelts, vm_out, "VM output mismatch"); + let mut frame = Vec::::default(); + PushToStack::try_push(&ibytes, &mut frame); // words + PushToStack::try_push(&2u32, &mut frame); // num_words + PushToStack::try_push(&0u32, &mut frame); // dest_ptr + //let rs_ofelts = group_slice_elements::(rs_out) + // .iter() + // .map(|&bytes| u32::from_le_bytes(bytes).into()) + // .collect::>(); + //let ifelts = group_slice_elements::(&ibytes) + // .iter() + // .map(|&bytes| u32::from_le_bytes(bytes).into()) + // .collect::>(); + dbg!(&ibytes, &frame, rs_out); + // Arguments are: [hash_input_ptr, hash_output_ptr] + let mut exec = MidenExecutor::new(vec![Felt::new(0), Felt::new(128 * 1024)]); + let mut advice_inputs = advice_inputs.clone(); + advice_inputs.extend_stack(frame); + exec.with_advice_inputs(advice_inputs); + let trace = exec.execute(&vm_program, &test.session); + let vm_out: [u8; 32] = trace + .read_from_rust_memory(128 * 1024) + .expect("expected memory to have been written"); + dbg!(&vm_out); + prop_assert_eq!(rs_out, &vm_out, "VM output mismatch"); Ok(()) }); + match res { Err(TestError::Fail(_, value)) => { panic!("Found minimal(shrinked) failing case: {:?}", value); diff --git a/tests/integration/src/rust_masm_tests/abi_transform/tx_kernel.rs b/tests/integration/src/rust_masm_tests/abi_transform/tx_kernel.rs index 0af3771bc..658cba1c6 100644 --- a/tests/integration/src/rust_masm_tests/abi_transform/tx_kernel.rs +++ b/tests/integration/src/rust_masm_tests/abi_transform/tx_kernel.rs @@ -5,7 +5,7 @@ use miden_assembly::LibraryPath; use miden_core::{Felt, FieldElement}; use miden_processor::ExecutionError; -use crate::{exec_vm::execute_vm_tracing, execute_emulator, execute_vm, CompilerTest}; +use crate::{exec_vm::execute_vm_tracing, execute_emulator, CompilerTestBuilder, MidenExecutor}; #[allow(unused)] fn setup_log() { @@ -19,30 +19,28 @@ fn setup_log() { fn test_get_inputs(test_name: &str, expected_inputs: Vec) { assert!(expected_inputs.len() == 4, "for now only word-sized inputs are supported"); - let mut main_fn = String::new(); - writeln!(main_fn, "() -> Vec {{\n").unwrap(); - writeln!(main_fn, " let inputs = get_inputs();").unwrap(); - // for (_i, _expected_input) in expected_inputs.iter().enumerate() { - // TODO: use miden asserts once they are implemented - // writeln!(main_fn, " assert_eq!(inputs[{i}], {expected_input});").unwrap(); - // } - writeln!(main_fn, " inputs").unwrap(); - writeln!(main_fn, "}}").unwrap(); - + let masm = format!( + " +export.get_inputs + push.{expect1}.{expect2}.{expect3}.{expect4} + # copy pointer to top of the stack + dup.4 + mem_storew + # push the inputs len on the stack + push.4 +end +", + expect1 = expected_inputs.first().map(|i| i.as_int()).unwrap_or(0), + expect2 = expected_inputs.get(1).map(|i| i.as_int()).unwrap_or(0), + expect3 = expected_inputs.get(2).map(|i| i.as_int()).unwrap_or(0), + expect4 = expected_inputs.get(3).map(|i| i.as_int()).unwrap_or(0), + ); + let main_fn = "() -> Vec { get_inputs() }"; let artifact_name = format!("abi_transform_tx_kernel_get_inputs_{}", test_name); - let mut test = CompilerTest::rust_fn_body_with_sdk(&artifact_name, &main_fn, true); - let mut masm = String::new(); - writeln!(masm, "export.get_inputs").unwrap(); - for expected_input in expected_inputs.iter() { - writeln!(masm, " push.{expected_input}").unwrap(); - } - // copy the pointer to the top of the stack - writeln!(masm, " dup.4").unwrap(); - writeln!(masm, " mem_storew").unwrap(); - // push the inputs len on the stack - writeln!(masm, " push.{}", expected_inputs.len()).unwrap(); - writeln!(masm, " end").unwrap(); - test.link_masm_modules = vec![(LibraryPath::new("miden::note").unwrap(), masm)]; + let mut test_builder = + CompilerTestBuilder::rust_fn_body_with_sdk(artifact_name.clone(), main_fn, true, None); + test_builder.link_with_masm_module("miden::note", masm); + let mut test = test_builder.build(); // Test expected compilation artifacts test.expect_wasm(expect_file![format!("../../../expected/{artifact_name}.wat")]); @@ -50,13 +48,18 @@ fn test_get_inputs(test_name: &str, expected_inputs: Vec) { test.expect_masm(expect_file![format!("../../../expected/{artifact_name}.masm")]); let vm_program = test.vm_masm_program(); - // let vm_out = execute_vm_tracing(&vm_program, &[]).unwrap(); + + let exec = MidenExecutor::new(vec![]); + let trace = exec.execute(&vm_program, &test.session); + let vm_out = trace.into_outputs(); + dbg!(&vm_out); // let ir_program = test.ir_masm_program(); // let emul_out = execute_emulator(ir_program.clone(), &[]); } #[test] +#[ignore = "pending rodata fixes"] fn test_get_inputs_4() { test_get_inputs("4", vec![u32::MAX.into(), Felt::ONE, Felt::ZERO, u32::MAX.into()]); } diff --git a/tests/integration/src/rust_masm_tests/apps.rs b/tests/integration/src/rust_masm_tests/apps.rs index 02df1f3cf..ad135c0e0 100644 --- a/tests/integration/src/rust_masm_tests/apps.rs +++ b/tests/integration/src/rust_masm_tests/apps.rs @@ -5,9 +5,8 @@ use midenc_hir::Felt; use proptest::{prelude::*, test_runner::TestRunner}; use crate::{ - execute_vm, felt_conversion::{PopFromStack, PushToStack}, - CompilerTest, + CompilerTest, MidenExecutor, }; #[test] @@ -28,10 +27,10 @@ fn fib() { let mut args = Vec::::default(); PushToStack::try_push(&a, &mut args); - let mut out = VecDeque::from(execute_vm(vm_program, &args)); - dbg!(&out); - let vm_out = u32::try_pop(&mut out).expect("invalid result"); - prop_assert_eq!(rust_out, vm_out); + let exec = MidenExecutor::new(args); + let output: u32 = exec.execute_into(vm_program, &test.session); + dbg!(output); + prop_assert_eq!(rust_out, output); // args.reverse(); // let emul_out: u32 = // execute_emulator(ir_masm.clone(), &args).first().unwrap().clone().into(); diff --git a/tests/integration/src/rust_masm_tests/components.rs b/tests/integration/src/rust_masm_tests/components.rs index 17f4db4a2..8bcae9705 100644 --- a/tests/integration/src/rust_masm_tests/components.rs +++ b/tests/integration/src/rust_masm_tests/components.rs @@ -77,7 +77,7 @@ fn wcm_no_imports() { ) .build(); let mut test = CompilerTest::rust_source_cargo_component(proj.root(), config); - let artifact_name = test.source.artifact_name(); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!("../../expected/components/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../expected/components/{artifact_name}.hir")]); } @@ -196,7 +196,8 @@ fn wcm_import() { .build(); let mut test = CompilerTest::rust_source_cargo_component(proj.root(), config); - let artifact_name = test.source.artifact_name(); + dbg!(&test); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!("../../expected/components/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../expected/components/{artifact_name}.hir")]); diff --git a/tests/integration/src/rust_masm_tests/instructions.rs b/tests/integration/src/rust_masm_tests/instructions.rs index c7ab8555e..87a957c8c 100644 --- a/tests/integration/src/rust_masm_tests/instructions.rs +++ b/tests/integration/src/rust_masm_tests/instructions.rs @@ -21,7 +21,7 @@ macro_rules! test_bin_op { let b_ty_str = stringify!($b_ty); let res_ty_str = stringify!($res_ty); let main_fn = format!("(a: {a_ty_str}, b: {b_ty_str}) -> {res_ty_str} {{ a {op_str} b }}"); - let mut test = CompilerTest::rust_fn_body(&main_fn); + let mut test = CompilerTest::rust_fn_body(&main_fn, None); // Test expected compilation artifacts let artifact_name = format!("{}_{}", stringify!($name), stringify!($a_ty)); test.expect_wasm(expect_file![format!("../../expected/{artifact_name}.wat")]); @@ -39,7 +39,7 @@ macro_rules! test_bin_op { let mut args = Vec::::default(); PushToStack::try_push(&b, &mut args); PushToStack::try_push(&a, &mut args); - run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args) + run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args, &test.session) }); match res { Err(TestError::Fail(_, value)) => { @@ -62,7 +62,7 @@ macro_rules! test_unary_op { let op_ty_str = stringify!($op_ty); let res_ty_str = stringify!($op_ty); let main_fn = format!("(a: {op_ty_str}) -> {res_ty_str} {{ {op_str}a }}"); - let mut test = CompilerTest::rust_fn_body(&main_fn); + let mut test = CompilerTest::rust_fn_body(&main_fn, None); // Test expected compilation artifacts let artifact_name = format!("{}_{}", stringify!($name), stringify!($op_ty)); test.expect_wasm(expect_file![format!("../../expected/{artifact_name}.wat")]); @@ -78,7 +78,7 @@ macro_rules! test_unary_op { dbg!(&rs_out); let mut args = Vec::::default(); a.try_push(&mut args); - run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args) + run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args, &test.session) }); match res { Err(TestError::Fail(_, value)) => { @@ -102,7 +102,7 @@ macro_rules! test_func_two_arg { let b_ty_str = stringify!($b_ty); let res_ty_str = stringify!($res_ty); let main_fn = format!("(a: {a_ty_str}, b: {b_ty_str}) -> {res_ty_str} {{ {func_name_str}(a, b) }}"); - let mut test = CompilerTest::rust_fn_body(&main_fn); + let mut test = CompilerTest::rust_fn_body(&main_fn, None); // Test expected compilation artifacts let artifact_name = format!("{}_{}_{}", stringify!($func), stringify!($a_ty), stringify!($b_ty)); test.expect_wasm(expect_file![format!("../../expected/{artifact_name}.wat")]); @@ -119,7 +119,7 @@ macro_rules! test_func_two_arg { let mut args = Vec::::default(); b.try_push(&mut args); a.try_push(&mut args); - run_masm_vs_rust(rust_out, &vm_program, ir_masm.clone(), &args) + run_masm_vs_rust(rust_out, &vm_program, ir_masm.clone(), &args, &test.session) }); match res { Err(TestError::Fail(_, value)) => { diff --git a/tests/integration/src/rust_masm_tests/intrinsics.rs b/tests/integration/src/rust_masm_tests/intrinsics.rs index 65188a688..af723bf3f 100644 --- a/tests/integration/src/rust_masm_tests/intrinsics.rs +++ b/tests/integration/src/rust_masm_tests/intrinsics.rs @@ -24,7 +24,7 @@ macro_rules! test_bin_op { let res_ty_str = stringify!($res_ty); let main_fn = format!("(a: {op_ty_str}, b: {op_ty_str}) -> {res_ty_str} {{ a {op_str} b }}"); let artifact_name = format!("{}_{}", stringify!($name), stringify!($op_ty).to_lowercase()); - let mut test = CompilerTest::rust_fn_body_with_stdlib_sys(&artifact_name, &main_fn, false); + let mut test = CompilerTest::rust_fn_body_with_stdlib_sys(artifact_name.clone(), &main_fn, false, None); // Test expected compilation artifacts test.expect_wasm(expect_file![format!("../../expected/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../expected/{artifact_name}.hir")]); @@ -43,7 +43,7 @@ macro_rules! test_bin_op { let mut args = Vec::::default(); PushToStack::try_push(&b, &mut args); PushToStack::try_push(&a, &mut args); - run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args) + run_masm_vs_rust(rs_out, &vm_program, ir_program.clone(), &args, &test.session) }); match res { Err(TestError::Fail(_, value)) => { @@ -66,7 +66,7 @@ macro_rules! test_compile_comparison_op { let op_str = stringify!($op); let main_fn = format!("(a: Felt, b: Felt) -> bool {{ a {op_str} b }}"); let artifact_name = format!("{}_felt", stringify!($name)); - let mut test = CompilerTest::rust_fn_body_with_stdlib_sys(&artifact_name, &main_fn, false); + let mut test = CompilerTest::rust_fn_body_with_stdlib_sys(artifact_name.clone(), &main_fn, false, None); // Test expected compilation artifacts test.expect_wasm(expect_file![format!("../../expected/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../expected/{artifact_name}.hir")]); diff --git a/tests/integration/src/rust_masm_tests/mod.rs b/tests/integration/src/rust_masm_tests/mod.rs index 79ac7deb3..a5971bcf9 100644 --- a/tests/integration/src/rust_masm_tests/mod.rs +++ b/tests/integration/src/rust_masm_tests/mod.rs @@ -4,9 +4,10 @@ use std::{collections::VecDeque, sync::Arc}; use miden_core::Felt; +use midenc_session::Session; use proptest::{prop_assert_eq, test_runner::TestCaseError}; -use crate::{execute_emulator, execute_vm, felt_conversion::PopFromStack}; +use crate::{execute_emulator, felt_conversion::PopFromStack, MidenExecutor}; mod abi_transform; mod apps; @@ -21,14 +22,17 @@ pub fn run_masm_vs_rust( vm_program: &miden_core::Program, ir_program: Arc, args: &[Felt], + session: &Session, ) -> Result<(), TestCaseError> where T: Clone + PopFromStack + std::cmp::PartialEq + std::fmt::Debug, { - let mut out = VecDeque::from(execute_vm(vm_program, args)); - let vm_out = T::try_pop(&mut out).expect("invalid result"); - dbg!(&vm_out); - prop_assert_eq!(rust_out.clone(), vm_out, "VM output mismatch"); + let mut exec = MidenExecutor::new(args.to_vec()); + for lib in ir_program.link_libraries() { + exec.with_library(lib); + } + let output = exec.execute_into(vm_program, session); + prop_assert_eq!(rust_out.clone(), output, "VM output mismatch"); // TODO: Uncomment after https://github.com/0xPolygonMiden/compiler/issues/228 is fixed // let emul_out: T = (*execute_emulator(ir_program.clone(), args).first().unwrap()).into(); // prop_assert_eq!(rust_out, emul_out, "Emulator output mismatch"); diff --git a/tests/integration/src/rust_masm_tests/rust_sdk.rs b/tests/integration/src/rust_masm_tests/rust_sdk.rs index c2b34dc0b..2e369a484 100644 --- a/tests/integration/src/rust_masm_tests/rust_sdk.rs +++ b/tests/integration/src/rust_masm_tests/rust_sdk.rs @@ -8,10 +8,11 @@ use crate::{cargo_proj::project, compiler_test::sdk_crate_path, CompilerTest}; fn account() { let artifact_name = "miden_sdk_account_test"; let mut test = CompilerTest::rust_source_cargo_lib( - PathBuf::from("../rust-apps-wasm/rust-sdk/account-test"), + "../rust-apps-wasm/rust-sdk/account-test", artifact_name, true, None, + None, ); test.expect_wasm(expect_file![format!( "../../expected/rust_sdk_account_test/{artifact_name}.wat" @@ -26,68 +27,27 @@ fn account() { #[test] fn basic_wallet() { - let sdk_crate_path = sdk_crate_path(); let project_name = "rust_sdk_basic_wallet"; - let proj = project(project_name) - .file( - "Cargo.toml", - format!( - r#" - - [package] - name = "{project_name}" - version = "0.0.1" - edition = "2021" - authors = [] - - [dependencies] - wee_alloc = {{ version = "0.4.5", default-features = false}} - miden-sdk = {{ path = "{sdk_crate_path}" }} - - [lib] - crate-type = ["cdylib"] - - [profile.release] - panic = "abort" - # optimize for size - opt-level = "z" - "#).as_str() - ) - .file( - "src/lib.rs", - r#" - #![no_std] + let source = r#" - #[panic_handler] - fn my_panic(_info: &core::panic::PanicInfo) -> ! { - loop {} - } +pub struct Account; - #[global_allocator] - static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +impl Account { + #[no_mangle] + pub fn receive_asset(asset: CoreAsset) { + add_asset(asset); + } - use miden_sdk::*; - - pub struct Account; - - impl Account { - #[no_mangle] - pub fn receive_asset(asset: CoreAsset) { - add_asset(asset); - } - - #[no_mangle] - pub fn send_asset(asset: CoreAsset, tag: Tag, note_type: NoteType, recipient: Recipient) { - let asset = remove_asset(asset); - create_note(asset, tag, note_type, recipient); - } - } - "#, - ) - .build(); + #[no_mangle] + pub fn send_asset(asset: CoreAsset, tag: Tag, note_type: NoteType, recipient: Recipient) { + let asset = remove_asset(asset); + create_note(asset, tag, note_type, recipient); + } +} +"#; - let mut test = CompilerTest::rust_source_cargo_lib(proj.root(), project_name, true, None); - let artifact_name = test.source.artifact_name(); + let mut test = CompilerTest::rust_source_with_sdk(project_name, source, true, None, None); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!("../../expected/{project_name}/{artifact_name}.wat")]); test.expect_ir(expect_file![format!("../../expected/{project_name}/{artifact_name}.hir")]); // TODO: fix flaky test, "exec."_ZN19miden_sdk_tx_kernel9add_asset17h6f4cff304c095ffc" is diff --git a/tests/integration/src/rust_masm_tests/wit_sdk.rs b/tests/integration/src/rust_masm_tests/wit_sdk.rs index f2ddf44af..b2f0781e7 100644 --- a/tests/integration/src/rust_masm_tests/wit_sdk.rs +++ b/tests/integration/src/rust_masm_tests/wit_sdk.rs @@ -10,10 +10,10 @@ use crate::CompilerTest; #[test] fn sdk() { let test = CompilerTest::rust_source_cargo_component( - PathBuf::from_str("../rust-apps-wasm/wit-sdk/sdk").unwrap(), + "../rust-apps-wasm/wit-sdk/sdk", Default::default(), ); - let artifact_name = test.source.artifact_name(); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!( "../../expected/wit_sdk_basic_wallet/{artifact_name}.wat" )]); @@ -63,11 +63,9 @@ fn sdk_basic_wallet() { import_metadata: import_metadata.clone(), ..Default::default() }; - let mut test = CompilerTest::rust_source_cargo_component( - PathBuf::from_str("../rust-apps-wasm/wit-sdk/basic-wallet").unwrap(), - config, - ); - let artifact_name = test.source.artifact_name(); + let mut test = + CompilerTest::rust_source_cargo_component("../rust-apps-wasm/wit-sdk/basic-wallet", config); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!( "../../expected/wit_sdk_basic_wallet/{artifact_name}.wat" )]); @@ -144,11 +142,9 @@ fn sdk_basic_wallet_p2id_note() { import_metadata: import_metadata.clone(), ..Default::default() }; - let mut test = CompilerTest::rust_source_cargo_component( - PathBuf::from_str("../rust-apps-wasm/wit-sdk/p2id-note").unwrap(), - config, - ); - let artifact_name = test.source.artifact_name(); + let mut test = + CompilerTest::rust_source_cargo_component("../rust-apps-wasm/wit-sdk/p2id-note", config); + let artifact_name = test.artifact_name(); test.expect_wasm(expect_file![format!( "../../expected/wit_sdk_basic_wallet/{artifact_name}.wat" )]); diff --git a/tests/rust-apps-wasm/fib/Cargo.toml b/tests/rust-apps-wasm/fib/Cargo.toml index 38bb21e14..b975b3f10 100644 --- a/tests/rust-apps-wasm/fib/Cargo.toml +++ b/tests/rust-apps-wasm/fib/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -dlmalloc = { version = "0.2.4", features = ["global"]} +dlmalloc = { version = "0.2.4", features = ["global"] } miden-integration-tests-rust-fib = { path = "../../rust-apps/fib" } [profile.release] -opt-level = "z" \ No newline at end of file +opt-level = "z" +debug = true diff --git a/tests/rust-apps-wasm/rust-sdk/account-test/Cargo.toml b/tests/rust-apps-wasm/rust-sdk/account-test/Cargo.toml index ea2f77d12..2b8a9e198 100644 --- a/tests/rust-apps-wasm/rust-sdk/account-test/Cargo.toml +++ b/tests/rust-apps-wasm/rust-sdk/account-test/Cargo.toml @@ -9,9 +9,10 @@ crate-type = ["cdylib", "rlib"] [dependencies] miden-sdk = { path = "../../../../sdk/sdk" } -wee_alloc = { version = "0.4.5", default-features = false} +wee_alloc = { version = "0.4.5", default-features = false } [profile.release] panic = "abort" # optimize for size -opt-level = "z" \ No newline at end of file +opt-level = "z" +debug = true diff --git a/tests/rust-apps-wasm/wit-sdk/basic-wallet/Cargo.toml b/tests/rust-apps-wasm/wit-sdk/basic-wallet/Cargo.toml index 841a8b849..65761785e 100644 --- a/tests/rust-apps-wasm/wit-sdk/basic-wallet/Cargo.toml +++ b/tests/rust-apps-wasm/wit-sdk/basic-wallet/Cargo.toml @@ -12,8 +12,10 @@ edition = "2021" publish = false [dependencies] -wit-bindgen = { version = "0.17.0", default-features = false, features = ["realloc"] } -wee_alloc = { version = "0.4.5", default-features = false} +wit-bindgen = { version = "0.17.0", default-features = false, features = [ + "realloc", +] } +wee_alloc = { version = "0.4.5", default-features = false } [lib] crate-type = ["cdylib"] @@ -27,4 +29,5 @@ package = "miden:basic-wallet" "miden:base" = { path = "../sdk/wit" } [profile.release] -panic = "abort" \ No newline at end of file +panic = "abort" +debug = true diff --git a/tests/rust-apps-wasm/wit-sdk/p2id-note/Cargo.toml b/tests/rust-apps-wasm/wit-sdk/p2id-note/Cargo.toml index 62adb6955..84e9f9970 100644 --- a/tests/rust-apps-wasm/wit-sdk/p2id-note/Cargo.toml +++ b/tests/rust-apps-wasm/wit-sdk/p2id-note/Cargo.toml @@ -15,8 +15,10 @@ publish = false crate-type = ["cdylib"] [dependencies] -wit-bindgen = { version = "0.17.0", default-features = false, features = ["realloc"] } -wee_alloc = { version = "0.4.5", default-features = false} +wit-bindgen = { version = "0.17.0", default-features = false, features = [ + "realloc", +] } +wee_alloc = { version = "0.4.5", default-features = false } [package.metadata.component] package = "miden:basic-wallet-p2id-note" @@ -31,4 +33,5 @@ package = "miden:basic-wallet-p2id-note" derives = ["PartialEq"] [profile.release] -panic = "abort" \ No newline at end of file +panic = "abort" +debug = true diff --git a/tests/rust-apps-wasm/wit-sdk/sdk/Cargo.toml b/tests/rust-apps-wasm/wit-sdk/sdk/Cargo.toml index deb5b17d5..ce8f682d0 100644 --- a/tests/rust-apps-wasm/wit-sdk/sdk/Cargo.toml +++ b/tests/rust-apps-wasm/wit-sdk/sdk/Cargo.toml @@ -12,8 +12,10 @@ publish = false [workspace] [dependencies] -wit-bindgen = { version = "0.17.0", default-features = false, features = ["realloc"] } -wee_alloc = { version = "0.4.5", default-features = false} +wit-bindgen = { version = "0.17.0", default-features = false, features = [ + "realloc", +] } +wee_alloc = { version = "0.4.5", default-features = false } [lib] @@ -25,4 +27,5 @@ package = "component:miden" [package.metadata.component.dependencies] [profile.release] -panic = "abort" \ No newline at end of file +panic = "abort" +debug = true diff --git a/tests/rust-apps/fib/Cargo.toml b/tests/rust-apps/fib/Cargo.toml index 35afc22eb..8f815bf3a 100644 --- a/tests/rust-apps/fib/Cargo.toml +++ b/tests/rust-apps/fib/Cargo.toml @@ -3,3 +3,6 @@ name = "miden-integration-tests-rust-fib" version = "0.0.0" edition = "2021" publish = false + +[profile.release] +debug = true diff --git a/tools/cargo-miden/Cargo.toml b/tools/cargo-miden/Cargo.toml index a6abca213..1ccfcedd1 100644 --- a/tools/cargo-miden/Cargo.toml +++ b/tools/cargo-miden/Cargo.toml @@ -12,7 +12,7 @@ keywords.workspace = true license.workspace = true readme.workspace = true edition.workspace = true -autotests = false # disable autodiscovery of tests +autotests = false # disable autodiscovery of tests [[bin]] name = "cargo-miden" @@ -24,7 +24,6 @@ path = "tests/mod.rs" [dependencies] midenc-compile.workspace = true midenc-session.workspace = true -miden-diagnostics.workspace = true env_logger.workspace = true log.workspace = true clap.workspace = true diff --git a/tools/cargo-miden/src/build.rs b/tools/cargo-miden/src/build.rs index 6ab9777aa..3773de177 100644 --- a/tools/cargo-miden/src/build.rs +++ b/tools/cargo-miden/src/build.rs @@ -3,50 +3,51 @@ use std::{ sync::Arc, }; -use anyhow::{bail, Context}; -use miden_diagnostics::Verbosity; use midenc_session::{ - InputFile, OutputFile, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, TargetEnv, + diagnostics::{DefaultSourceManager, IntoDiagnostic, Report, WrapErr}, + InputFile, OutputFile, OutputType, OutputTypeSpec, OutputTypes, Session, Verbosity, }; pub fn build_masm( wasm_file_path: &Path, output_folder: &Path, is_bin: bool, -) -> anyhow::Result { - let project_type = if is_bin { - ProjectType::Program - } else { - ProjectType::Library - }; - +) -> Result { if !output_folder.exists() { - bail!("MASM output folder '{}' does not exist.", output_folder.to_str().unwrap()); + return Err(Report::msg(format!( + "MASM output folder '{}' does not exist.", + output_folder.to_str().unwrap() + ))); } log::debug!( "Compiling '{}' Wasm to '{}' directory with midenc ...", wasm_file_path.to_str().unwrap(), &output_folder.to_str().unwrap() ); - let input = InputFile::from_path(wasm_file_path).context("Invalid input file")?; + let input = InputFile::from_path(wasm_file_path) + .into_diagnostic() + .wrap_err("Invalid input file")?; let output_file_folder = OutputFile::Real(output_folder.to_path_buf()); let output_type = OutputType::Masm; let output_types = OutputTypes::new(vec![OutputTypeSpec { output_type, path: Some(output_file_folder.clone()), }]); - let cwd = std::env::current_dir().context("Failed to get current working directory")?; - let options = midenc_session::Options::new(cwd) - // .with_color(color) + let project_type = if is_bin { "--exe" } else { "--lib" }; + let source_manager = Arc::new(DefaultSourceManager::default()); + let options = midenc_compile::CompilerOptions::parse_options(&[project_type]) .with_verbosity(Verbosity::Debug) - // .with_warnings(self.warn) .with_output_types(output_types); - let target = TargetEnv::default(); - let session = Arc::new( - Session::new(target, input, Some(output_folder.to_path_buf()), None, None, options, None) - .with_project_type(project_type), - ); - midenc_compile::compile(session.clone()).context("Wasm to MASM compilation failed!")?; + let session = Arc::new(Session::new( + input, + Some(output_folder.to_path_buf()), + None, + None, + options, + None, + source_manager, + )); + midenc_compile::compile(session.clone())?; let mut output_path = output_folder.join(wasm_file_path.file_stem().unwrap()); output_path.set_extension(output_type.extension()); Ok(output_path) diff --git a/tools/cargo-miden/src/lib.rs b/tools/cargo-miden/src/lib.rs index 73ba7907a..7078d2521 100644 --- a/tools/cargo-miden/src/lib.rs +++ b/tools/cargo-miden/src/lib.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; -use anyhow::bail; use cargo_component::load_metadata; use cargo_component_core::terminal::Terminal; use clap::{CommandFactory, Parser}; use config::CargoArguments; +use midenc_session::diagnostics::Report; use new_project::NewCommand; use crate::run_cargo_command::run_cargo_command; @@ -25,7 +25,7 @@ const BUILTIN_COMMANDS: &[&str] = &[ "new", ]; -const AFTER_HELP: &str = "Unrecognized subcommands will be passed to cargo verbatim +const AFTER_HELP: &str = "Unrecognized subcommands will be passed to cargo verbatim and the artifacts will be processed afterwards (e.g. `build` command compiles MASM). \nSee `cargo help` for more information on available cargo commands."; @@ -80,7 +80,7 @@ where None } -pub fn run(args: T, terminal: &Terminal) -> anyhow::Result> +pub fn run(args: T, terminal: &Terminal) -> Result, Report> where T: Iterator, { @@ -92,7 +92,7 @@ where Some(cmd) if BUILTIN_COMMANDS.contains(&cmd) => { match CargoMiden::parse_from(args.clone()) { CargoMiden::Miden(cmd) | CargoMiden::Command(cmd) => match cmd { - Command::New(cmd) => vec![cmd.exec()?], + Command::New(cmd) => vec![cmd.exec().map_err(Report::msg)?], }, } } @@ -104,19 +104,21 @@ where // If somehow the CLI parsed correctly despite no subcommand, // print the help instead - CargoMiden::command().print_long_help()?; + CargoMiden::command().print_long_help().map_err(Report::msg)?; Vec::new() } _ => { // Not a built-in command, run the cargo command - let cargo_args = CargoArguments::parse_from(args.clone().into_iter())?; - let metadata = load_metadata(terminal, cargo_args.manifest_path.as_deref(), false)?; + let cargo_args = + CargoArguments::parse_from(args.clone().into_iter()).map_err(Report::msg)?; + let metadata = load_metadata(terminal, cargo_args.manifest_path.as_deref(), false) + .map_err(Report::msg)?; if metadata.packages.is_empty() { - bail!( + return Err(Report::msg(format!( "manifest `{path}` contains no package or the workspace has no members", path = metadata.workspace_root.join("Cargo.toml") - ); + ))); } let spawn_args: Vec<_> = args.into_iter().skip(1).collect(); diff --git a/tools/cargo-miden/src/run_cargo_command.rs b/tools/cargo-miden/src/run_cargo_command.rs index 23ea0c62e..f87822c36 100644 --- a/tools/cargo-miden/src/run_cargo_command.rs +++ b/tools/cargo-miden/src/run_cargo_command.rs @@ -1,7 +1,7 @@ use std::{path::PathBuf, process::Command}; -use anyhow::bail; use cargo_metadata::Metadata; +use midenc_session::diagnostics::{IntoDiagnostic, Report}; use crate::{ build::build_masm, @@ -21,7 +21,7 @@ pub fn run_cargo_command( subcommand: Option<&str>, cargo_args: &CargoArguments, spawn_args: &[String], -) -> anyhow::Result> { +) -> Result, Report> { let cargo = std::env::var("CARGO") .map(PathBuf::from) .ok() @@ -48,7 +48,7 @@ pub fn run_cargo_command( // Handle the target for build commands if is_build { - install_wasm32_wasi()?; + install_wasm32_wasi().map_err(|err| Report::msg(err))?; // Add an implicit wasm32-wasi target if there isn't a wasm target present if !cargo_args.targets.iter().any(|t| is_wasm_target(t)) { @@ -68,11 +68,17 @@ pub fn run_cargo_command( match cmd.status() { Ok(status) => { if !status.success() { - bail!("cargo failed with exit code {}", status.code().unwrap_or(1)); + return Err(Report::msg(format!( + "cargo failed with exit code {}", + status.code().unwrap_or(1) + ))); } } Err(e) => { - bail!("failed to spawn `{cargo}`: {e}", cargo = cargo.display()); + return Err(Report::msg(format!( + "failed to spawn `{cargo}`: {e}", + cargo = cargo.display() + ))); } } let mut outputs = Vec::new(); @@ -99,7 +105,7 @@ pub fn run_cargo_command( "debug" }); if !miden_out_dir.exists() { - std::fs::create_dir_all(&miden_out_dir)?; + std::fs::create_dir_all(&miden_out_dir).into_diagnostic()?; } for package in &metadata.packages { @@ -119,7 +125,7 @@ pub fn run_cargo_command( outputs.push(output); } else { log::debug!("no output found for package `{name}`", name = package.name); - bail!("Cargo build failed, no Wasm artifact found"); + return Err(Report::msg("Cargo build failed, no Wasm artifact found")); } } }